1.8.8 How to get a ItemStack from a serializated String?

Discussion in 'Spigot Plugin Development' started by Mxxthi, Feb 27, 2020.

  1. Hey,

    I would like to save my inventory in a MySQL database and have already informed myself about it.
    I am using this method:
    This is my conversion to a String, which I'm am using for my Database. I place barriers here for free space in the inventory. Because otherwise, I will get a null error. BUT its working.

    Code (Java):
    Inventory invv = Bukkit.createInventory(null, InventoryType.PLAYER);
                for (int j = 0; j < 9; j++) {
                    invv.setItem(j, inv.getItem(j));
                    if (inv.getItem(j) == null)
                        invv.setItem(j, new ItemBuilder(Material.BARRIER).build());
                }
             
             
                Map<String, Object> i0 = invv.getItem(0).serialize();
                Map<String, Object> i1 = invv.getItem(1).serialize();
                Map<String, Object> i2 = invv.getItem(2).serialize();
                Map<String, Object> i3 = invv.getItem(3).serialize();
                Map<String, Object> i4 = invv.getItem(4).serialize();
                Map<String, Object> i5 = invv.getItem(5).serialize();
                Map<String, Object> i6 = invv.getItem(6).serialize();
                Map<String, Object> i7 = invv.getItem(7).serialize();
                Map<String, Object> i8 = invv.getItem(8).serialize();
                try {
                    PreparedStatement ps0 = MySQL.getConnection().prepareStatement("INSERT INTO `inventories`(`UUID`, `ITEM_ID`, `SLOT`) VALUES (?,?,?)");
                    ps0.setString(1, uuid.toString());
                    ps0.setString(2, i0.toString());
                    ps0.setInt(3, 0);
                    ps0.execute();
    And here is my try to get the ItemStacks back. (I'm using an ItemBuilder)

    Code (Java):
    public Inventory getInventory(UUID uuid) {
            if(isInventoryExists(uuid)) {
                Inventory inv = Bukkit.createInventory(null, InventoryType.PLAYER);
                try {
                    for (int j = 0; j < 9; j++) {
                    PreparedStatement ps = MySQL.getConnection().prepareStatement("SELECT ITEM_ID FROM `inventories` WHERE UUID = ? AND SLOT = ?");
                    ps.setString(1, uuid.toString());
                    ps.setInt(2, j);
                    ResultSet rs = ps.executeQuery();
                    Map<String, Object> serialization = (Map<String, Object>) ps.executeQuery();
                    ItemStack i = ItemStack.deserialize(serialization);
                        if (i.getType() == Material.BARRIER) {
                            inv.setItem(j, new ItemBuilder(Material.AIR).build());
                        }else {
                            inv.setItem(j, i);
                        }
                    }
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return inv;
            }else {
                return null;
            }
         
        }
    And now i am getting a null-Error (I am trying to set the inventory here with a command)

    Code (Text):

    [00:50:17 ERROR]: null
    org.bukkit.command.CommandException: Unhandled exception executing command 'gi' in plugin MLG-Rush v1.0
            at org.bukkit.command.PluginCommand.execute(PluginCommand.java:46) ~[spigot.jar:git-Spigot-db6de12-18fbb24]
            at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:141) ~[spigot.jar:git-Spigot-db6de12-18fbb24]
            at org.bukkit.craftbukkit.v1_8_R3.CraftServer.dispatchCommand(CraftServer.java:641) ~[spigot.jar:git-Spigot-db6de12-18fbb24]
            at net.minecraft.server.v1_8_R3.PlayerConnection.handleCommand(PlayerConnection.java:1162) [spigot.jar:git-Spigot-db6de12-18fbb24]
            at net.minecraft.server.v1_8_R3.PlayerConnection.a(PlayerConnection.java:997) [spigot.jar:git-Spigot-db6de12-18fbb24]
            at net.minecraft.server.v1_8_R3.PacketPlayInChat.a(PacketPlayInChat.java:45) [spigot.jar:git-Spigot-db6de12-18fbb24]
            at net.minecraft.server.v1_8_R3.PacketPlayInChat.a(PacketPlayInChat.java:1) [spigot.jar:git-Spigot-db6de12-18fbb24]
            at net.minecraft.server.v1_8_R3.PlayerConnectionUtils$1.run(SourceFile:13) [spigot.jar:git-Spigot-db6de12-18fbb24]
            at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) [?:1.8.0_231]
            at java.util.concurrent.FutureTask.run(Unknown Source) [?:1.8.0_231]
            at net.minecraft.server.v1_8_R3.SystemUtils.a(SourceFile:44) [spigot.jar:git-Spigot-db6de12-18fbb24]
            at net.minecraft.server.v1_8_R3.MinecraftServer.B(MinecraftServer.java:715) [spigot.jar:git-Spigot-db6de12-18fbb24]
            at net.minecraft.server.v1_8_R3.DedicatedServer.B(DedicatedServer.java:374) [spigot.jar:git-Spigot-db6de12-18fbb24]
            at net.minecraft.server.v1_8_R3.MinecraftServer.A(MinecraftServer.java:654) [spigot.jar:git-Spigot-db6de12-18fbb24]
            at net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:557) [spigot.jar:git-Spigot-db6de12-18fbb24]
            at java.lang.Thread.run(Unknown Source) [?:1.8.0_231]
    Caused by: java.lang.ClassCastException: com.mysql.jdbc.JDBC4ResultSet cannot be cast to java.util.Map
            at de.mxxthi.mlgrush.mysql.MySQLInventories.getInventory(MySQLInventories.java:219) ~[?:?]
            at de.mxxthi.mlgrush.commands.GetInventory.onCommand(GetInventory.java:27) ~[?:?]
            at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[spigot.jar:git-Spigot-db6de12-18fbb24]
            ... 15 more
     
    If you need more information (about the command or something), please tell me..
     
    #1 Mxxthi, Feb 27, 2020
    Last edited: Feb 28, 2020
  2. There are two things to unpack here.
    First, you're calling "qs.executeQuery()" twice and the second time you try to cast it to a map, which obviously fails. "executeQuery()" returns a result set, like you do in the first call; you cannot just cast it to a map. A ResultSet is a set of resulting rows that the query returned, so basically all the rows you previously saved. That means, you'd need to look at each row independently, by iterating through it (using "ResultSet.next()" and "ResultSet.getString")

    Secondly, you can't easily get from a string back to a map, unlike getting from a map to a string. To work around that you'd have to manually define how to go from map to string, so when you read it, you can invert this process. For example, you could loop through the entries of the map, turn each entry into a string in the format of "key:value" and then concatenate these with ";". When reading, you could then inverse this process (by splitting first at ";" and then at ":"). However, I don't believe this is a sensible solution given that you need to handle multiple types of values (since there could be another map as a value!) which makes this so much more complicated. My advice would be to serialize the items into base64 (for example, using this) instead of using "ItemStack.serialize()" directly. That way, you have a type that you can directly store in the database (i.e. string) and don't have anything in between.