How to serialize and deserialize inventories?

Hi!

I’m currently trying to serialize and deserialize a players inventory. I had a look around and found two methods here in the forum however they both do not work.
I’ve managed to break down the inventory into it’s itemstacks so that’s not a problem, however serializing them is turning out to be extremely difficult!

Here are the methods I tried and the result:

	private static Optional<String> serializeItemStack(ItemStack item) {
		try {
			StringWriter sink = new StringWriter();
			GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setSink(() -> new BufferedWriter(sink))
					.build();
			ConfigurationNode node = loader.createEmptyNode();
			node.setValue(TypeToken.of(ItemStack.class), item);
			loader.save(node);
			return Optional.of(sink.toString());
		} catch (Exception e) {
			e.printStackTrace();
			return Optional.empty();
		}
	}

	private static Optional<ItemStack> deserializeItemStack(String json) {
		try {
			StringReader source = new StringReader(json);
			GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
					.setSource(() -> new BufferedReader(source)).build();
			ConfigurationNode node = loader.load();
			return Optional.of(node.getValue(TypeToken.of(ItemStack.class)));
		} catch (Exception e) {
			e.printStackTrace();
			return Optional.empty();
		}
	}

However this results in a crash:

[17:13:44 ERROR] [STDERR]: java.lang.IllegalArgumentException: Configuration does not accept objects of type class java.lang.Byte
[17:13:44 ERROR] [STDERR]:        at ninja.leaping.configurate.ScalarConfigValue.setValue(ScalarConfigValue.java:42)
[17:13:44 ERROR] [STDERR]:        at ninja.leaping.configurate.SimpleConfigurationNode.insertNewValue(SimpleConfigurationNode.java:274)
[17:13:44 ERROR] [STDERR]:        at ninja.leaping.configurate.SimpleConfigurationNode.setValue(SimpleConfigurationNode.java:201)
[17:13:44 ERROR] [STDERR]:        at ninja.leaping.configurate.SimpleConfigurationNode.setValue(SimpleConfigurationNode.java:182)
[17:13:44 ERROR] [STDERR]:        at ninja.leaping.configurate.SimpleConfigurationNode.setValue(SimpleConfigurationNode.java:168)
[17:13:44 ERROR] [STDERR]:        at ninja.leaping.configurate.SimpleConfigurationNode.setValue(SimpleConfigurationNode.java:182)
[17:13:44 ERROR] [STDERR]:        at ninja.leaping.configurate.SimpleConfigurationNode.setValue(SimpleConfigurationNode.java:182)
[17:13:44 ERROR] [STDERR]:        at ninja.leaping.configurate.SimpleConfigurationNode.setValue(SimpleConfigurationNode.java:182)
[17:13:44 ERROR] [STDERR]:        at ninja.leaping.configurate.SimpleConfigurationNode.setValue(SimpleConfigurationNode.java:39)
[17:13:44 ERROR] [STDERR]:        at org.spongepowered.common.config.DataSerializableTypeSerializer.serialize(DataSerializableTypeSerializer.java:59)
[17:13:44 ERROR] [STDERR]:        at org.spongepowered.common.config.DataSerializableTypeSerializer.serialize(DataSerializableTypeSerializer.java:41)
[17:13:44 ERROR] [STDERR]:        at ninja.leaping.configurate.ConfigurationNode.setValue(ConfigurationNode.java:402)
[17:13:44 ERROR] [STDERR]:        at world.jnc.invsync.util.InventorySerializer.serializeItemStack(InventorySerializer.java:95)
[17:13:44 ERROR] [STDERR]:        at world.jnc.invsync.util.InventorySerializer.serializeInventory(InventorySerializer.java:44)
[17:13:44 ERROR] [STDERR]:        at world.jnc.invsync.PlayerEvents.onPlayerJoin(PlayerEvents.java:23)
[17:13:44 ERROR] [STDERR]:        at org.spongepowered.common.event.listener.JoinListener_PlayerEvents_onPlayerJoin32.handle(Unknown Source)
[17:13:44 ERROR] [STDERR]:        at org.spongepowered.common.event.RegisteredListener.handle(RegisteredListener.java:95)
[17:13:44 ERROR] [STDERR]:        at org.spongepowered.common.event.SpongeEventManager.post(SpongeEventManager.java:305)
[17:13:44 ERROR] [STDERR]:        at org.spongepowered.common.event.SpongeEventManager.post(SpongeEventManager.java:319)
[17:13:44 ERROR] [STDERR]:        at org.spongepowered.common.SpongeImpl.postEvent(SpongeImpl.java:133)
[17:13:44 ERROR] [STDERR]:        at net.minecraft.server.management.PlayerList.initializeConnectionToPlayer(SourceFile:1407)
[17:13:44 ERROR] [STDERR]:        at net.minecraft.server.management.PlayerList.func_72355_a(SourceFile:1173)
[17:13:44 ERROR] [STDERR]:        at net.minecraft.server.network.NetHandlerLoginServer.func_147326_c(SourceFile:120)
[17:13:44 ERROR] [STDERR]:        at net.minecraft.server.network.NetHandlerLoginServer.func_73660_a(SourceFile:66)
[17:13:44 ERROR] [STDERR]:        at net.minecraft.network.NetworkManager.func_74428_b(SourceFile:232)
[17:13:44 ERROR] [STDERR]:        at net.minecraft.network.NetworkSystem.func_151269_c(SourceFile:187)
[17:13:44 ERROR] [STDERR]:        at net.minecraft.server.MinecraftServer.func_71190_q(SourceFile:1603)
[17:13:44 ERROR] [STDERR]:        at net.minecraft.server.dedicated.DedicatedServer.func_71190_q(SourceFile:335)
[17:13:44 ERROR] [STDERR]:        at net.minecraft.server.MinecraftServer.func_71217_p(SourceFile:562)
[17:13:44 ERROR] [STDERR]:        at net.minecraft.server.MinecraftServer.run(SourceFile:466)
[17:13:44 ERROR] [STDERR]:        at java.lang.Thread.run(Thread.java:745)

