[SPIGOT-6257] Items with boolean-literal-like Strings in PersistentDataContainer are deserialized as booleans Created: 28/Nov/20  Updated: 26/May/24  Resolved: 26/May/24

Status: Resolved
Project: Spigot
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Minor
Reporter: Dmitry Chankov Assignee: Unassigned
Resolution: Fixed Votes: 2
Labels: ConfigurationSerializable, PersistentDataContainer, deserialization, itemstack

Attachments: Java Archive File TestPlugin.jar    
Version: This server is running CraftBukkit version git-Spigot-a19903d-e1ebdd9 (MC: 1.16.4) (Implementing API version 1.16.4-R0.1-SNAPSHOT)
Guidelines Read: Yes

 Description   

 

  1. Create an ItemStack and set some boolean-like String value (e.g. testplugin:value: 'TRUE')
  2. Serialize it to YAML (the code below also saves it to a file because it's its main purpose)
  3. Deserizlize the ItemStack from the output code and the value has become 1b
public void onEnable() {
    File saveFile = new File(getDataFolder(), "test.yml");
    
    try {
        FileConfiguration configuration = new YamlConfiguration();
        ItemStack item = new ItemStack(Material.STONE);
        ItemMeta meta = item.getItemMeta();
        meta.getPersistentDataContainer().set(new NamespacedKey(this, "value"), PersistentDataType.STRING, "TRUE");
        item.setItemMeta(meta);
        configuration.set("item", item);
        configuration.save(saveFile);

        // SAVED
        getLogger().info(configuration.saveToString());

    } catch (IOException e) {
        e.printStackTrace();
    }

    try {
        FileConfiguration configuration = new YamlConfiguration();
        configuration.load(saveFile);

        // LOADED
        getLogger().info(configuration.saveToString());

    } catch (IOException | InvalidConfigurationException e) {
        e.printStackTrace();
    }
}

 

Output:

[20:34:51] [Server thread/INFO]: [TestPlugin] item:
  ==: org.bukkit.inventory.ItemStack
  v: 2584
  type: STONE
  meta:
    ==: ItemMeta
    meta-type: UNSPECIFIC
    PublicBukkitValues:
      testplugin:value: 'TRUE'
[20:34:51] [Server thread/INFO]: [TestPlugin] item:
  ==: org.bukkit.inventory.ItemStack
  v: 2584
  type: STONE
  meta:
    ==: ItemMeta
    meta-type: UNSPECIFIC
    PublicBukkitValues:
      testplugin:value: 1b


 Comments   
Comment by Marvin Rieple [ 26/May/24 ]

Can no longer reproduce in 1.20.6. Got fixed in 1.20.2 with this commit: f0661c3514a

Comment by PikaMug [ 25/Jul/22 ]

Still an issue on CraftBukkit version 3553-Spigot-14a2382-ef09464 (MC: 1.19) (Implementing API version 1.19-R0.1-SNAPSHOT)

Comment by blablubbabc [ 10/Mar/21 ]

This issue not only affects custom plugin data inside the PersistentDataContainer, but also items with BlockStateTag. For example:

ItemStack itemStack = new ItemStack(Material.CAMPFIRE);
ItemMeta itemMeta = itemStack.getItemMeta();
BlockData data = Material.CAMPFIRE.createBlockData();
((Campfire) data).setLit(false);
((BlockDataMeta) itemMeta).setBlockData(data);
itemStack.setItemMeta(itemMeta);

Serializing this item to a Yaml config will produce:

  ==: org.bukkit.inventory.ItemStack
  v: 1976
  type: CAMPFIRE
  meta:
    ==: ItemMeta
    meta-type: TILE_ENTITY
    BlockStateTag:
      waterlogged: 'false'
      signal_fire: 'false'
      lit: 'false'
      facing: north
    blockMaterial: CAMPFIRE

Deserializing the item from this will produce an ItemStack which then has this data internally (the following is the output of serializing the deserialized item again):

  v: 1976
  type: CAMPFIRE
  meta:
    ==: ItemMeta
    meta-type: TILE_ENTITY
    BlockStateTag:
      waterlogged: 0b
      signal_fire: 0b
      lit: 0b
      facing: north
    blockMaterial: CAMPFIRE

These two items will also no longer be considered equal.

Comment by Bjarne Koll [ 13/Dec/20 ]

@blablubbabc my first ideas of a fix would look like this: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/pull-requests/774/overview

Comment by blablubbabc [ 13/Dec/20 ]

As far as I understand, boolean nbt values are already 'escaped' in the yaml output due to them being represented as 0b and 1b according to above serialization output. The only thing missing is to check for this explicit boolean representation during deserialization, and then avoid using the result of the Mojang parser if it parses a String as boolean NBT value (similar as for integers).
Edit: Nvm. I just realized there are not NBT booleans, just NBT bytes.

Comment by Bjarne Koll [ 13/Dec/20 ]

Yeah that may actually be the best solution for this issue, similar to how we are serializing doubles and floats right now I believe.

Tho maybe simply escaping true and false directly and returning them as a string might be the most useful thing.

Comment by blablubbabc [ 12/Dec/20 ]

I believe that the issue is actually in the use of the MojangParser (Mojang's parser for the Json-like SNBT syntax) (ref: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/browse/src/main/java/org/bukkit/craftbukkit/util/CraftNBTTagConfigSerializer.java#84). If I create a quick test plugin which simply writes and reloads the String value "TRUE" from a yaml config, I get back the actual String value (because it is actually escaped with quotes, like you noted). Plugin: https://pastebin.com/MkzDk2Vc Output:

[Server thread/INFO]: [TestPlugin2] Enabling TestPlugin2 v1.0
[Server thread/INFO]: [TestPlugin2] Value of 'some key': TRUE , type: java.lang.String

Edit: The same issue has already existed in the past for integer values as well (https://hub.spigotmc.org/jira/browse/SPIGOT-4577). The fix has been to escape them when serializing them to yaml (via the "i" suffix) (https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/8059a937eb6a3e160e1421206e46d24cc5b70687). A similar approach could be applied for boolean values ("b" suffix, which it already produces due to that being Mojangs representation). When deserializing the values, we would need to check if the parsed value matches that boolean representation, and otherwise assume that it is actually a String (instead of passing it to the Mojang parser).
Edit: Maybe @LynxPlay can take a look?

Comment by Airtheon Thesalion [ 12/Dec/20 ]

This seems more like an issue with YAML itself (and then of course the parser that the API uses).
The API nevers casts values explicitely, but instead just gets their type from the parser. Furthermore the YAML standard is notorious for automatically infering many words as booleans. See Boolean Language-Independent Type for YAML™ Version 1.1 for the regex that decides if something is a boolean.

The solution for you is to explicitly write this to YAML using quotes, so '"TRUE"' instead of just 'TRUE'. Or to just use another word that means the same thing, but is not covered by the regex that YAML uses. For example 'Affirmative'.

Yes, it might be possible to fix it, by adding a check using this regex, somewhere deep inside the PersistentData Primitives, as String is one of those Primitives, but this would not be worth the effort, for such a niche usecase.

Generated at Tue Apr 15 09:25:50 UTC 2025 using Jira 10.3.3#10030003-sha1:d220e3fefc8dfc6d47f522d3b9a20c1455e12b7b.