Enchantments couldn't retreat due ItemStack serialization

enchantments couldn’t retreat due ItemStack serialization.

code:

Player player = (Player) src;
String data;

{//serialize
    StringWriter sink = new StringWriter();
    GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setSink(() -> new BufferedWriter(sink)).build();
    final DataTranslator<ConfigurationNode> translator = DataTranslators.CONFIGURATION_NODE;
    final DataView container = player.getItemInHand(HandTypes.OFF_HAND).get().toContainer();
    try {
        loader.save(translator.translate(container));
    } catch (IOException e) {
        e.printStackTrace();
    }
    data = sink.toString();
}

System.out.println(data);//line 16, check serialize data

{//deserialize
    StringReader source = new StringReader(data);
    GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setSource(() -> new BufferedReader(source)).build();
    ConfigurationNode node = null;
    try {
        node = loader.load();
    } catch (IOException e) {
        e.printStackTrace();
    }
    final DataTranslator<ConfigurationNode> translator = DataTranslators.CONFIGURATION_NODE;
    final DataView container = translator.translate(node);
    ItemStack itemStack = Sponge.getDataManager().deserialize(ItemStack.class, container).get();
    player.getInventory().offer(itemStack);
}

Output of line 16:

[21:50:06] [Server thread/INFO] [STDOUT]: [***.execute(***.java:16)]: {
  "ContentVersion": 1,
  "ItemType": "minecraft:enchanted_book",
  "Count": 1,
  "UnsafeDamage": 0,
  "UnsafeData": {
    "StoredEnchantments": [
      {
        "lvl": "1",
        "id": "33"
      }
    ]
  }
}

Off hand item is a enchanted book of silk touch, after deserialize, it became protect I.

Dig more:

ConfigurationNode node1 = null;
ConfigurationNode node2 = null;
{//serialize
    StringWriter sink = new StringWriter();
    GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setSink(() -> new BufferedWriter(sink)).build();

    final DataTranslator<ConfigurationNode> translator = DataTranslators.CONFIGURATION_NODE;
    final DataView container = player.getItemInHand(HandTypes.OFF_HAND).get().toContainer();

    try {
        node1 = translator.translate(container);
        loader.save(node1);

    } catch (IOException e) {
        e.printStackTrace();
    }
    data = sink.toString();
}


{//deserialize
    StringReader source = new StringReader(data);
    GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setSource(() -> new BufferedReader(source)).build();
    try {
        node2 = loader.load();
        System.out.println(node2.getString());
        System.out.println(node1.getString());
    } catch (IOException e) {
        e.printStackTrace();
    }
    final DataTranslator<ConfigurationNode> translator = DataTranslators.CONFIGURATION_NODE;
    final DataView container1 = translator.translate(node1);
    final DataView container2 = translator.translate(node2);
    ItemStack itemStack1 = Sponge.getDataManager().deserialize(ItemStack.class, container1).get();
    ItemStack itemStack2 = Sponge.getDataManager().deserialize(ItemStack.class, container2).get();
    player.getInventory().offer(itemStack1);
    player.getInventory().offer(itemStack2);
}

Output:

[08:18:46] [Server thread/INFO] [STDOUT]: [***.execute(***.java:64)]: {ContentVersion=1, ItemType=minecraft:enchanted_book, Count=1, UnsafeDamage=0, UnsafeData={StoredEnchantments=[{lvl=3, id=35}]}}
[08:18:46] [Server thread/INFO] [STDOUT]: [***.execute(***.java:65)]: {ContentVersion=1, ItemType=minecraft:enchanted_book, Count=1, UnsafeDamage=0, UnsafeData={StoredEnchantments=[{lvl=3, id=35}]}}

Version: SpongeVanilla version 1.11.2-6.0.0-BETA-249

Two node seems same but serialize one was broken.

Same problem but with the author data of a book. Here I use ItemStackSnapshot but that shouldn’t make a difference.
I haven’t tested any other data yet because I thought I might use it in a wrong way.

Nope, I’ve ready you post, but writtenbook is fine in my case. ItemStackSnapshot also works too.

Does my code work for you?

Can you tell me how would you get configLoader in your case?

Through @Inject but this can also be done like in your code.

After running your code, this show up in my config file:

itemstack-snapshot {
    ContentVersion=1
    Count=1
    Data=[
        {
            DataClass="org.spongepowered.common.data.manipulator.immutable.item.ImmutableSpongeAuthorData"
            ManipulatorData {
                BookAuthor="{\"text\":\"Author One\"}"
            }
        },
        {
            DataClass="org.spongepowered.common.data.manipulator.immutable.item.ImmutableSpongeGenerationData"
            ManipulatorData {
                ContentVersion=1
                Generation=0
            }
        }
    ]
    ItemType="minecraft:written_book"
    UnsafeDamage=0
    UnsafeData {
        author="Author One"
    }
}

Does it works?

That’s the same for me. But:

The deserialization from the DataView to the actual ItemStackSnapshot doesn’t work properly. The author data isn’t set onto the ItemStackSnapshot.

Okay, I got this after add line:
player.getInventory().offer(deserializedItemStackSnapshot.createStack());

But, what you want?
How about this:

