NMS How to override default Minecraft mobs

Discussion in 'Resources' started by TeeePeee, Jan 6, 2014.

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

    TeeePeee

    Hey guys,

    Since Jacek hasn't been active recently and his excellent tutorial - How to Customize the Behaviour of a Mob or Entity isn't updated for 1.7.2, I figured I'd make this post.

    If you know nothing about NMS, start here. Otherwise, skip the following paragraph right ahead to the first code snippet.

    NMS (net.minecraft.server) is the nitty gritty of making plugins even more awesome. With it, you can do things that Bukkit doesn't support. Of course, to use this, you need to use the Craftbukkit jar instead of the Bukkit jar. If you don't know how to do that, search here on the forums as it's different for Maven, Eclipse, etc. The two nasty parts about NMS are:
    1) There aren't as many nicely named methods and there are absolutely no commented methods. You need to figure out what each field and method does yourself.
    2) All the fields and methods you figured out are subject to change with each update. In fact, the names rarely stay the same as they are obfuscated (named randomly) with each compilation.

    ----

    Before we get into how to use NMS methods, some code.

    Code:java
    1. package p;
    2.  
    3. import java.lang.reflect.Field;
    4. import java.util.List;
    5. import java.util.Map;
    6.  
    7. import net.minecraft.server.v1_7_R1.BiomeBase;
    8. import net.minecraft.server.v1_7_R1.BiomeMeta;
    9. import net.minecraft.server.v1_7_R1.EntityInsentient;
    10. import net.minecraft.server.v1_7_R1.EntityTypes;
    11. import net.minecraft.server.v1_7_R1.EntityZombie;
    12.  
    13. import org.bukkit.entity.EntityType;
    14.  
    15. public enum CustomEntityType {
    16.  
    17. ZOMBIE("Zombie", 54, EntityType.ZOMBIE, EntityZombie.class, CustomEntityZombie.class);
    18.  
    19. private String name;
    20. private int id;
    21. private EntityType entityType;
    22. private Class<? extends EntityInsentient> nmsClass;
    23. private Class<? extends EntityInsentient> customClass;
    24.  
    25. private CustomEntityType(String name, int id, EntityType entityType, Class<? extends EntityInsentient> nmsClass,
    26. Class<? extends EntityInsentient> customClass) {
    27. this.name = name;
    28. this.id = id;
    29. this.entityType = entityType;
    30. this.nmsClass = nmsClass;
    31. this.customClass = customClass;
    32. }
    33.  
    34. public String getName() {
    35. return name;
    36. }
    37.  
    38. public int getID() {
    39. return id;
    40. }
    41.  
    42. public EntityType getEntityType() {
    43. return entityType;
    44. }
    45.  
    46. public Class<? extends EntityInsentient> getNMSClass() {
    47. return nmsClass;
    48. }
    49.  
    50. public Class<? extends EntityInsentient> getCustomClass() {
    51. return customClass;
    52. }
    53.  
    54. /**
    55. * Register our entities.
    56. */
    57. public static void registerEntities() {
    58. for (CustomEntityType entity : values())
    59. a(entity.getCustomClass(), entity.getName(), entity.getID());
    60.  
    61. // BiomeBase#biomes became private.
    62. BiomeBase[] biomes;
    63. try {
    64. biomes = (BiomeBase[]) getPrivateStatic(BiomeBase.class, "biomes");
    65. } catch (Exception exc) {
    66. // Unable to fetch.
    67. return;
    68. }
    69. for (BiomeBase biomeBase : biomes) {
    70. if (biomeBase == null)
    71. break;
    72.  
    73. // This changed names from J, K, L and M.
    74. for (String field : new String[] { "as", "at", "au", "av" })
    75. try {
    76. Field list = BiomeBase.class.getDeclaredField(field);
    77. list.setAccessible(true);
    78. @SuppressWarnings("unchecked")
    79. List<BiomeMeta> mobList = (List<BiomeMeta>) list.get(biomeBase);
    80.  
    81. // Write in our custom class.
    82. for (BiomeMeta meta : mobList)
    83. for (CustomEntityType entity : values())
    84. if (entity.getNMSClass().equals(meta.b))
    85. meta.b = entity.getCustomClass();
    86. } catch (Exception e) {
    87. e.printStackTrace();
    88. }
    89. }
    90. }
    91.  
    92. /**
    93. * Unregister our entities to prevent memory leaks. Call on disable.
    94. */
    95. public static void unregisterEntities() {
    96. for (CustomEntityType entity : values()) {
    97. // Remove our class references.
    98. try {
    99. ((Map) getPrivateStatic(EntityTypes.class, "d")).remove(entity.getCustomClass());
    100. } catch (Exception e) {
    101. e.printStackTrace();
    102. }
    103.  
    104. try {
    105. ((Map) getPrivateStatic(EntityTypes.class, "f")).remove(entity.getCustomClass());
    106. } catch (Exception e) {
    107. e.printStackTrace();
    108. }
    109. }
    110.  
    111. for (CustomEntityType entity : values())
    112. try {
    113. // Unregister each entity by writing the NMS back in place of the custom class.
    114. a(entity.getNMSClass(), entity.getName(), entity.getID());
    115. } catch (Exception e) {
    116. e.printStackTrace();
    117. }
    118.  
    119. // Biomes#biomes was made private so use reflection to get it.
    120. BiomeBase[] biomes;
    121. try {
    122. biomes = (BiomeBase[]) getPrivateStatic(BiomeBase.class, "biomes");
    123. } catch (Exception exc) {
    124. // Unable to fetch.
    125. return;
    126. }
    127. for (BiomeBase biomeBase : biomes) {
    128. if (biomeBase == null)
    129. break;
    130.  
    131. // The list fields changed names but update the meta regardless.
    132. for (String field : new String[] { "as", "at", "au", "av" })
    133. try {
    134. Field list = BiomeBase.class.getDeclaredField(field);
    135. list.setAccessible(true);
    136. @SuppressWarnings("unchecked")
    137. List<BiomeMeta> mobList = (List<BiomeMeta>) list.get(biomeBase);
    138.  
    139. // Make sure the NMS class is written back over our custom class.
    140. for (BiomeMeta meta : mobList)
    141. for (CustomEntityType entity : values())
    142. if (entity.getCustomClass().equals(meta.b))
    143. meta.b = entity.getNMSClass();
    144. } catch (Exception e) {
    145. e.printStackTrace();
    146. }
    147. }
    148. }
    149.  
    150. /**
    151. * A convenience method.
    152. * @param clazz The class.
    153. * @param f The string representation of the private static field.
    154. * @return The object found
    155. * @throws Exception if unable to get the object.
    156. */
    157. private static Object getPrivateStatic(Class clazz, String f) throws Exception {
    158. Field field = clazz.getDeclaredField(f);
    159. field.setAccessible(true);
    160. return field.get(null);
    161. }
    162.  
    163. /*
    164. * Since 1.7.2 added a check in their entity registration, simply bypass it and write to the maps ourself.
    165. */
    166. private static void a(Class paramClass, String paramString, int paramInt) {
    167. try {
    168. ((Map) getPrivateStatic(EntityTypes.class, "c")).put(paramString, paramClass);
    169. ((Map) getPrivateStatic(EntityTypes.class, "d")).put(paramClass, paramString);
    170. ((Map) getPrivateStatic(EntityTypes.class, "e")).put(Integer.valueOf(paramInt), paramClass);
    171. ((Map) getPrivateStatic(EntityTypes.class, "f")).put(paramClass, Integer.valueOf(paramInt));
    172. ((Map) getPrivateStatic(EntityTypes.class, "g")).put(paramString, Integer.valueOf(paramInt));
    173. } catch (Exception exc) {
    174. // Unable to register the new class.
    175. }
    176. }
    177. }
    (Adaptation of Jacek code)

    This class provides an easy and safe way to replace Minecraft mobs with your own. Copy it and paste it into your plugin. You'll need it.

    ----

    Let's analyze it now and find out how to create your own custom mobs.

    Code:java
    1. ZOMBIE("Zombie", 54, EntityType.ZOMBIE, EntityZombie.class, CustomEntityZombie.class);


    This is the part that you will be changing. Notice the declaration. You need to provide the custom entity with a String, an integer, an EntityType, and 2 classes. Most of these parameters can be found here.

    "Zombie": This is the name of the mob in Minecraft. Refer to the link above to find the right name.
    54: This is the entity ID given by Minecraft. It also can be found in the link above directly after the name.
    EntityType.ZOMBIE: This is the Bukkit EntityType that you will be replacing. In this situation, we want to replace the zombie, so we provided it with the zombie type.
    EntityZombie.class: Again, this parameter is found in the link above. It is the Minecraft class where the code for the entity resides.
    CustomEntityZombie.class: This is a class you create yourself. We'll go through an example of this in a moment but know that it must extend the original class (provided as the previous argument).

    If you also wanted to replace pigs, for example, here's what your code would look like.

    Code:java
    1. ZOMBIE("Zombie", 54, EntityType.ZOMBIE, EntityZombie.class, CustomEntityZombie.class), PIGGY("Pig", 90, EntityType.PIG, EntityPig.class, CustomEntityPig.class);


    Notice the enum name given does not matter.

    ----

    Now that we have our custom entity overrides in place, let's make sure we register them. Head into your main class and in your onEnable, add the following:
    Code:java
    1. CustomEntityType.registerEntities();


    And perhaps more importantly, this in your onDisable:
    Code:java
    1. CustomEntityType.unregisterEntities();


    Many tutorials miss out this crucial bit of code. Without it, you may as well be stuffing pie into the disk drive of your server. The unregister method ensures that once Bukkit unloads your plugin, all references to your plugin are severed so that no memory leaks occur. You'll also want to despawn all the custom entities and replace them with normal entities or at least just despawn them. Simply iterate through each entity in each world and if it's one that you have overridden, despawn it.

    ----

    Okay, the easy part is done. But now is the fun part - writing your own entities. As I said earlier, you will need to use NMS for this. Don't let the scary bold letters frighten you though. It's not nearly as complicated as you think.

    Since we're replacing the zombie with our custom zombie in the example, let's demonstrate that now.

    First you'll need to create the class and ensure it extends properly. Once you've added the necessary imports, your IDE will prompt you to create the necessary constructor. Do this and your class should look something like the following.

    Code:java
    1. package p;
    2.  
    3. import net.minecraft.server.v1_7_R1.EntityZombie;
    4. import net.minecraft.server.v1_7_R1.World;
    5.  
    6. import org.bukkit.entity.EntityType;
    7.  
    8. public class CustomEntityZombie extends EntityZombie {
    9.  
    10. public CustomEntityZombie(World world) {
    11. super(world);
    12. }
    13. }
    14.  


    At this point, your code will compile but your custom class is exactly the same as the normal zombie. You'll need to delve into NMS to change that. Let's look at the source code for the normal zombie. By the way, you can find all the source code of net.minecraft.server on the Github page linked directly above.

    Looking at the very first method after the constructor, we see a method called aD.

    Code:java
    1. protected void aD() {
    2. super.aD();
    3. this.getAttributeInstance(GenericAttributes.b).setValue(40.0D);
    4. this.getAttributeInstance(GenericAttributes.d).setValue(0.23000000417232513D);
    5. this.getAttributeInstance(GenericAttributes.e).setValue(3.0D);
    6. this.bc().b(bp).setValue(this.random.nextDouble() * 0.10000000149011612D);
    7. }


    GenericAttributes? What? Don't worry - just go to the source.

    Ah, now we can see that GenericAttributes has the fields a, b, c, d and e and what each one does by reading the text in the IAttribute constructor. Since we want our zombie to be a badass, let's crank up the attack damage - GenericAttribute e.

    To do this, just override the method from the default class.

    Code:java
    1. @Override
    2. protected void aD() {
    3. super.aD();
    4. this.getAttributeInstance(GenericAttributes.e).setValue(300.0D); // Original 3.0D
    5. }


    Now our custom zombie is 100x stronger than default. Not bad, but we don't want our zombie attacking villagers. However, we do want it to attack skeleton.

    ----

    For this, we need to use Pathfinders. Now, we could make our own pathfinders by creating a class that extends a PathfinderGoal, but since there's already premade Pathfinders for attacking targets, lets just use those.

    First and foremost, let's see what Pathfinders the zombie already has. These are almost always exclusively in the constructor.

    Code:java
    1. this.goalSelector.a(0, new PathfinderGoalFloat(this));
    2. this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, EntityHuman.class, 1.0D, false));
    3. this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, EntityVillager.class, 1.0D, true));
    4. this.goalSelector.a(5, new PathfinderGoalMoveTowardsRestriction(this, 1.0D));
    5. this.goalSelector.a(6, new PathfinderGoalMoveThroughVillage(this, 1.0D, false));
    6. this.goalSelector.a(7, new PathfinderGoalRandomStroll(this, 1.0D));
    7. this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F));
    8. this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this));
    9. this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true));
    10. this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, 0, true));
    11. this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityVillager.class, 0, false));


    Okay, now we know that the Pathfinder that allows our zombie to attack is the PathfinderGoalMeleeAttack and the one that makes it find a target to attack is the PathfinderGoalNearestAttackableTarget simply by analyzing the names. We can also see that the entity class we want to target is passed along with it.

    Since we don't want our zombie to attack villagers, we have to remove Pathfinders that have already been applied. Let's just clear all of the Pathfinders and re-apply the ones we want later. For this, we need to use Reflection. Add this code into the constructor of your custom zombie.

    Code:java
    1. try {
    2. Field bField = PathfinderGoalSelector.class.getDeclaredField("b");
    3. bField.setAccessible(true);
    4. Field cField = PathfinderGoalSelector.class.getDeclaredField("c");
    5. cField.setAccessible(true);
    6. bField.set(goalSelector, new UnsafeList<PathfinderGoalSelector>());
    7. bField.set(targetSelector, new UnsafeList<PathfinderGoalSelector>());
    8. cField.set(goalSelector, new UnsafeList<PathfinderGoalSelector>());
    9. cField.set(targetSelector, new UnsafeList<PathfinderGoalSelector>());
    10. } catch (Exception exc) {
    11. exc.printStackTrace();
    12. }


    Now our zombie will have no Pathfinders and will instead just stand still. Let's reapply the normal Pathfinders but replace villager attacking with skeleton.

    Code:java
    1. this.goalSelector.a(0, new PathfinderGoalFloat(this));
    2. this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, EntityHuman.class, 1.0D, false));
    3. this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, EntitySkeleton.class, 1.0D, true));
    4. this.goalSelector.a(5, new PathfinderGoalMoveTowardsRestriction(this, 1.0D));
    5. this.goalSelector.a(6, new PathfinderGoalMoveThroughVillage(this, 1.0D, false));
    6. this.goalSelector.a(7, new PathfinderGoalRandomStroll(this, 1.0D));
    7. this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F));
    8. this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this));
    9. this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true));
    10. this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, 0, true));
    11. this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntitySkeleton.class, 0, false));


    With these changes, your class should look like so:
    Code:java
    1. package p;
    2.  
    3. import net.minecraft.server.v1_7_R1.EntityZombie;
    4. import net.minecraft.server.v1_7_R1.World;
    5.  
    6. import org.bukkit.entity.EntityType;
    7.  
    8. public class CustomEntityZombie extends EntityZombie {
    9.  
    10. public CustomEntityZombie(World world) {
    11. super(world);
    12.  
    13. try {
    14. Field bField = PathfinderGoalSelector.class.getDeclaredField("b");
    15. bField.setAccessible(true);
    16. Field cField = PathfinderGoalSelector.class.getDeclaredField("c");
    17. cField.setAccessible(true);
    18. bField.set(goalSelector, new UnsafeList<PathfinderGoalSelector>());
    19. bField.set(targetSelector, new UnsafeList<PathfinderGoalSelector>());
    20. cField.set(goalSelector, new UnsafeList<PathfinderGoalSelector>());
    21. cField.set(targetSelector, new UnsafeList<PathfinderGoalSelector>());
    22. } catch (Exception exc) {
    23. exc.printStackTrace();
    24. // This means that the name of one of the fields changed names or declaration and will have to be re-examined.
    25. }
    26.  
    27. this.goalSelector.a(0, new PathfinderGoalFloat(this));
    28. this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, EntityHuman.class, 1.0D, false));
    29. this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, EntitySkeleton.class, 1.0D, true));
    30. this.goalSelector.a(5, new PathfinderGoalMoveTowardsRestriction(this, 1.0D));
    31. this.goalSelector.a(6, new PathfinderGoalMoveThroughVillage(this, 1.0D, false));
    32. this.goalSelector.a(7, new PathfinderGoalRandomStroll(this, 1.0D));
    33. this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F));
    34. this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this));
    35. this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true));
    36. this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, 0, true));
    37. this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntitySkeleton.class, 0, false));
    38. }
    39.  
    40. @Override
    41. protected void aD() {
    42. super.aD();
    43. this.getAttributeInstance(GenericAttributes.e).setValue(300.0D); // Original 3.0D
    44. }
    45. }
    46.  


    Great! Now we have a zombie that is 100x stronger than default, targets skeletons instead of villagers and spawns naturally into the world!

    ----

    Hopefully now you have a better idea of what NMS is and how to use it to your advantage in order to spawn in custom entities. You should also now have an idea of how to change the speed, follow distance, etc. of any entity.

    If you have any questions, feel free to ask below! Otherwise, have fun changing up those entities!

    In case you get an error when registering an attribute, use this piece of code:

    Code:java
    1. this.bc().b(GenericAttributes.<ATTRIBUTE>);


    This will unregister the attribute first as the error is caused when you try to re-register an attribute.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jan 25, 2016
  2. Offline

    thepaperboy99

    Great post! Thanks for this. :)
     
    GrandmaJam likes this.
  3. Offline

    TeeePeee

    thepaperboy99
    No problem at all. I'm glad the sheer length didn't scare you off :)
     
  4. Offline

    Jalau

    TeeePeee So the part with the reflection Goal Selectors, if i don't use that what will it change? I have the bug that when chunks unload the mobs in it will return to normal, can this be caused by that? I don't overwrite alle Zombies, I just spawn the manually ;) Also can you add a part for creating a pet that will follow his owner? ;)
    Thanks in advantage ;)
     
  5. Offline

    Garris0n

    in advance*

    Anyway, just write a new pathfinder goal that follows a player, it's not too difficult, you can copy most of the code from another one. Then just add that pathfinder goal to the mob you're using.
     
    GrandmaJam and Jalau like this.
  6. Offline

    Jalau


    Garris0n
    Thanks for the correction, still learning english (9th Class (German)) ;) Will give it a try ;)
     
    Garris0n likes this.
  7. Offline

    TeeePeee

  8. Offline

    marwzoor

    TeeePeee
    I have been struggling with removing spjders default behaviour. If you look at the class for EntitySpider pathfinders aren't specified in the constructor :( you've got any clue how to do it?
     
  9. Offline

    Minnymin3

    Ivan likes this.
  10. Offline

    TeeePeee

    The trick with NMS is to follow the logical flow of events - so let's analyze the Spider class.

    First, we'll notice the method called a with the parameters for an Entity and a float. It checks a distance and a random that either resolves and clears the entities target or resolves and makes the Spider jump. This sounds a lot like the behaviour of the Spider so we can assume that this 'a' method is responsible for making the Spider jump at it's target.

    Next, we'll notice the inconspicuous findTarget method. Again we see the same call to this.d(1.0F) and we know that Spiders only find targets during the day. In the 'a' method, we noticed that the Spider forgets its target when this.d(1.0F) > 0.5 so we can safely assume that the this double is greater than 0.5 during the day and less that 0.5 during the night. With this knowledge, we can look at the findTarget method and realize that the Spider will find a target within 16 blocks during the night.

    Finally, the other a method with a GroupDataEntity parameter is the only one dealing with skeletons and we can immediately assume that this is what controls the creation of Spider Jockeys.
    ----
    In order to prevent the default Spider behaviour, you can override these methods and leave them empty. Since EntitySpider indirectly extends EntityInsentient, it is safe to use the method outlined in the above tutorial to apply Pathfinders as well.
     
    Plo124, marwzoor and Garris0n like this.
  11. Offline

    Ronbo

    I've been trying to make chickens. I found that by making a custom chicken class extend EntityMonster the chickens will begin dealing damage, and aggroing players if you change their targetSelector accordingly.

    However, they lose their default EntityChicken movement and move really strangely instead - just steadily nudging forward without any walking animations. Could you help me out with this? I've looked but couldn't find the relative code.
     
  12. Offline

    Garris0n

    Ronbo
    Out of curiosity, can you post the code you used?
     
  13. Offline

    TeeePeee

    Ronbo
    Simply apply a PathFinderGoalNearestAttackableTarget like so:
    (From the EntityZombie class) so that it aggros on humans. Then add the PathfinderGoalMeleeAttack to have it attack.
     
  14. Offline

    marwzoor

    Thank you!

    It still moves around randomly though :( Any idea?
     
  15. Offline

    Garris0n

    marwzoor
    Did you use the code in the OP to remove any pathfinder goals it has first? Otherwise it probably still has some such as RandomStroll.
     
  16. Offline

    TeeePeee

    Garris0n
    The EntitySpider isn't initialized with any default Pathfinders.
    marwzoor
    With respect to what I just tahgged Garris0n in, I would recommend trying to clear the pathfinders. I can't see any other reason why it would stroll unless the targetSelector is initialized with a default random stroll goal.
     
  17. Offline

    marwzoor

    TeeePeee
    I have already cleared all PathfinderGoals so thats not the issue.
     
  18. Offline

    bennie3211

    After a long time google-ing i finally found an awesome good tutorial about this :D Thnx!
     
  19. Offline

    Garris0n

  20. Offline

    Broc923

  21. Offline

    cummo15

    TeeePeee I can't seem to get my mob to target EntityItem?
     
  22. Offline

    bennie3211

    1 Question, is it possible when you created a CustomZombie entity, to cancel his behaviour? So if i set the attack range to 60 blocks, and you hold a dirt in your hand it will cancel the attack movement / decrease attack range for player? Or is this not possible then?
     
  23. Offline

    TeeePeee

    Broc923
    Before you set the value of your attribute, try using bc().b(YOUE ATTRIBUTE) to unregister the attribute before setting it. Minecraft will throw an error in some cases.
    cummo15
    You'll have to create your own pathfinder goal and change the parameter of EntityCreature to Entity or EntityItem to allow the pathfinder to accept these as valid targets.
    bennie3211
    You have to create your own pathfinder goal and perform checks for what a player is holding, etc. before continuing to pathfind. You can see an example from my plugin, MyZ, https://github.com/JordanSicherman/MyZ-3.0/blob/master/src/myz/mobs/pathing/PathingSupport.java#L74 which is added to a pathfinder https://github.com/JordanSicherman/...derGoalNearestAttackableZombieTarget.java#L90.
     
    bennie3211 likes this.
  24. Offline

    sebasju1234

    Hi, thanks for this tutorial! But I've got a problem; it doesn't work for me and I did exactly the same as you did.
    This is the error:
    Code:
    [16:25:03 WARN]: java.lang.reflect.InvocationTargetException
    [16:25:03 WARN]:        at sun.reflect.GeneratedConstructorAccessor49.newInstanc
    e(Unknown Source)
    [16:25:03 WARN]:        at sun.reflect.DelegatingConstructorAccessorImpl.newInst
    ance(Unknown Source)
    [16:25:03 WARN]:        at java.lang.reflect.Constructor.newInstance(Unknown Sou
    rce)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.SpawnerCreature.spawnEnt
    ities(SpawnerCreature.java:150)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.WorldServer.doTick(World
    Server.java:187)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.MinecraftServer.u(Minecr
    aftServer.java:629)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.DedicatedServer.u(Dedica
    tedServer.java:250)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.MinecraftServer.t(Minecr
    aftServer.java:545)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.MinecraftServer.run(Mine
    craftServer.java:457)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.ThreadServerApplication.
    run(SourceFile:617)
    [16:25:03 WARN]: Caused by: java.lang.IllegalArgumentException: Attribute is alr
    eady registered!
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.AttributeMapServer.b(Sou
    rceFile:28)
    [16:25:03 WARN]:        at com.titaniumpvp.amethyst.mobs.custom.CustomEntityZomb
    ie.aD(CustomEntityZombie.java:61)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.EntityLiving.<init>(Enti
    tyLiving.java:84)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.EntityInsentient.<init>(
    EntityInsentient.java:38)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.EntityCreature.<init>(En
    tityCreature.java:25)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.EntityMonster.<init>(Ent
    ityMonster.java:8)
    [16:25:03 WARN]:        at net.minecraft.server.v1_7_R1.EntityZombie.<init>(Enti
    tyZombie.java:25)
    [16:25:03 WARN]:        at com.titaniumpvp.amethyst.mobs.custom.CustomEntityZomb
    ie.<init>(CustomEntityZombie.java:26)
    [16:25:03 WARN]:        ... 10 more
    Line 26 is: super(world);
     
  25. Offline

    TeeePeee

    sebasju1234
    The real error is on line 61 of CustomEntityZombie when you register an attribute. Try using bc().b(YOUE ATTRIBUTE) to unregister the attribute before setting it. Minecraft will throw an error in some cases.
     
  26. Offline

    Broc923

    Would that be something like this:
    this.bc().b((IAttribute) a);
    this.getAttributeInstance(GenericAttributes.a).setValue(48.0D); //original 16
     
  27. Offline

    TeeePeee

    Broc923
    this.bc().b(GenericAttributes.a);
     
    Broc923 likes this.
  28. Offline

    bennie3211

  29. Offline

    marwzoor

    TeeePeee
    I managed to remove all behaviors from the spider, but it doesn't go through the list of PathfinderGoals like the other custom mobs I have created. Do you have any idea why?

    Code:java
    1.  
    2. public class MythEntityCompanionSpider extends EntitySpider implements MythEntity
    3. {
    4. @SuppressWarnings("rawtypes")
    5. public MythEntityCompanionSpider(World w, Player owner)
    6. {
    7. super(w);
    8. try
    9. {
    10. Field f = net.minecraft.server.v1_6_R3.PathfinderGoalSelector.class.getDeclaredField("a");
    11. f.setAccessible(true);
    12.  
    13. //Remove all current goals
    14. ((UnsafeList)f.get(this.goalSelector)).clear();
    15. ((UnsafeList)f.get(this.targetSelector)).clear();
    16. }
    17. catch(Exception e)
    18. {
    19. e.printStackTrace();
    20. }
    21. this.goalSelector.a(0, new PathfinderGoalLeapAtTarget(this, 0.4F));
    22. this.goalSelector.a(1, new PathfinderGoalMeleeAttack(this, 1.5D, true));
    23. this.goalSelector.a(2, new PathfinderGoalFollowOwner(this, owner, 3));
    24. this.targetSelector.a(0, new PathfinderGoalTargetSelected(this));
    25. this.targetSelector.a(1, new PathfinderGoalOwnerTarget(this, owner));
    26. this.getNavigation().a(false);
    27. }
    28.  
    29. public MythEntityCompanionSpider(World w)
    30. {
    31. super(w);
    32. }
    33.  
    34. @Override
    35. protected void a(Entity e, float f) {}
    36.  
    37. @Override
    38. public GroupDataEntity a(GroupDataEntity groupDataEntity)
    39. {
    40. return null;
    41. }
    42.  
    43. @Override
    44. protected void bK(){}
    45.  
    46. @Override
    47. public Entity findTarget() { return null; }
    48.  
    49. @Override
    50. protected void az()
    51. {
    52. super.az();
    53. this.getAttributeInstance(GenericAttributes.b).setValue(70D);
    54. }
    55.  
    56. @Override
    57. public boolean moveTo(Location loc)
    58. {
    59. return false;
    60. }
    61. }
    62.  


    As I said before, the class is 1.6.4 so the methods are correctly ^^
     
  30. Offline

    bennie3211

    Do you run a server with craftbukkit 1.6.4 R3? Else it could be changed.

    Edit:

    Own question, isn't it causing lagg when you loop through all online players to get a nearby player? I dunno how often you call that method to let zombies choose there path again?
     
Thread Status:
Not open for further replies.

Share This Page