The I tried this:

	private static Optional<String> serializeItemStack(ItemStack item) {
		try {
			ConfigurationNode configNode = SimpleConfigurationNode.root();

			configNode.getNode("myItem").setValue(TypeToken.of(ItemStack.class), item);

			return Optional.of(configNode.toString());
		} catch (ObjectMappingException e) {
			e.printStackTrace();
			return Optional.empty();
		}
	}

However the string I get looks like this (Essentially contains no data):

SimpleConfigurationNode{[email protected], attached=true, key=null, parent=null, [email protected]}

Can somebody help me out? I need the data in the form of a string or a byte array for external storage.

As a side note: I have a shulkerbox with some contents in my inventory. Some spawneggs are in there too (I suspect they might be the problem).

Maybe as a temp fix register a global TypeSerializer for Byte objects.

What exactly do you mean and how?

The error is that it can’t de/serialize java.lang.Byte. So create a class which implements TypeSerializer<Byte> which literally just reads/writes as int and converts it. Then register it in the TypeSerializerCollection provided by TypeSerializers.getDefaultSerializers().

1 Like

The class TypeSerializer does not exist in the API

Edit: Oops. It’s an interface. My bad

Ok. I implemented the TypeSerializer and am registering it in the preinit of my plugin. But I keep getting the same error.

Edit: Neither the Construction event, nor putting the registration in static code of the main class is working.

Does it still say the same thing about bytes?

1 Like

Exactly the same error.
If it helps here is the repo: https://github.com/BrainStone/InvSync

It may have something to do with the fact that you’re serializing the whole thing to a relational database. If you were to serialize an inventory, it would be best represented as an NBT file, an object-structured text file (like JSON), or a nonrelational database (like MongoDB), since those formats are designed for arbitrary data (which inventories and items are) rather than relational databases which are optimized for identical data format sets.

Anyway, try using toContainer() and then using DataTranslators.CONFIGURATION_NODE

1 Like

I’m trying to convert the data to JSON. That’s the part that is not working.
Or a String or byte array for that matter.

@pie_flavor: Thank you alot. It is partially working, however NBT infomation like Enchantments aren’t applied to the item when reconstructing it like this, even though the data is in the string!

private static Optional<ItemStack> deserializeItemStack(String json) {
	try {
		StringReader source = new StringReader(json);
		GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
				.setSource(() -> new BufferedReader(source)).build();
		ConfigurationNode node = loader.load();
		return Optional
				.of(ItemStack.builder().fromContainer(DataTranslators.CONFIGURATION_NODE.translate(node)).build());
	} catch (Exception e) {
		e.printStackTrace();
		return Optional.empty();
	}
}

Any idea how to fix that? (And if I should make a new thread for it)

There’s already a ticket about that, it’s something to do with the deserialization​ process.

1 Like

So a bug in Sponge?
In that case let’s hope it gets fixed soon. And do you have a link to the issue?

Thank you alot! You were a great help!

Oh, interesting. Tell me what happens when you serialize to HOCON instead of JSON.

How exactly would I do this? I’m pretty new to sponge.

Just use the HoconConfigurationLoader instead of the GsonConfigurationLoader

@Valandur Thanks. That worked!
@pie_flavor Very strange as this working. Seems to be a bug in the GsonConfigurationLoader. If there isn’t a bug report maybe we should make one.