ConfigurationLoader<CommentedConfigurationNode> configLoader = ...
CommentedConfigurationNode rootNode = null;
try {
    rootNode = configLoader.load();
} catch (IOException e) {
    e.printStackTrace();
}

CommentedConfigurationNode node = rootNode.getNode("itemstack-snapshot");

ItemStack itemStack = ItemStack.of(ItemTypes.WRITTEN_BOOK, 1);
itemStack.offer(Keys.DISPLAY_NAME, Text.of("new book"));
itemStack.offer(Keys.BOOK_AUTHOR, Text.of("aaa") );
itemStack.offer(Keys.BOOK_PAGES, Arrays.asList(Text.of("some content")));

// To node
ConfigurationNode itemStackNode = DataTranslators.CONFIGURATION_NODE.translate(itemStack.toContainer());

// From node
DataContainer dataContainer = DataTranslators.CONFIGURATION_NODE.translate(itemStackNode);
ItemStack deserializedItemStack = Sponge.getDataManager()
        .deserialize(ItemStack.class, dataContainer).get();

// And see result in config file for easier debugging
node.setValue(itemStackNode);
try {
    configLoader.save(rootNode);
} catch (IOException e) {
    e.printStackTrace();
}

player.getInventory().offer(deserializedItemStack);

I can get correct item with this code.

I have to admit the author data suddenly works. I may have done dumb things while testing.
I also get the “Invalid book tag” and the book is enchanted for no reason(it appears enchanted; that shiny look). The enchantment probably is connected to your issue.

ItemStackSnapshot itemStackSnapshot = ItemStack.builder()
        .quantity(1)
        .itemType(ItemTypes.WRITTEN_BOOK)
        .add(Keys.DISPLAY_NAME, Text.of("Book Title One"))
        .add(Keys.BOOK_AUTHOR, Text.of("Author One"))
        .add(Keys.BOOK_PAGES, Collections.singletonList(Text.of("Page Text One")))
        .build().createSnapshot();

ItemStackSnapshot a = Sponge.getDataManager().deserialize(ItemStackSnapshot.class, itemStackSnapshot.toContainer()).orElse(null);

player.getInventory().offer(a.createStack());

I don’t get invalid book tag. But here I don’t write it to a config.

Edit: Works now for me: Deserialization of config node fails at data manipulators - #3 by RandomByte
Edit2: Man, I don’t play enough Minecraft; written books look enchanted in Vanilla too. :joy:

@TonyFoster this works for me, it is like your code: public static void doStuff2(Player player) throws IOException { // seri - Pastebin.com

But enchantments are altered like you described.

written books look enchanted in Vanilla too.

Maybe it’s because the written book are born before the enchantment book.:slight_smile:

But enchantments are altered like you described.

Thank you for confirm!

The outputted debug string reflects the correct enchantment ids and level. The problem is here again the deserialization of the DataView. The attached data(here the enchantments) doesn’t get deserialized correctly. The enchantment just defaults to protection level 0.

But until the ConfigurationNode is fine, I mean if don’t serialize to string, it works fine.

So you mean config node to data view is bad?

No.

ItemStack -> DataView -> ConfigurationNode -> String -> ConfigurationNode* -> DataView -> ItemStack
This is what we do now, but have some issue.

ItemStack -> DataView -> ConfigurationNode* -> DataView -> ItemStack
This will work perfectly but I still need String to store something.

I can’t understand why ConfigurationNode produce by this two way(signd by *) give seems equals result, but only first way has issue?

I have update some detail in my first post.

Correct?

1 Like

I think the describe of issue is correct.
Thank you!!:smile:

1 Like

I think problem is solved by using HoconConfigurationLoader instead GsonConfigurationLoader. Because I noticed issue isn’t between ConfigurationNode and DataView, and Sponge Documentation is using HoconConfigurationLoader , not GsonConfigurationLoader.

Full example:

@Override
	public CommandResult execute(CommandSource src, CommandContext args) throws CommandException {
    if (!(src instanceof Player)) return CommandResult.empty();
    Player player = (Player) src;
    String data;

    {//serialize
        StringWriter sink = new StringWriter();
        HoconConfigurationLoader loader
                = HoconConfigurationLoader.builder().setSink(() -> new BufferedWriter(sink)).build();
        final DataTranslator<ConfigurationNode> translator = DataTranslators.CONFIGURATION_NODE;
        final DataView container
                = player.getItemInHand(HandTypes.OFF_HAND).get().toContainer();//Can be any DataSerializable object
        ConfigurationNode node = translator.translate(container);
        try {
            loader.save(node);
        } catch (IOException e) {
            e.printStackTrace();
        }
        data = sink.toString();//The String can store to anywhere you want.
    }

    {//deserialize
        StringReader source = new StringReader(data);
        HoconConfigurationLoader loader =
                HoconConfigurationLoader.builder().setSource(() -> new BufferedReader(source)).build();
        ConfigurationNode node = null;
        try {
            node = loader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }
        final DataTranslator<ConfigurationNode> translator = DataTranslators.CONFIGURATION_NODE;
        final DataView container = translator.translate(node);
        ItemStack itemStack
                = Sponge.getDataManager().deserialize(ItemStack.class, container).get();//Get your object back!
        player.getInventory().offer(itemStack);
    }

    return CommandResult.success();
}