Util Storing hidden data in ItemStacks using colors (persistent, no NMS)

Discussion in 'Resources' started by filoghost, Nov 2, 2014.

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

    filoghost

    I would like to share with you a very simple way to store any kind of data in an ItemStack's lore or custom name (using the lore is preferable because an item could be renamed with an anvil).

    The tutorial difficulty is intermediate. Not simple, not difficult. Requires a basic knowledge of the Bukkit API.

    Please let me know if you're going to use this resource in your plugins!

    Introduction
    Colors are not rendered by clients, and they can easily represent HEX strings (0-9, a-f). Two HEX numbers can represent a byte. You can easily turn strings into array of bytes and vice-versa. So, what about encoding a string into an ItemStack's, using colors? We will just use a utility class that I made for you. The class will add a header and a footer to delimit the hidden string.

    Tip: use JSON to store more than a single information.

    Example
    Referenced class: https://gist.github.com/filoghost/f53ecb7b014c40b66bdc

    We want to store a JSON stat into an iron sword: "{MobsKilled: 0}"
    When we kill 50 mobs with that sword, its name becomes "Mob Slayer".

    Code:java
    1. // We first create the item.
    2. ItemStack ironSword = new ItemStack(Material.IRON_SWORD);


    At this point, we have to decide where to store the hidden message. I will use the first line of the lore.
    Code:java
    1. // Create the hidden message.
    2. List<String> lore = new ArrayList<String>();
    3. lore.add(HiddenStringUtils.encodeString("{MobsKilled: 0}"));
    4.  
    5. // Apply the ItemMeta.
    6. ItemMeta meta = ironSword.getItemMeta();
    7. meta.setLore(lore);
    8. ironSword.setItemMeta(meta);


    This is our sword now:
    [​IMG]

    The stat is completely invisible to the clients, that will see an empty lore (there's an extra white line). You can even add some text before, because the hidden string contains a header and a footer.

    Suppose that the player now kills a mob. We want to update the stat in the sword.
    Code:java
    1. @EventHandler
    2. public void onEntityDeath(EntityDeathEvent event) {
    3. Player killer = event.getEntity().getKiller();
    4.  
    5. if (killer == null || !killer.isOnline()) {
    6. return;
    7. }
    8.  
    9. ItemStack inHand = killer.getItemInHand();
    10.  
    11. // Check if the mob was killed with an iron sword.
    12. if (inHand == null || inHand.getType() != Material.IRON_SWORD) {
    13. return;
    14. }
    15.  
    16. ItemMeta meta = inHand.getItemMeta();
    17. List<String> lore = meta.getLore();
    18.  
    19. // Check if the mob was killed with OUR sword, scanning the lore for a hidden message.
    20. if (lore != null && lore.size() > 0 && HiddenStringUtils.hasHiddenString(lore.get(0))) {
    21. // At this point we extract the json data from the item.
    22. String json = HiddenStringUtils.extractHiddenString(lore.get(0));
    23.  
    24. // Code to edit the json message, I will not explain this.
    25. ...
    26.  
    27. // The string is now "{MobsKilled: 1}" if the previous was "{MobsKilled: 0}", we replace the old json.
    28. lore.set(0, HiddenStringUtils.replaceHiddenString(lore.get(0), json));
    29.  
    30. // Now we save the changes.
    31. meta.setLore(lore);
    32.  
    33. if (mobKills >= 50) {
    34. // Change the display name with 50+ kills.
    35. meta.setDisplayName(ChatColor.GOLD + "Mob Slayer");
    36. }
    37.  
    38. inHand.setItemMeta(meta);
    39. }
    40. }


    The updated sword, with 50+ kills:
    [​IMG]

    Done! Now we have updated the hidden string in the lore, that will be saved to disk, and will persist even after a full server stop.


    Tips
    • If you want to store just a few informations, you can avoid using JSON.
    • If you have many informations to store, use multiple lines.
    • If you just want to store numbers, you can use &0-9 to represent them without using HEX codes (you have to change the code a bit).
    Final steps
    • Change the constants SEQUENCE_HEADER and the SEQUENCE_FOOTER to avoid conflicts with other plugins.
    • If you're going to use this resource give me the credits, and (if you want) put a link to this resource.
    How it works
    What happens to the string we declared in the beginning? This is the encoding part:
    • You pass the string to the method .encodeString(...)
    • The class converts the string to a byte array, using UTF-8.
    • The byte array is converted to a HEX string. (each pair of hex chars represents a byte).
    • The color char '§' is put before each char, converting the string to colors.
    • The class adds a header and a footer to delimit the string.
    • The method returns the encoded string.
    Decoding part:
    • You pass the string to the method .extractHiddenString(...)
    • The class searches for the header and the footer, extracting the string between them.
    • The color char '§' is removed from the string.
    • The HEX string is converted into a byte array.
    • A new String object is created, using the byte array with UTF-8.
    • The method returns the decoded string.
    Downsides
    • There's a limit of data that can be put in the lore. Each byte will use 4 chars, so be careful with long data.
    • I don't know if the clients lags with long invisible strings of colors.
    • I don't know the impact on the server perfomance if used frequently. This may also depend on the serialization method you use.
    • A modified client could still decode and read the hidden string, using this resource. Do NOT store sensitive data.
    • Items with different lores will not stack. That's good for us developers, but the user may be a bit confused. I would suggest to write some explicit data in the lore too.
     
    rbrick, Skyost and Plugers11 like this.
  2. Offline

    Plugers11

    It could be used on so many servers :D
    Of course like ;)
     
  3. Offline

    TheCoolGuy123_5

    kill 50 mobs*
     
  4. Offline

    filoghost

    Woops, that was a funny typo :D
     
    ChipDev likes this.
  5. Offline

    TheCoolGuy123_5

    Also another way of doing this (someone said this on the skript forums, because I don't know java) is to just put color codes like &a&7&b and do something of that. The JSON way is more practical, because you can put stats and numbers and add up on something (like kills)
     
  6. Offline

    minecreatr

    Looks very useful :D
     
  7. Offline

    Skionz

    Very nice
     
  8. Offline

    MisterErwin

    I'm just guessing there is a limit for the "data" saved on the itemstack. You might want to say that too.
    (I'm guessing the client disconnects/crashs with a too long packet length)
     
  9. Offline

    filoghost

    I said it in the downsides :)
     
Thread Status:
Not open for further replies.

Share This Page