From eec949f405ae06e920c7aae21511e77e5fc30f58 Mon Sep 17 00:00:00 2001 From: laforetbrut Date: Sat, 4 Apr 2026 07:16:50 +0200 Subject: [PATCH] Fix anti-duplication: clear slots before restoring data --- .../playersync/sync/addons/ModCompatSync.java | 22 ++++++---- .../playersync/sync/addons/ModsSupport.java | 40 ++++++++++--------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/main/java/vip/fubuki/playersync/sync/addons/ModCompatSync.java b/src/main/java/vip/fubuki/playersync/sync/addons/ModCompatSync.java index bcaaa90..2dc94f4 100644 --- a/src/main/java/vip/fubuki/playersync/sync/addons/ModCompatSync.java +++ b/src/main/java/vip/fubuki/playersync/sync/addons/ModCompatSync.java @@ -155,17 +155,15 @@ public class ModCompatSync { */ public static void applyAccessoriesFromData(Player player, String accessoriesData) { if (!ModList.get().isLoaded("accessories")) return; - if (accessoriesData == null || accessoriesData.length() <= 2) return; try { io.wispforest.accessories.api.AccessoriesCapability cap = io.wispforest.accessories.api.AccessoriesCapability.get(player); if (cap == null) return; - Map storedMap = LocalJsonUtil.StringToMap(accessoriesData); - if (storedMap.isEmpty()) return; - Map containers = cap.getContainers(); + // FIX ANTI-DUPLICATION: ALWAYS clear accessories slots first to wipe stale + // data from Minecraft's .dat file, then only restore if DB has valid data. for (io.wispforest.accessories.api.AccessoriesContainer container : containers.values()) { var accessories = container.getAccessories(); for (int i = 0; i < accessories.getContainerSize(); i++) { @@ -173,6 +171,11 @@ public class ModCompatSync { } } + if (accessoriesData == null || accessoriesData.length() <= 2) return; + + Map storedMap = LocalJsonUtil.StringToMap(accessoriesData); + if (storedMap.isEmpty()) return; + for (Map.Entry entry : storedMap.entrySet()) { String compositeKey = entry.getKey(); int lastColon = compositeKey.lastIndexOf(':'); @@ -308,19 +311,22 @@ public class ModCompatSync { */ public static void applyCosmeticArmorFromData(Player player, String cosmeticArmorData) { if (!ModList.get().isLoaded("cosmeticarmorreworked")) return; - if (cosmeticArmorData == null || cosmeticArmorData.length() <= 2) return; try { lain.mods.cos.impl.inventory.InventoryCosArmor cosInv = lain.mods.cos.impl.ModObjects.invMan.getCosArmorInventory(player.getUUID()); if (cosInv == null) return; - Map storedMap = LocalJsonUtil.StringToEntryMap(cosmeticArmorData); - if (storedMap.isEmpty()) return; - + // FIX ANTI-DUPLICATION: ALWAYS clear cosmetic armor slots first to wipe stale + // data from Minecraft's .dat file, then only restore if DB has valid data. for (int i = 0; i < cosInv.getContainerSize(); i++) { cosInv.setItem(i, ItemStack.EMPTY); } + if (cosmeticArmorData == null || cosmeticArmorData.length() <= 2) return; + + Map storedMap = LocalJsonUtil.StringToEntryMap(cosmeticArmorData); + if (storedMap.isEmpty()) return; + for (Map.Entry entry : storedMap.entrySet()) { int slot = entry.getKey(); try { diff --git a/src/main/java/vip/fubuki/playersync/sync/addons/ModsSupport.java b/src/main/java/vip/fubuki/playersync/sync/addons/ModsSupport.java index cf5e477..0c2bca8 100644 --- a/src/main/java/vip/fubuki/playersync/sync/addons/ModsSupport.java +++ b/src/main/java/vip/fubuki/playersync/sync/addons/ModsSupport.java @@ -152,21 +152,10 @@ public class ModsSupport { curiosData = rs.getString("curios_item"); } - // FIX: Check if data is valid BEFORE clearing slots - if (curiosData == null || curiosData.length() <= 2) { - PlayerSync.LOGGER.debug("Empty curios data for player {}, skipping restore", player.getUUID()); - return; - } - - Map storedMap = LocalJsonUtil.StringToMap(curiosData); - if (storedMap.isEmpty()) { - PlayerSync.LOGGER.debug("No curios entries for player {}, skipping restore", player.getUUID()); - return; - } - ICuriosItemHandler handler = handlerOpt.get(); - // Clear current Curios slots ONLY after confirming valid data exists + // FIX ANTI-DUPLICATION: ALWAYS clear curios slots first to wipe stale data + // loaded from Minecraft's .dat file, then only restore if DB has valid data. handler.getCurios().forEach((slotType, stacksHandler) -> { IDynamicStackHandler dynStacks = stacksHandler.getStacks(); for (int i = 0; i < dynStacks.getSlots(); i++) { @@ -174,6 +163,17 @@ public class ModsSupport { } }); + if (curiosData == null || curiosData.length() <= 2) { + PlayerSync.LOGGER.debug("Empty curios data for player {}, slots cleared", player.getUUID()); + return; + } + + Map storedMap = LocalJsonUtil.StringToMap(curiosData); + if (storedMap.isEmpty()) { + PlayerSync.LOGGER.debug("No curios entries for player {}, slots cleared", player.getUUID()); + return; + } + // Restore each saved item for (Map.Entry entry : storedMap.entrySet()) { String compositeKey = entry.getKey(); @@ -267,7 +267,6 @@ public class ModsSupport { */ public static void applyCuriosFromData(Player player, String curiosData) { if (!ModList.get().isLoaded("curios")) return; - if (curiosData == null || curiosData.length() <= 2) return; Optional handlerOpt = CuriosApi.getCuriosInventory(player); if (handlerOpt.isEmpty()) { @@ -275,12 +274,11 @@ public class ModsSupport { return; } - Map storedMap = LocalJsonUtil.StringToMap(curiosData); - if (storedMap.isEmpty()) return; - ICuriosItemHandler handler = handlerOpt.get(); - // Clear all curios slots BEFORE restoring + // FIX ANTI-DUPLICATION: ALWAYS clear curios slots first, even when DB data is + // empty. Without this, stale curios loaded from Minecraft's .dat file (world save) + // persist when the DB has no curios data — causing item duplication across servers. for (Map.Entry entry : handler.getCurios().entrySet()) { IDynamicStackHandler stacks = entry.getValue().getStacks(); for (int i = 0; i < stacks.getSlots(); i++) { @@ -288,6 +286,12 @@ public class ModsSupport { } } + // If no data to restore, we're done (slots already cleared above) + if (curiosData == null || curiosData.length() <= 2) return; + + Map storedMap = LocalJsonUtil.StringToMap(curiosData); + if (storedMap.isEmpty()) return; + // Restore items from pre-read data for (Map.Entry entry : storedMap.entrySet()) { String compositeKey = entry.getKey();