[Util] Edit ItemStack attributes, adding speed, damage or health bonuses.

Discussion in 'Resources' started by Comphenix, Jul 7, 2013.

  1. The new attribute system in 1.6.1 allows map makers and plugin authors (yes, you) to set arbitrary bonuses on armor and weaponry, much like any old RPG. This include things like extra health, displayed as additional hearts in the UI and faster movement speed and "follow range" on mobs - things that would have been difficult to impossible to do in 1.5.2.

    Unfortunately, this system isn't exposed as an API in CraftBukkit yet, nor is it entirely supported. ItemStacks may lose their attributes when they're cloned, or when plugins edit their ItemMeta. But you can still play with this system - it's just a bit buggy.

    You can get my utility for editing attributes here:
    https://gist.github.com/aadnk/5943946

    However, is it dependent on 1.6.1, and since 1.6.2 is soon out, it might be a good idea to use a version-independent edition. It's dependent on ProtocolLib instead:
    https://gist.github.com/aadnk/5943952

    NEW! If you add this class to your project (thread), you'll avoid referencing CB/NMS without also having to depend on ProtocolLib:
    https://gist.github.com/aadnk/6754159

    In either case, it's very easy to use:
    Code:java
    1. @Override
    2. public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
    3. if (sender instanceof Player) {
    4. ItemStack helmet = new ItemStack(Material.GOLD_HELMET);
    5. Attributes attributes = new Attributes(helmet);
    6.  
    7. attributes.add(Attribute.newBuilder().name("Health")
    8. .type(AttributeType.GENERIC_MAX_HEALTH).amount(20).build());
    9.  
    10. attributes.add(Attribute.newBuilder().name("Speed")
    11. .type(AttributeType.GENERIC_MOVEMENT_SPEED).amount(2).build());
    12.  
    13. ((Player) sender).getInventory().addItem(attributes.getStack());
    14. }
    15. return true;
    16. }

    Just remember to always call attributes.getStack(), as the item stack may get recreated as a CraftItemStack underneath.
     
  2. Comphenix
    Very nice! May use this.
    You mentioned you knew how to remove them with metadata? How?
     
  3. Currently, they're always removed when a plugin is using ItemMeta:
    Code:java
    1. ItemStack result = attributes.getStack();
    2. result.setItemMeta(result.getItemMeta());
    3.  
    4. ((Player) sender).getInventory().addItem(result);

    The reason for this, is that ItemMeta will only extract information it is familiar with from the native NBTTagCompound field, expose it for modification, and then restore it using a new NBTTagCompound. Any data ItemMeta isn't aware of will be deleted. This is presumably intentional, and serves as a way to discourage saving data as custom NBT tags. So, plugins typically store metadata using lore lines instead.

    Of course, this will be fixed when the Bukkit team adds support for attributes for item stacks and entities, which should be fairly easy to do with the current system.
     
    hawkfalcon likes this.
  4. For me it gets cleared after opening inventory :/
     
  5. Strange. It worked fine for me as long as I didn't change game mode.

    Though now that you mention it, it's possible this doesn't work at all in creative mode, only survival mode. Unfortunately, there's very few ways aroundt this - either simulate it by setting the attributes on the entity yourself, or patch the server so that it at least keeps this information around.

    It is possible to patch the server in memory, but it's exceedingly difficult as all the possible "entry points" are final. You can do this with PowerMock, but it's ment for testing and not production. I doubt a plugin could use it. So - you have to fork CraftBukkit, modify it and distribute it.
     
  6. I had ready lib for attributes 5 days ago and worked on it for about 3-4 hours, but i thought it's not working. I tested it on creative...
     
    Comphenix likes this.
  7. stirante and hawkfalcon like this.
  8. I have a question, does this have to have ProtocolLib or is it independent?
     
  9. he has 2 versions.
     
    Comphenix likes this.
  10. Does this work on Bukkit 1.6.2? I've tested the independent version - changed imports for new NMS and nothing. No effect, did something changed?
     
  11. KodekPL Może testujesz na trybie kreatywnym? W trybie kreatywnym bukkit czyści od razu wszystkie nieznane tagi NBT.
    //Maybe you test it on creative? In Creative mode bukkit automaticlly cleares all unknown tags.
     
  12. Okay, that was not fault of gamemode. Itemstack in hand of player didn't want to update. Sorry.
    It's really helpful utility, thanks!
     
  13. I've found a different workaround for the creative problem (download):
    Code:java
    1. public class ExampleMod extends JavaPlugin implements Listener {
    2. private ItemStack expectedCursor;
    3. private long expectedTime;
    4.  
    5. @Override
    6. public void onEnable() {
    7. getServer().getPluginManager().registerEvents(this, this);
    8.  
    9. ProtocolLibrary.getProtocolManager().addPacketListener(
    10. new PacketAdapter(
    11. this, ConnectionSide.CLIENT_SIDE, new ListenerOptions[] { ListenerOptions.INTERCEPT_INPUT_BUFFER }, Packets.Client.SET_CREATIVE_SLOT) {
    12. @Override
    13. public void onPacketReceiving(PacketEvent event) {
    14. DataInputStream input = event.getNetworkMarker().getInputStream();
    15. try {
    16. // Read slot
    17. input.readShort();
    18. ItemStack stack = readItemStack(input, new StreamSerializer());
    19.  
    20. // Undo the stupid "item meta" cleanup
    21. event.getPacket().getItemModifier().write(0, stack);
    22.  
    23. // Update the next event handler
    24. expectedTime = System.currentTimeMillis();
    25. expectedCursor = stack;
    26.  
    27. } catch (IOException e) {
    28. e.printStackTrace();
    29. }
    30. }
    31. });
    32. }
    33.  
    34. @EventHandler(priority = EventPriority.LOWEST)
    35. public void onCreativeInventory(InventoryCreativeEvent e) {
    36. if ((System.currentTimeMillis() - expectedTime) < 50) {
    37. e.setCursor(expectedCursor);
    38. }
    39. }
    40.  
    41. private ItemStack readItemStack(DataInputStream input, StreamSerializer serializer) throws IOException {
    42. ItemStack result = null;
    43. short type = input.readShort();
    44.  
    45. if (type >= 0) {
    46. byte amount = input.readByte();
    47. short damage = input.readShort();
    48.  
    49. result = new ItemStack(type, amount, damage);
    50. NbtCompound tag = serializer.deserializeCompound(input);
    51.  
    52. if (tag != null) {
    53. result = MinecraftReflection.getBukkitItemStack(result);
    54. NbtFactory.setItemTag(result, tag);
    55. }
    56. }
    57. return result;
    58. }
    59.  
    60. @Override
    61. public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
    62. if (sender instanceof Player) {
    63. ItemStack helmet = new ItemStack(Material.GOLD_HELMET);
    64. Attributes attributes = new Attributes(helmet);
    65.  
    66. attributes.add(Attribute.newBuilder().name("Health")
    67. .type(AttributeType.GENERIC_MAX_HEALTH).amount(20).build());
    68.  
    69. attributes.add(Attribute.newBuilder().name("Speed")
    70. .type(AttributeType.GENERIC_MOVEMENT_SPEED).amount(2).build());
    71.  
    72. ((Player) sender).getInventory().addItem(attributes.getStack());
    73. }
    74. return true;
    75. }
    76. }

    This particular solution requires ProtocolLib 2.5.0 or newer, though I'm sure there's a way that doesn't require it. The key here is to reapply the Attributes in InventoryCreativeEvent.
     
    Cybermaxke likes this.
  14. I should probably mention that the latest CraftBukkit build (#2822++) support attributes out of the box - though without an attribute API.

    So, the ProtocolLib method in post #13 is no longer necessary.
     
  15. This looks amazing. :O Thanks!
     
  16. Been trying to do this on offline players for a couple hours now...any tips?
     
  17. Comphenix
    Nice!

    (Trodde aldri du var Norsk? :p) <- Translate: Never thought you were Norwegian? :p
     
  18. How are you modifying a offline player's inventory? Keep in mind that Bukkit doesn't support serializing and de-serializing attributes, though it should work fine if Minecraft itself is doing that job.

    Maybe I just chose a random name and country to obfuscate my identity? A pseudonym to protect my privacy.

    Neida, er ikke så paranoid. Har jo til og med et bilde av fjeset mitt på GitHub. :p
     
  19. Comphenix, I'm trying to change the generic.maxHealth attribute of player's dat files.
     
  20. By modifying the NBT data manually?

    Perhaps you could post a code snippet?
     
  21. I tried doing it with your library, but nothing worked.
     
  22. My library DOES NOT support offline players, so I don't know what you did.

    You can edit the dat-files, but you need to rewrite the attribute classes extensively to use the slightly different NBT format.
     
  23. Alright thank you very much, I had also tried another public library for offline players that didn't support Attributes that I tried to implement, but the key "generic.maxHealth" wasn't found in the dat files for some reason. Either that or the string I entered was wrong.
     
  24. Offline

    netizen539


    =( Why?! This is exactly what I want to do. Just an arbitrary NBTCompound tag titled "BukkitPluginCustom" *sigh*

    Anyway great job on this, its really cool and helpful!
     
  25. So what version should we use right now?​
     
  26. Both works, you just have to decide whether or not to depend on ProtocolLib or CraftBukkit 1.6.2.

    It's probably best to just go for CB, unless you already are using ProtocolLib.
     
  27. I've now added a new version that is version-independent, without relying on ProtocolLib. Take a look at the main post for more information.
     
  28. Ok, i'm trying to get the damage of the players' item in hand, and damage the nearby entities with this damage. All is ready except the part from getting the damage. How do i can do that?
     
  29. Yeah i know, but how i can convert o an int?
    int damage = ?
    Current code:
    Code:java
    1. ItemStack hand = yaw.getItemInHand();
    2. Attributes attributesa = new Attributes(hand);
    3. attributesa.add(Attribute.newBuilder().name("Damage")
    4. .type(AttributeType.GENERIC_ATTACK_DAMAGE).amount(2).build());
     

Share This Page