From 148ac4db9bc67e126b51d6b62e7f6a79aa4dceb2 Mon Sep 17 00:00:00 2001 From: mlus <1319237806@qq.com> Date: Tue, 24 Feb 2026 10:24:13 +0800 Subject: [PATCH] Revert "fallback BNBT, Compat with codec" This reverts commit ce7004dba079e40eb4d248ef9107185f8fd626bf. --- .../fubuki/playersync/sync/VanillaSync.java | 65 +++++++++++++++++-- .../playersync/sync/addons/ModsSupport.java | 18 ++++- .../fubuki/playersync/util/LocalJsonUtil.java | 6 +- 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/main/java/vip/fubuki/playersync/sync/VanillaSync.java b/src/main/java/vip/fubuki/playersync/sync/VanillaSync.java index b86c0cc..ab2b560 100644 --- a/src/main/java/vip/fubuki/playersync/sync/VanillaSync.java +++ b/src/main/java/vip/fubuki/playersync/sync/VanillaSync.java @@ -375,8 +375,34 @@ public class VanillaSync { return ItemStack.EMPTY; } - String nbtString = deserializeString(serializedNbt); - CompoundTag compoundTag = TagParser.parseTag(nbtString); + CompoundTag compoundTag; + String nbtString = serializedNbt; // Will be overwritten with decoded SNBT for legacy formats + + // Try binary NBT format first (new format, avoids SNBT round-trip issues) + if (serializedNbt.startsWith("BNBT:")) { + try { + compoundTag = deserializeBinaryBase64Tag(serializedNbt); + } catch (Exception e) { + PlayerSync.LOGGER.error("Failed to deserialize binary NBT data, skipping item.", e); + return ItemStack.EMPTY; + } + } else { + // Legacy SNBT-based deserialization (B64: or old custom format) + nbtString = deserializeString(serializedNbt); + try { + compoundTag = TagParser.parseTag(nbtString); + } catch (CommandSyntaxException e) { + // TagParser may fail on certain 1.21.1 component SNBT formats (e.g. nested lists [[{...}]]) + // Try NbtUtils.snbtToStructure as a fallback + PlayerSync.LOGGER.warn("TagParser.parseTag failed, trying NbtUtils.snbtToStructure fallback. SNBT: {}", nbtString); + try { + compoundTag = NbtUtils.snbtToStructure(nbtString); + } catch (CommandSyntaxException e2) { + PlayerSync.LOGGER.error("Both SNBT parsers failed for data: {}", nbtString); + throw e; // re-throw original exception + } + } + } if (compoundTag.isEmpty() || !compoundTag.contains("id", Tag.TAG_STRING)) { return ItemStack.EMPTY; // Invalid or empty tag @@ -578,11 +604,42 @@ public class VanillaSync { // It's our placeholder, retrieve the original NBT string return itemStack.getComponents().get(DataComponents.CUSTOM_DATA).copyTag().getString("playersync:original_item_nbt"); } else { - // It's a normal item or empty, serialize its current NBT - return serialize(serializeNBT(itemStack).toString()); + // It's a normal item or empty, serialize using binary NBT to avoid SNBT round-trip issues + Tag tag = serializeNBT(itemStack); + if (tag instanceof CompoundTag compoundTag) { + return serializeTagToBinaryBase64(compoundTag); + } + // Fallback to SNBT-based serialization for non-compound tags + return serialize(tag.toString()); } } + /** + * Serializes a CompoundTag to a Base64-encoded binary NBT string. + * This avoids SNBT round-trip issues where Tag.toString() produces SNBT + * that TagParser.parseTag() cannot parse back (e.g. with nested lists [[{...}]]). + */ + public static String serializeTagToBinaryBase64(CompoundTag tag) { + try { + java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); + net.minecraft.nbt.NbtIo.writeCompressed(tag, baos); + return "BNBT:" + Base64.getEncoder().encodeToString(baos.toByteArray()); + } catch (IOException e) { + PlayerSync.LOGGER.error("Failed to serialize NBT to binary, falling back to SNBT", e); + return serialize(tag.toString()); + } + } + + /** + * Deserializes a Base64-encoded binary NBT string back to a CompoundTag. + */ + public static CompoundTag deserializeBinaryBase64Tag(String encoded) throws IOException { + String base64 = encoded.substring(5); // Remove "BNBT:" prefix + byte[] bytes = Base64.getDecoder().decode(base64); + java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(bytes); + return net.minecraft.nbt.NbtIo.readCompressed(bais, net.minecraft.nbt.NbtAccounter.unlimitedHeap()); + } + public static Tag serializeNBT(ItemStack itemStack) { if (itemStack == null || itemStack.isEmpty()) { return new CompoundTag(); 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 f79068e..324aa5c 100644 --- a/src/main/java/vip/fubuki/playersync/sync/addons/ModsSupport.java +++ b/src/main/java/vip/fubuki/playersync/sync/addons/ModsSupport.java @@ -42,8 +42,18 @@ public class ModsSupport { ResultSet rsBackpack = qrBackpack.resultSet(); if (rsBackpack.next()) { String serialized = rsBackpack.getString("backpack_nbt"); - String nbtString = VanillaSync.deserializeString(serialized); - CompoundTag backpackNbt = TagParser.parseTag(nbtString); + CompoundTag backpackNbt; + if (serialized.startsWith("BNBT:")) { + backpackNbt = VanillaSync.deserializeBinaryBase64Tag(serialized); + } else { + String nbtString = VanillaSync.deserializeString(serialized); + try { + backpackNbt = TagParser.parseTag(nbtString); + } catch (CommandSyntaxException ex) { + PlayerSync.LOGGER.warn("TagParser.parseTag failed for backpack UUID {}, trying fallback", contentsUuid); + backpackNbt = net.minecraft.nbt.NbtUtils.snbtToStructure(nbtString); + } + } // Update BackpackStorage with the retrieved NBT net.p3pp3rf1y.sophisticatedbackpacks.backpack.BackpackStorage.get().setBackpackContents(contentsUuid, backpackNbt); PlayerSync.LOGGER.info("Restored backpack data for UUID " + contentsUuid); @@ -54,6 +64,8 @@ public class ModsSupport { PlayerSync.LOGGER.error("Error restoring backpack data for UUID " + contentsUuid, e); } catch (CommandSyntaxException e) { PlayerSync.LOGGER.error("Error parsing backpack NBT for UUID {}. Skipping backpack.", contentsUuid, e); + } catch (IOException e) { + PlayerSync.LOGGER.error("Error reading binary backpack NBT for UUID {}. Skipping backpack.", contentsUuid, e); } } else { PlayerSync.LOGGER.warn("Backpack item in slot " + slot + " has no contentsUuid during restore"); @@ -182,7 +194,7 @@ public class ModsSupport { UUID contentsUuid = uuidOpt.get(); // Get internal backpack data from BackpackStorage (creates it if missing) CompoundTag backpackNbt = net.p3pp3rf1y.sophisticatedbackpacks.backpack.BackpackStorage.get().getOrCreateBackpackContents(contentsUuid); - String serialized = VanillaSync.serialize(backpackNbt.toString()); + String serialized = VanillaSync.serializeTagToBinaryBase64(backpackNbt); try { // Use REPLACE INTO so existing records are updated JDBCsetUp.executeUpdate("REPLACE INTO backpack_data (uuid, backpack_nbt) VALUES ('" + contentsUuid + "', '" + serialized + "')"); diff --git a/src/main/java/vip/fubuki/playersync/util/LocalJsonUtil.java b/src/main/java/vip/fubuki/playersync/util/LocalJsonUtil.java index dc460ba..b91fb6d 100644 --- a/src/main/java/vip/fubuki/playersync/util/LocalJsonUtil.java +++ b/src/main/java/vip/fubuki/playersync/util/LocalJsonUtil.java @@ -46,8 +46,8 @@ public class LocalJsonUtil { public static String cleanSnbt(String snbt) { if (snbt == null) return null; - return snbt.replace("{\"\":", "{\"_\":") - .replace(",\"\":", ",\"_\":") - .replace("[\"\":", "[\"_\":"); + return snbt.replaceAll(",\\s*\\{\"\":\"\"}", "") + .replaceAll("\\{\"\":\"\"}\\s*,", "") + .replaceAll("\\{\"\":\"\"}", ""); } }