Default Item Attributes

Discussion in 'Resources' started by metalhedd, Jan 12, 2014.

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

    metalhedd

    I stumbled across an interesting technique this afternoon for applying attribute modifiers to items. These modifiers apply to ALL items of the specified type, whether newly created or pre-existing, even better, they disappear on server restart if you don't re-register them. What this means is, you can make ALL diamond armor have a -speed modifier, or all wool blocks give you knockback resistance when held. All sorts of wacky things. The technique is rather simple. First you subclass the nms Item subclass you wish to modify, then you add it to the Item REGISTRY.

    in it's simplest form:
    Code:java
    1.  
    2. // get the Item instance that we wish to modify from the registry
    3. Item item = (Item) Item.REGISTRY.a("diamond_leggings");
    4.  
    5. // Create a custom version. this will be explained later.
    6. ModifiedItemArmor modified = new ModifiedItemArmor((ItemArmor) item, modifiersToAdd);
    7.  
    8. // Retrieve the ID from the registry, so that we can register this new Item in the same spot as the old one.
    9. int id = Item.REGISTRY.b(item);
    10. // Add the new Item to the registry. The server will complain on startup that a duplicate entry is being added. Oh well! :)
    11. Item.REGISTRY.a(id, "diamond_leggings", modified);
    12.  

    Obviously the real magic happens in our ModifiedItemArmor class. Here we're responsible for making an exact copy of the original Item instance, with a single method overridden. I struggled with this for a while, as java reflection is not my area of expertise. Keep in mind that every item you subclass this way will be different. Many will require even more reflection to copy all the relevant properties.
    Code:java
    1.  
    2. public class ModifiedItemArmor extends ItemArmor {
    3. // we use a multimap to hold our modifiers, to allow us to define
    4. // more than one modifier for an attribute.
    5. private final Multimap<String, AttributeModifier> modifiers;
    6. public ModifiedItemArmor(ItemArmor armor, Multimap<String, AttributeModifier> modifiers) {
    7. // The ItemArmor constructor needs these 3 values
    8. // which luckily are available to us without reflection
    9. super(armor.m_(), armor.d, armor.b);
    10. this.modifiers = modifiers;
    11. // this method copies all of the 'base' properties of the Item class.
    12. // it should be the same for every custom Item subclass you define.
    13. Reflection.copyItem(armor, this);
    14. }
    15. @Override
    16. public Multimap k() {
    17. // This is where we add our default modifiers!
    18. Multimap localMultimap = super.k();
    19. for (String s: modifiers.keySet()) {
    20. localMultimap.putAll(s, modifiers.get(s));
    21. }
    22. return localMultimap;
    23. }
    24. }
    25.  

    This class contains the copyItem method we use to copy all of the base properties
    Code:java
    1.  
    2. public class Reflection {
    3. private static Field nameField;
    4. private static Field altNameField;
    5. private static Field toolMaterialField;
    6. private static Field hoeMaterialField;
    7. private static Field creativeModeTabField;
    8. private static Field durabilityField;
    9. private static Field itemIField;
    10. private static Field itemJField;
    11. private static Field itemDField;
    12. private static Field craftingResultField;
    13. static {
    14. try {
    15. nameField = Item.class.getDeclaredField("name");
    16. nameField.setAccessible(true);
    17. altNameField = Item.class.getDeclaredField("l");
    18. altNameField.setAccessible(true);
    19. toolMaterialField = ItemTool.class.getDeclaredField("b");
    20. toolMaterialField.setAccessible(true);
    21. hoeMaterialField = ItemHoe.class.getDeclaredField("a");
    22. hoeMaterialField.setAccessible(true);
    23. creativeModeTabField = Item.class.getDeclaredField("a");
    24. creativeModeTabField.setAccessible(true);
    25. durabilityField = Item.class.getDeclaredField("durability");
    26. durabilityField.setAccessible(true);
    27. itemIField = Item.class.getDeclaredField("i");
    28. itemIField.setAccessible(true);
    29. itemJField = Item.class.getDeclaredField("j");
    30. itemJField.setAccessible(true);
    31. itemDField = Item.class.getDeclaredField("d");
    32. itemDField.setAccessible(true);
    33. craftingResultField = Item.class.getDeclaredField("craftingResult");
    34. craftingResultField.setAccessible(true);
    35. } catch (NoSuchFieldException ex) {
    36. ex.printStackTrace();
    37. }
    38. }
    39. public static void copyItem(Item original, Item newItem) {
    40. try {
    41. newItem.e(original.getMaxStackSize());
    42. newItem.a(creativeModeTabField.get(original));
    43. newItem.c(nameField.get(original));
    44. altNameField.set(newItem, altNameField.get(original));
    45. craftingResultField.set(newItem, craftingResultField.get(original));
    46. itemIField.set(newItem, itemIField.get(original));
    47. itemDField.set(newItem, itemDField.get(original));
    48. itemJField.set(newItem, itemJField.get(original));
    49. durabilityField.set(newItem, original.getMaxDurability());
    50. } catch (IllegalAccessException ex) {
    51. ex.printStackTrace();
    52. }
    53. }
    54. }
    55.  

    I've been working on a plugin that wraps this all up nicely and allows any attributes to be added to any items via the config file. It's still very new and subject to change dramatically. I also suspect MUCH of the code there can be eliminated with some more clever reflection. (I wonder if Comphenix would have any interest in this? or tips? :))
     
    TryB4, Assist, Chinwe and 1 other person like this.
Thread Status:
Not open for further replies.

Share This Page