Tutorial - Setting Entity Attributes

Discussion in 'Resources' started by Jacek, Jul 6, 2013.

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

    Jacek

    1.6.1 added a new system of attributes to most entities, this allows us to set modifiers for various properties of their behaviour, the most interesting one being movement speed.

    I'll assume a small amount of knowledge of the net.minecraft.server part of CraftBukkit for this tutorial, you may want to look at my other tutorial on the topic.

    Each entity has a set of default attributes, what each of these do can be worked out from the top of GenericAttributes.java, each of these can have a modifier applied.

    As an example lets create a listener that speeds up all zombies
    Code:
        @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
        public void onEntitySpawn(CreatureSpawnEvent event){
            LivingEntity entity = event.getEntity();
     
            if (entity.getType() == EntityType.ZOMBIE){
         
            }
        }
    simple so far, were just selecting all the zombies that managed to spawn. Now we need to get hold of the Minecraft entity instead of the Bukkit one
    Code:
        @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
        public void onEntitySpawn(CreatureSpawnEvent event){
            LivingEntity entity = event.getEntity();
     
            if (entity.getType() == EntityType.ZOMBIE){
                EntityInsentient nmsEntity = (EntityInsentient) ((CraftLivingEntity) entity).getHandle();
                AttributeInstance attributes = nmsEntity.a(GenericAttributes.d);
            }
        }
    We're also getting an entity attribute here. nmsEntity.a(GenericAttributes.d) returns an object which contains all of the modifiers that are applied for the attribute that is defined by the d field, looking at the source we see that this is movement speed.

    Next we need to create a modifier for this attribute that we can then apply, we need a UUID to identify it so that it can be removed later it we want. I would define this as a static field in the class.
    Code:
        private static final UUID movementSpeedUID = UUID.fromString("206a89dc-ae78-4c4d-b42c-3b31db3f5a7c");
    The string here is a random one, you should generate a unique ID for this specific modifier. For example there is one UUID for speed potions and one for baby mobs.
    The modifier can then be created
    Code:
    AttributeModifier modifier = new AttributeModifier(movementSpeedUID, "<plugin_name> movement speed multiplier", 1.1d, 1);
    The string parameter is not used anywhere that I can see but we may as well use a sensible name. The third parameter is the modifier value and the 3rd is the type of modifier, this can be 0 for additive or 1 for multiplicative.

    The next step is simply to add the modifier to the attribute
    Code:
                attributes.a(modifier);
    AttributeInstance has two important methods, a() adds a modifier and b() removes it. Adding a modifier that already exists (has the same UUID) causes an error so it makes sense to remove it before hand. The final thing becomes
    Code:
        private static final UUID movementSpeedUID = UUID.fromString("206a89dc-ae78-4c4d-b42c-3b31db3f5a7c");
     
        @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
        public void onEntitySpawn(CreatureSpawnEvent event){
            LivingEntity entity = event.getEntity();
     
            if (entity.getType() == EntityType.ZOMBIE){
                EntityInsentient nmsEntity = (EntityInsentient) ((CraftLivingEntity) entity).getHandle();
                AttributeInstance attributes = nmsEntity.getAttributeInstance(GenericAttributes.d);
         
                AttributeModifier modifier = new AttributeModifier(movementSpeedUID, "<plugin_name> movement speed multiplier", 1.1d, 1);
         
                attributes.b(modifier);
                attributes.a(modifier);
            }
        }
    As a final point, it might be a good idea to wrap this up into a method or class depending on what you are doing. I created my own LivingEntity class. https://github.com/betterphp/BloodM...t/bloodmoon/entity/BloodMoonEntityLiving.java
     
    FuZioN720, Rprrr, Axe2760 and 9 others like this.
  2. Offline

    chasechocolate

    Thanks! This will come probably come in handy some time soon :) assuming Bukkit doesn't add an API for it.
     
  3. Offline

    LazyLemons

    I was literally just looking into this, thanks for the help.
     
  4. Offline

    Eteocles

    Thanks, Jacek. It works like a charm.
     
  5. Offline

    SoloemAA

    Is one coming out for tools any time soon? I'd love to see that one too. Thanks.
     
  6. Offline

    mollekake

    Nice! Will probably be an API for it. It's a pretty big deal for this update, imo :)
     
  7. Offline

    hawkfalcon

    So make it 0d to freeze the mob?
     
  8. Offline

    Jacek

    Don't see a reason why that would not work.
     
    hawkfalcon likes this.
  9. Offline

    Jacek

    EntityLiving.a(Attribute) is now EntityLiving.getAttributeInstance(Attribute) <3

    OP updated.
     
    hawkfalcon likes this.
  10. Offline

    ThaH3lper

    This makes my zombies go bananaz running like crazy :S, doing 0d as multipliers and they are still running very fast... any idea?
     
  11. Offline

    Jacek

    Use a lower multiplier :p
     
  12. Offline

    ThaH3lper

    Is there anything lower then 0?
    Code:java
    1. AttributeModifier modifier = new AttributeModifier(movementSpeedUID, "TestPlugin movement speed multiplier", 0d, 1);
     
  13. Offline

    Jacek

    It's a multiplier, so 0.5 should make it slower. Setting 0 might cause a math error somewhere :/
     
  14. Offline

    ThaH3lper

    well... if it's a multiplier 1.0 should be the same speed... but they are going bananaz :S I'm probably doing something wrong... Has anyone made a class for it, like Mobs.setSpeed(LivingeEntity, Multiplier); would be great!
     
  15. Offline

    Jacek

    You might be setting an additive attribute instead of a multiplier, check the ID in the constructor. 0 is addition 1 is multiplication.
     
  16. Offline

    Ewe Loon

    thanks, for the code at the top i was able to read and set horses speed with these 2 methods


    Code:
        public Double getHorseSpeed(Horse h){   
            AttributeInstance attributes = ((EntityInsentient)((CraftLivingEntity)h).getHandle()).getAttributeInstance(GenericAttributes.d);
            return attributes.getValue();
        }
       
        public void setHorseSpeed(Horse h,double speed){   
            // use about  2.25 for normalish speed  
            AttributeInstance attributes = ((EntityInsentient)((CraftLivingEntity)h).getHandle()).getAttributeInstance(GenericAttributes.d);
            attributes.setValue(speed);
        }
       
    
     
  17. Offline

    Gorro_Rojo

    I'll just wait for bukkit to make and API XD
     
  18. Offline

    Bancey

    Jacek Very nice tutorial, easy to follow. Thanks :)
     
    Jacek likes this.
  19. Offline

    Jake0oo0

    Does this only work with mobs..? I'd think it would work with players right?
     
  20. Offline

    TomFromCollege

    Quick question, is the max health and attack damage a multiplier or a modifier?

    If you don't know, that's fine I'll do some testing :)
     
  21. Offline

    wafwot

    Jacek This tutorial, along with your previous one on the same subject, are great resources and have helped a lot. I can not thank you enough for taking the time to clearly explain how something works.

    Only caveat is that best practices say you should use UUID.randomUUID() or something similar rather than making one up that could (I know the odds are astronomical) be duplicated.

    Again, thanks for a thought-out and concisely written tutorial.

    And to those waiting for the bukkit api, I hope you have a lifetime supply of patience.
     
  22. Offline

    Jacek

    Jake0oo0 Only entities that have AI as far as I know. Anything that extends EntityInsentient

    wafwot Thanks :) Nice to know the time I put in was worth it. On the UUID thing, it needs to stay the same across restarts so that you don't end up with attributes that cannot be removed, I see your point though, hopefully people don't just copy the one from my post :(
     
  23. Offline

    ainast

    I'm having a few issues setting a mobs attackDamage. Would you mind taking a peak? When I use the speed UUID and GenericAttributes.d it makes the mobs move faster. But a-c don't work for anything.

    Any Advice?

    Code Sample:

    privatestaticfinal UUID attackDamageUID = UUID.fromString("7bbe3bb1-079d-4150-ac6f-669e71550776");

    EntityInsentient nmsEntity = (EntityInsentient) ((CraftLivingEntity) entity).getHandle();
    AttributeInstance attributes = nmsEntity.getAttributeInstance(GenericAttributes.a);
    AttributeModifier modifier = new AttributeModifier(attackDamageUID, "MPM attack strength", 5.0d, 1);

    attributes.b(modifier);
    attributes.a(modifier);
     
  24. Offline

    cr0ss

    Hey there,

    Is this still relevant? I am looking to accomplish something of this sort, but am very new with NMS source. Thought I'd ask (while still attempting to dive in) and see if I could fish up any new info if there have been developments.

    Thanks!

    edit: Looks like this is still very relevant, though with a small tweak in some syntax changes since September. Thanks for the guide!
     
  25. Offline

    iKeirNez

    Just as a note to you guys, as of Minecraft 1.7.2 this change is required for the above code to work

    Replace:
    Code:java
    1. AttributeInstance attributes = nmsEntity.a(GenericAttributes.d);


    With:
    Code:java
    1. AttributeInstance attributes = nmsEntity.getAttributeInstance(GenericAttributes.d);


    Apart from that everything else appears to be the same, also to help some others out, here are the field names of each generic attribute

    Max Health = GenericAttributes.a
    Follow Range = GenericAttributes.b
    Knockback Resistance = GenericAttributes.c
    Movement Speed = GenericAttributes.d
    Attack Damage = GenericAttributes.e

    Depending on which attribute you want to apply, this line will have to be changed
    Code:java
    1. AttributeInstance attributes = nmsEntity.getAttributeInstance(GenericAttributes.d);


    There are also mob specific attributes but I haven't looked into them.
     
    bobacadodl likes this.
  26. Offline

    CrystalxNeth

    Jacek Is there any way to make this work with an ender dragon? The dragon is different from all other mobs based on that it doesn't use path finders (All of the movement code is inside of one method called e()), and also it is composed of several parts which are all their own entities.
     
  27. Offline

    TheMintyMate

    So did we find a way to stop mobs moving using this?

    Just did some messing around:
    Code:
     AttributeModifier modifier = new AttributeModifier(movementSpeedUID, "<plugin_name> movement speed multiplier", -100D, 1);
    
    This seems to work

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 3, 2016
Thread Status:
Not open for further replies.

Share This Page