[SPIGOT-7857] BlockStateMeta: Item not the same (isSimilar fails) after deserialization cycle Created: 03/Aug/24 Updated: 25/Dec/24 Resolved: 07/Aug/24 |
|
Status: | Resolved |
Project: | Spigot |
Component/s: | None |
Affects Version/s: | None |
Fix Version/s: | None |
Type: | Bug | Priority: | Minor |
Reporter: | blablubbabc | Assignee: | Unassigned |
Resolution: | Fixed | Votes: | 0 |
Labels: | BlockStateMeta, item, serialization |
Version: | This server is running CraftBukkit version dev-Spigot-34bd42b-0cc6acb (MC: 1.21) (Implementing API version 1.21-R0.1-SNAPSHOT) |
Guidelines Read: | Yes |
Description |
Example involving a campfire item: public class SerializationTest implements CommandExecutor { public SerializationTest() { Test.INSTANCE.getCommand("test").setExecutor(this); } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { try { ItemStack item = createItemStack(); YamlConfiguration config = new YamlConfiguration(); config.set("item", item); String serialized = config.saveToString(); sender.sendMessage("Serialized: " + serialized); config.loadFromString(serialized); ItemStack deserialized = config.getItemStack("item"); sender.sendMessage("Deserialized: " + config.saveToString()); sender.sendMessage("Deserialized equals original? " + item.isSimilar(deserialized)); } catch (Exception e) { sender.sendMessage("Command execution failed: " + e.getMessage()); Test.INSTANCE.getLogger().log(Level.SEVERE, "Command execution failed!", e); } return true; } private static ItemStack createItemStack() { ItemStack itemStack = new ItemStack(Material.CAMPFIRE); BlockDataMeta itemMeta = (BlockDataMeta) itemStack.getItemMeta(); Campfire blockData = (Campfire) Material.CAMPFIRE.createBlockData(); blockData.setLit(false); itemMeta.setBlockData(blockData); itemStack.setItemMeta(itemMeta); return itemStack; } } Output: [17:51:46] [Server thread/INFO]: Serialized: item: [17:51:46] [Server thread/INFO]: ==: org.bukkit.inventory.ItemStack [17:51:46] [Server thread/INFO]: v: 3953 [17:51:46] [Server thread/INFO]: type: CAMPFIRE [17:51:46] [Server thread/INFO]: meta: [17:51:46] [Server thread/INFO]: ==: ItemMeta [17:51:46] [Server thread/INFO]: meta-type: TILE_ENTITY [17:51:46] [Server thread/INFO]: BlockStateTag: [17:51:46] [Server thread/INFO]: waterlogged: 'false' [17:51:46] [Server thread/INFO]: signal_fire: 'false' [17:51:46] [Server thread/INFO]: lit: 'false' [17:51:46] [Server thread/INFO]: facing: north [17:51:46] [Server thread/INFO]: blockMaterial: CAMPFIRE [17:51:46] [Server thread/INFO]: [17:51:46] [Server thread/INFO]: Deserialized: item: [17:51:46] [Server thread/INFO]: ==: org.bukkit.inventory.ItemStack [17:51:46] [Server thread/INFO]: v: 3953 [17:51:46] [Server thread/INFO]: type: CAMPFIRE [17:51:46] [Server thread/INFO]: meta: [17:51:46] [Server thread/INFO]: ==: ItemMeta [17:51:46] [Server thread/INFO]: meta-type: TILE_ENTITY [17:51:46] [Server thread/INFO]: BlockStateTag: [17:51:46] [Server thread/INFO]: waterlogged: '' [17:51:46] [Server thread/INFO]: signal_fire: '' [17:51:46] [Server thread/INFO]: lit: '' [17:51:46] [Server thread/INFO]: facing: north [17:51:46] [Server thread/INFO]: internal: H4sIAAAAAAAA/+NiYOBi4HPKyU/Ods0rySypDElMZ2ZgrGAAAiBdCaI5GVg9S1JzixmgglUgmpuBxzk/PzszLz0kMzcVJMfCgAa4GQRhSvJLEnNwquNgYMpMYRDKzcxLTS5KTCuxSk7MLUjLLEplYAAAe+3XY58AAAA= [17:51:46] [Server thread/INFO]: blockMaterial: CAMPFIRE [17:51:46] [Server thread/INFO]: [17:51:46] [Server thread/INFO]: Deserialized equals original? false The decoded "internal" data is as follows: {"BlockEntityTag":{"x":0,"y":0,"Items":[],"z":0,"CookingTimes":[I;0,0,0,0],"CookingTotalTimes":[I;0,0,0,0],"id":"minecraft:campfire"}} |
Comments |
Comment by blablubbabc [ 06/Aug/24 ] |
Okay, seems like this (deserializing boolean block state data) didn't properly work before 1.20.5 already (see comment in: https://hub.spigotmc.org/jira/browse/SPIGOT-6257). I will prepare a PR and ignore this issue. |
Comment by md_5 [ 06/Aug/24 ] |
Unfortunately I don't have a better idea |
Comment by blablubbabc [ 05/Aug/24 ] |
Something like this works:
Object blockData = SerializableMeta.getObject(Object.class, map, BLOCK_DATA.BUKKIT, true); if (blockData != null) { Map<String, String> mapBlockData = new HashMap<>(); if (blockData instanceof Map) { for (Entry<String, Object> entry : ((Map<String, Object>) blockData).entrySet()) { mapBlockData.put(entry.getKey(), entry.getValue().toString()); } } else { // Legacy pre 1.20.5 NBTTagCompound nbtBlockData = (NBTTagCompound) CraftNBTTagConfigSerializer.deserialize(blockData); for (String key : nbtBlockData.getAllKeys()) { // TODO What if the values did not parse as string? mapBlockData.put(key, nbtBlockData.getString(key)); } } this.blockData = mapBlockData; But I am not sure if the legacy data is handled correctly. E.g. if legacy data ByteTag was serialized, it is deserialized as ByteTag, but there is no way to turn this into 'true'/'false' (only "1b"/"0b") Strings, right (except by hard-coding this conversion)? |
Comment by blablubbabc [ 05/Aug/24 ] |
My guess is that the 'false' string is parsed by boolean by the MojangParser, which results in empty strings to be returned for "getString(key)" during deserialization (https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/browse/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java#524) Likely an issue since the update for 1.20.5 |
Comment by blablubbabc [ 05/Aug/24 ] |
The additional "internal" tag is gone [23:24:02] [Server thread/INFO]: Serialized: item: [23:24:02] [Server thread/INFO]: ==: org.bukkit.inventory.ItemStack [23:24:02] [Server thread/INFO]: v: 3953 [23:24:02] [Server thread/INFO]: type: CAMPFIRE [23:24:02] [Server thread/INFO]: meta: [23:24:02] [Server thread/INFO]: ==: ItemMeta [23:24:02] [Server thread/INFO]: meta-type: TILE_ENTITY [23:24:02] [Server thread/INFO]: BlockStateTag: [23:24:02] [Server thread/INFO]: waterlogged: 'false' [23:24:02] [Server thread/INFO]: signal_fire: 'false' [23:24:02] [Server thread/INFO]: lit: 'false' [23:24:02] [Server thread/INFO]: facing: north [23:24:02] [Server thread/INFO]: blockMaterial: CAMPFIRE [23:24:02] [Server thread/INFO]: [23:24:02] [Server thread/INFO]: Deserialized: item: [23:24:02] [Server thread/INFO]: ==: org.bukkit.inventory.ItemStack [23:24:02] [Server thread/INFO]: v: 3953 [23:24:02] [Server thread/INFO]: type: CAMPFIRE [23:24:02] [Server thread/INFO]: meta: [23:24:02] [Server thread/INFO]: ==: ItemMeta [23:24:02] [Server thread/INFO]: meta-type: TILE_ENTITY [23:24:02] [Server thread/INFO]: BlockStateTag: [23:24:02] [Server thread/INFO]: waterlogged: '' [23:24:02] [Server thread/INFO]: signal_fire: '' [23:24:02] [Server thread/INFO]: lit: '' [23:24:02] [Server thread/INFO]: facing: north [23:24:02] [Server thread/INFO]: blockMaterial: CAMPFIRE [23:24:02] [Server thread/INFO]: [23:24:02] [Server thread/INFO]: Deserialized equals original? false But there still seems to be some issue with the block states and the comparison between the serialized and deserialized item. |
Comment by md_5 [ 05/Aug/24 ] |
I think this is the fix, can you please test? diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java index ca5820792..42b812b03 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java @@ -110,8 +110,10 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta } else { material = Material.AIR; } - blockEntityTag = getBlockState(material, internalTag); - internalTag = null; + if (internalTag != null) { + blockEntityTag = getBlockState(material, internalTag); + internalTag = null; + } } @Override |
Comment by md_5 [ 05/Aug/24 ] |
> Also noticed (might be related): https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/browse/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java#135 deserializeInternal writes to the "internalTag" field. It should probably write to "blockEntityTag". That's intentional due to initialisation order issues |
Comment by blablubbabc [ 03/Aug/24 ] |
Also noticed (might be related): https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/browse/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java#135 deserializeInternal writes to the "internalTag" field. It should probably write to "blockEntityTag". |