Bukkit Version Proof ItemStack Serialization

Discussion in 'Resources' started by Ultimate_n00b, Oct 13, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    Ultimate_n00b

    Code:java
    1. public class ItemHandler {
    2.  
    3. public static String toString(ItemStack i) {
    4. return mapToString(i.serialize());
    5. }
    6.  
    7. public static ItemStack fromString(String s) {
    8. return ItemStack.deserialize(stringToMap(s));
    9. }
    10.  
    11. private static String mapToString(Map<String, Object> map) {
    12. StringBuilder stringBuilder = new StringBuilder();
    13. for (String key : map.keySet()) {
    14. if (stringBuilder.length() > 0) {
    15. stringBuilder.append("&");
    16. }
    17. String value = map.get(key).toString();
    18. try {
    19. stringBuilder.append((key != null ? URLEncoder.encode(key, "UTF-8") : ""));
    20. stringBuilder.append("=");
    21. stringBuilder.append(value != null ? URLEncoder.encode(value, "UTF-8") : "");
    22. throw new RuntimeException("This method requires UTF-8 encoding support", e);
    23. }
    24. }
    25. return stringBuilder.toString();
    26. }
    27.  
    28. private static Map<String, Object> stringToMap(String input) {
    29. Map<String, Object> map = new HashMap<String, Object>();
    30. String[] nameValuePairs = input.split("&");
    31. for (String nameValuePair : nameValuePairs) {
    32. String[] nameValue = nameValuePair.split("=");
    33. try {
    34. map.put(URLDecoder.decode(nameValue[0], "UTF-8"),
    35. nameValue.length > 1 ? URLDecoder.decode(nameValue[1], "UTF-8") : "");
    36. throw new RuntimeException("This method requires UTF-8 encoding support", e);
    37. }
    38. }
    39.  
    40. return map;
    41. }
    42. }

    A quick class a threw together as to serialize itemstacks to strings, then back to itemstacks.
     
    Chinwe likes this.
  2. Offline

    MiniDigger

    Ultimate_n00b can this be used with other Objects that implements ConfigurationSerialisation? It would be a nice way so serialize custom object to strings for databases :D
     
  3. Offline

    Ultimate_n00b

    MiniDigger I believe so.


    The way I automatically assume the second thing in the map is a String doesn't work in a few cases, so I'm working on a way to fix it.
     
  4. Offline

    MiniDigger

    Ultimate_n00b that would be realy nice since i planed to use a database instad of files and I using the serialisation thing for all my custom objects :D.
    Cant i just put a string on second place to avoid this?
     
  5. Offline

    MrTwiggy

    You might want to look into using JSON, as it is basically designed to do what you're doing, but much cleaner/better.
     
  6. Offline

    Ultimate_n00b

    Do I need a lib for json? If so, no thanks.
     
  7. Offline

    drtshock

    gson is built into bukkit and simple-json is in craftbukkit.

    Also, what's wrong with using the api for this? ConfigurationSerializable and BukkitObjectInputStream work for this :)
     
  8. Offline

    MiniDigger

    drtshock I am using the ConfiguartionSerializable but I want it to return a String to put it into a database or something like that. I never heard of BukkitObjectInputStream. Ill google it now :D
    EDIT: I found something but i dont understand it -.-. Are there some ressorces about this to learn it? I found nothing. And I dont what the api docs trying to say me :D
    EDIT 2: Found something, tested it and... got an error https://gist.github.com/MiniDigger/6992945 -.-
     
  9. Offline

    Njol

    Take a look at ItemStack's serialize() method:
    Code:java
    1. public Map<String, Object> serialize() {
    2. Map<String, Object> result = new LinkedHashMap<String, Object>();
    3.  
    4. result.put("type", getType().name());
    5.  
    6. if (getDurability() != 0) {
    7. result.put("damage", getDurability());
    8. }
    9.  
    10. if (getAmount() != 1) {
    11. result.put("amount", getAmount());
    12. }
    13.  
    14. ItemMeta meta = getItemMeta();
    15. if (!Bukkit.getItemFactory().equals(meta, null)) {
    16. result.put("meta", meta);
    17. }
    18.  
    19. return result;
    20. }


    As you can see, it puts the item's ItemMeta directly into the map without serializing it, so your code will not work for item stacks with enchantments or other data.

    edit: actually it will crash under any circumstances: you load numbers as strings, but ItemStack's deserialize(Map) casts them to Number and Integer, resulting in a ClassCastException. Have you ever tested your code?

    edit 2: to make things more complicated, CraftMetaItem also saves a List<String>. I highly recommend to just use a new YamlConfiguration, save the ItemStack, and use config.saveToString() to get the saved ItemStack:
    Code:java
    1. final YamlConfiguration y = new YamlConfiguration();
    2. y.set("value", is);
    3. return y.saveToString();
     
  10. Offline

    Ultimate_n00b

    I have tested my code, being why I have never bumped this thread. I currently use this with it's online counterpart here.
     
  11. Offline

    Ne0nx3r0

    Stumbled across this and I thought I'd add a few cents... Isn't this code susceptible to an "item meta injection"?

    For example, if I used an anvil to name an item "a;amount=64" fromString(String) would grab a for the item's name as "a" then set the amount to 64. You could do the same thing to get enchantments, custom lore, etc...

    I mean granted you'd have to know how the name was being stored, but a clever person could really abuse the heck out of this.
     
    Rayzr522 likes this.
Thread Status:
Not open for further replies.

Share This Page