[Tutorial] [1.7] Creating a Custom Entity

Discussion in 'Resources' started by Jogy34, Dec 29, 2013.

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

    Jogy34

    Although Jacek has created a great tutorial for this already (here), it's gotten really out of date and has over 11 pages of comments which searching through becomes a nightmare. He also hasn't been active since September 2013.

    I will be showing you how to create custom entities based off of pre-existing ones. The techniques shown in this may not be the best but they do work and don't cause any damage to servers as far as I am aware.

    You will need to use net.minecraft.server (NMS) and org.bukkit.craftbukkit (OBC or Craftbukkit) so you will need to use the Craftbukit.jar which is the server jar. You can find the code for these, which you will probably need to look at, on the Bukkit github page specifically in Craftbukkit repo, which contains all of the modified and added server classes, and the mc-dev repo, which contains all of base server files some of which aren't changed in Craftbukkit so will not be found in the Craftbukkit repo.

    I will be making a custom zombie entity.

    To start off with, you'll need your basic custom entity class extending whatever entity you wish.
    Code:java
    1.  
    2. public class CustomZombie extends EntityZombie
    3. {
    4. public CustomZombie(/*net.minecraft.server.v1_7_R1.*/ World world)
    5. {
    6. super(world);
    7. }
    8. }
    9.  

    That all looks fairly simple and it is for the most part. It is important to have that exact constructor in addition to any others that you may have because if you don't, the server will throw errors when it attempts to recreate your custom entity. The server always tries to create entities by using reflection and creating the entity from a constructor that only contains the NMS World object as its argument.

    Now, you need to tell the server about your entity by registering it. This involves using reflection to obtain static Map Fields from the EntityTypes class.
    Code:java
    1.  
    2. protected static Field mapStringToClassField, mapClassToStringField, mapClassToIdField, mapStringToIdField;
    3. //protected static Field mapIdToClassField;
    4.  
    5. static
    6. {
    7. try
    8. {
    9. mapStringToClassField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("c");
    10. mapClassToStringField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("d");
    11. //mapIdtoClassField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("e");
    12. mapClassToIdField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("f");
    13. mapStringToIdField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("g");
    14.  
    15. mapStringToClassField.setAccessible(true);
    16. mapClassToStringField.setAccessible(true);
    17. //mapIdToClassField.setAccessible(true);
    18. mapClassToIdField.setAccessible(true);
    19. mapStringToIdField.setAccessible(true);
    20. }
    21. catch(Exception e) {e.printStackTrace();}
    22. }
    23.  

    The Field that's commented out will cause spawn eggs to spawn your custom entity in place of the normal one.

    Now to actually use these fields
    Code:java
    1.  
    2. @SuppressWarnings({ "rawtypes", "unchecked" })
    3. protected static void addCustomEntity(Class entityClass, String name, int id)
    4. {
    5. if (mapStringToClassField == null || mapStringToIdField == null || mapClassToStringField == null || mapClassToIdField == null)
    6. {
    7. return;
    8. }
    9. else
    10. {
    11. try
    12. {
    13. Map mapStringToClass = (Map) mapStringToClassField.get(null);
    14. Map mapStringToId = (Map) mapStringToIdField.get(null);
    15. Map mapClasstoString = (Map) mapClassToStringField.get(null);
    16. Map mapClassToId = (Map) mapClassToIdField.get(null);
    17.  
    18. mapStringToClass.put(name, entityClass);
    19. mapStringToId.put(name, Integer.valueOf(id));
    20. mapClasstoString.put(entityClass, name);
    21. mapClassToId.put(entityClass, Integer.valueOf(id));
    22.  
    23. mapStringToClassField.set(null, mapStringToClass);
    24. mapStringToIdField.set(null, mapStringToId);
    25. mapClassToStringField.set(null, mapClasstoString);
    26. mapClassToIdField.set(null, mapClassToId);
    27. }
    28. catch (Exception e)
    29. {
    30. e.printStackTrace();
    31. }
    32. }
    33. }
    34.  

    The if statement is just there as a precaution in case something went wrong.
    The first of the three blocks after that retrieves the static map from the field and stores it into variables.
    The second block adds the parameters into the maps how you would think they should be based on the names.
    The third block sets the static maps to the maps that you had just retrieved. This part is most likely not necessary but I use it just as assurance.

    Parameters:
    - entityClass => This is the class of your custom entity
    - name => This is the name that the server uses to save your entity. It should be something different than existing names
    - id => This is the network id that the server sends to the client to tell the client what the entity looks like. You can make some interesting things happen with this. Not all 'id's will work with all entities. I haven't found any problems using a squid or a bat with any id so far.

    Using the above method, you would just have to add it into your onLoad() method of your main class:
    Code:java
    1.  
    2. public void onLoad()
    3. {
    4. addCustomEntity(CustomZombie.class, "CustomZombie", 51);
    5. //51 is the network id of a sekeleton so if I were to spawn this, it would appear to be a skeleton instead of a zombie
    6. }
    7.  


    Now to actually be able to spawn the custom entity, this requires a few lines of code.
    Code:java
    1.  
    2. public void spawnCustomEntity(Location loc)
    3. {
    4. net.minecraft.server.v1_7_R1.World nmsWorld = ((CraftWorld) loc.getWorld()).getHandle();
    5. CustomZombie customZombie = new CustomZombie(nmsWorld);
    6. customZombie.setPosition(loc.getX(), lost.getY(), loc.getZ());
    7. nmsWorld.addEntity(customZombie);
    8. }
    9.  


    There, now you have a custom entity. To customize your entity more you just have to override methods or modify variables. For instance, if I wanted my custom zombie to be fire proof, have a special name, a lot of health and drop a diamond then I would modify my class like so:
    Code:java
    1.  
    2. public class CustomZombie extends EntityZombie
    3. {
    4. public CustomZombie(/*net.minecraft.server.v1_7_R1.*/ World world)
    5. {
    6. super(world);
    7.  
    8. this.getAttributeInstance(GenericAttributes.a).setValue(100); //Max Health
    9. this.setHealth(100);
    10.  
    11. this.fireProof = true;
    12. }
    13.  
    14. @Override
    15. public String getName() //This will change the name in death messages along with other things
    16. {
    17. return "Tough Zombie";
    18. }
    19.  
    20. @Override
    21. public void dropDeathLoot(boolean flag, int i)//'i' is the looting level used and 'flag' is if it was killed by a player
    22. {
    23. this.world.getWorld().dropItem(this.getBukkitEntity().getLocation(), new ItemStack(Material.DIAMOND));
    24. }
    25. }
    26.  

    If you want to do more you just have to search through the NMS code to find what you want to do.

    It is important to note that 'public void h()' is the 'onTick' method. It is what gets called every time a world ticks all of its entities.

    That's pretty much it, have fun.
     
  2. Offline

    et477

    i think you can explain to those who dont know what you are talking about what this complicated thing is.
     
    leet4044 likes this.
  3. Offline

    Jogy34

    I'm not quite sure what you're saying.
     
  4. Offline

    Garris0n

    Judging from this, the flag in dropDeathLoot() is whether the entity was killed by a player. An example use is here, because spiders only drop spider eyes when killed by a player.

    Edit: i is the level of looting on the sword of whoever killed the entity. Look here.
     
  5. Offline

    Jogy34

    Thanks, changed it.
     
    Garris0n likes this.
  6. Offline

    Ad237

    Jogy34 Great Tutorial! I'm glad you explained what each piece of the code does. I hate tutorials that just say copy and paste this and it will work. Your tutorial actually teaches people! Do you know much about PathfinderGoals? There is a tutorial on here but it is a bit outdated. Thanks.
     
  7. Offline

    Jogy34

    Thanks, whenever I show people bits of code I always try to make sure that they actually understand it.

    Anyways, I don't really do much with PathfinderGoals and the only time that I have messed with them it has just been removing, replacing, or adding pre-existing ones to the target and goal selectors, I've barely ever touched on actually creating my own custom ones. Most of the behavioral things that I do with my custom entities I handle through the onTick (currently h()) method.
     
  8. Offline

    Cirno

    It's important to know that you shouldn't touch NMS unless you're really experienced at making plugins and/or suicidal maniac who wishes to die of insanity.

    Just going to add this; if you're still working on 1.6.4:
    h() == l_();

    Other than that, if you're going to be using this and if you're going to have a different constructor taking other arguments besides World, you'll need to kill the entity before shutting down, or else it'll attempt to create the same entity with the constructor that takes world, and either A: promptly crash in your face, or B: spam console with errors/failed to create entity.
     
  9. Offline

    Quaro

    Mobs still dropping equipment, how to disable it?
     
  10. Offline

    Jogy34

    There is an array of floats called dropChances that matches with the equipment array to decide if the equipment is dropped. If you never want the equipment to drop, you want to set all of the values in this array to a negative value. If you just set it to zero, with a high looting sword it's still possible to get the items to drop:
    Code:java
    1.  
    2. //In your constructor
    3. this.dropChances[0] = -10.0F; //Held Item
    4. this.dropChances[1] = -10.0F; //Feet Slot
    5. this.dropChances[2] = -10.0F; //Legs Slot
    6. this.dropChances[3] = -10.0F; //Chest Slot
    7. this.dropChances[4] = -10.0F; //Head Slot
     
    Micius likes this.
  11. Offline

    Garris0n

    Micius
    Or you can override this. For reference, it's overridden here with actual functionality.
     
  12. Offline

    Bloxcraft

    Finally! A tutorial for 1.7! I will DEFINITELY be using this!

    Best explanation, too! Thanks for making this!
     
  13. Offline

    DevRosemberg

    Jogy34 You my friend, have earned a new follower.
     
  14. Offline

    Conlexio

    i've been doing plugins for quite a while now, but i'm new to NMS. Anyway, i am a bit confused on where some methods go.. I'm calling
    Code:java
    1. public void onLoad() {
    2. getLogger().info("Loading Custom Mobs.. ");
    3. CustomZombie.addCustomEntity(CustomZombie.class, "CustomZombie", 51); }

    in the main class that extends JavaPlugin, and then have the reflection fields, addCustomZombie, and the constructor left in CustomZombie.class.

    then in my main i also have spawnCustomZombie(Location loc) and this event registered:
    Code:java
    1. @EventHandler
    2. public void onNameLogin(PlayerJoinEvent ev) {
    3.  
    4. Player name = ev.getPlayer();
    5.  
    6.  
    7. Bukkit.getLogger().info("Attempting to spawn in a custom zombie!");
    8. spawnCustomZombie(name.getLocation());
    9.  
    10. }


    I am new to this, so sorry if it is obvious.. But if anyone could clarify this it would be great :)
    There are no errors in console at all so I can only assume it is either not being called at all or it just does not spit errors.. The event is registered

    Also i've never used onLoad() ever before.. is that the problem?
     
  15. Offline

    tylersyme

    This was awesome! It worked great, but I want to expand a bit further. You can use GenericAttribute for entities, but are there Entity specific attributes that can be changed? For example, the egg laying rate of a chicken
     
  16. Offline

    Jogy34

    onLoad() just gets called when your plugin loads which happens before any worlds or anything like that are loaded (onEnable() happens after that).

    Second, just want to make sure that you realize that 51 is the network id of a skeleton, 54 is for zombie.

    Third, are you sure that you have the listener registered and that onNameLogin() is being called? I'm assuming that pawnCustomZombie() and addCustomEntity() are the same (at a bast level) to the methods I showed above.

    The only other thing that I can think of is that it isn't working because the PlayerJoinEvent is being called before the player actually appears in the game. You could try delaying spawning your custom entity by a tick to see if that works.

    For things like that you'll have to search through the NMS code. From a quick search through EntityChicken it looks like in the e() method the laying of eggs is controlled by the 'bu' variable. If you reduce that, it should lay eggs faster.

    Just wanted to let everyone know that the mc-dev repository has been updated on bukkit's github. This is where you can find any server classes that were not changed by Craftbukkit along with the original server classes. There is a link to it in the original post.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 6, 2016
    Conlexio and Garris0n like this.
  17. Offline

    tylersyme

    I also wanted to point out to everyone, that in addition to jogy mentioned above, if you have the minecraft source code that is used to create mods, you can view most of this NMS code unobfuscated. After about an hour of trying to read the obfuscated code, which was possible but required some guess work, once I realized I could just look at the actual minecraft source code. It was quite a bit easier :D
     
  18. Offline

    Conlexio

    Got it working! It was no one's fault but my own, as apparently my server turned monster spawning off without my knowledge! (I changed it to a cow and it worked).. Sorry for the trouble xD

    Thanks for this awesome tutorial btw! <3

    Ps: Does anyone know how to do this for EntityHuman? it requires a GameProfile and 3 implemented methods.. Seems pretty complex
     
  19. Offline

    Jogy34

    I helped someone a while back with that. You can find the thread here. Please don't post on that thread though as it is over a month old, if you have any questions just post them here. Anyways I'll make a tutorial on creating player npc's when I get a chance.
     
    Conlexio likes this.
  20. Offline

    Conlexio

    Thanks dude! You truly are awesome! <3 *hugs*
     
  21. Offline

    bfgbfggf

    is that possible to add own blocks?
    (only for change durability etc...)
     
  22. Offline

    Jogy34

    You can't add in actual new blocks or items with bukkit. There might be a way to change a block's toughness but I wouldn't know how to do that.
     
  23. Offline

    highz

    Jogy34 So is it possible to edit the behavior of mobs?
    For instance could I make a chicken that behaves like an enderdragon or something?
     
  24. Offline

    Jogy34

    Yeah. One way to do that would be to create a custom enderdragon and then send in the network id of a chicken. Another way would be to create a custom chicken and then change around it's path finding, target and goal selectors, etc... to make it mimic the behaviors of an enderdragon.
     
  25. Offline

    Omletowy

    What I am doing wrong? I can't spawn my custom zombie :/

    Main class:

    Code:java
    1. package example.test.custommob;
    2.  
    3. import java.lang.reflect.Field;
    4. import java.util.Map;
    5.  
    6. import org.bukkit.Location;
    7. import org.bukkit.craftbukkit.v1_7_R1.CraftWorld;
    8. import org.bukkit.entity.Player;
    9. import org.bukkit.event.EventHandler;
    10. import org.bukkit.event.Listener;
    11. import org.bukkit.event.player.PlayerJoinEvent;
    12. import org.bukkit.plugin.java.JavaPlugin;
    13.  
    14. public class Main extends JavaPlugin implements Listener{
    15.  
    16. static Main instance;
    17.  
    18. protected static Field mapStringToClassField, mapClassToStringField, mapClassToIdField, mapStringToIdField;
    19. //protected static Field mapIdToClassField;
    20.  
    21. static
    22. {
    23. try
    24. {
    25. mapStringToClassField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("c");
    26. mapClassToStringField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("d");
    27. //mapIdtoClassField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("e");
    28. mapClassToIdField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("f");
    29. mapStringToIdField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("g");
    30.  
    31. mapStringToClassField.setAccessible(true);
    32. mapClassToStringField.setAccessible(true);
    33. //mapIdToClassField.setAccessible(true);
    34. mapClassToIdField.setAccessible(true);
    35. mapStringToIdField.setAccessible(true);
    36. }
    37. catch(Exception e) {e.printStackTrace();}
    38. }
    39.  
    40.  
    41. public void onEnable()
    42. {
    43. instance = this;
    44. getServer().getPluginManager().registerEvents(this, this);
    45. }
    46.  
    47. public void onLoad()
    48. {
    49. addCustomEntity(CustomZombie.class, "CustomZombie", 54);
    50. }
    51.  
    52.  
    53. public void onDisable()
    54. {
    55.  
    56. }
    57.  
    58. @EventHandler
    59. public void onJoin(PlayerJoinEvent event)
    60. {
    61. Player player = event.getPlayer();
    62. if (player.getName().equals("Medykoo"))
    63. {
    64. /*ProtocolManager manager = ProtocolLibrary.getProtocolManager();
    65. WrapperPlayServerNamedEntitySpawn spawned = new WrapperPlayServerNamedEntitySpawn();
    66.  
    67. spawned.setEntityID(123546);
    68. spawned.setPosition(player.getLocation().toVector());
    69. spawned.setPlayerName("Bob");
    70. spawned.setPlayerUUID(UUID.randomUUID().toString().replaceAll("-", ""));
    71. // The rotation of the player's head (in degrees)
    72. spawned.setYaw(0);
    73. spawned.setPitch(-45);
    74.  
    75. spawned.setCurrentItem((short) 50);
    76.  
    77. int id = spawned.getEntityID();
    78. player.sendMessage("" + id);
    79.  
    80. // Documentation:
    81. // [url]http://mc.kev009.com/Entities#Entity_Metadata_Format[/url]
    82. WrappedDataWatcher watcher = new WrappedDataWatcher();
    83. watcher.setObject(0, (byte) 0); // Flags. Must be a byte.
    84. watcher.setObject(1, (short) 300); // Drowning counter. Must be short.
    85. watcher.setObject(9, (byte) 3);
    86. spawned.setMetadata(watcher);
    87.  
    88. try {
    89. manager.sendServerPacket(player, spawned.getHandle());
    90. } catch (InvocationTargetException e) {
    91. // TODO Auto-generated catch block
    92. e.printStackTrace();
    93. }*/
    94. spawnCustomEntity(player.getLocation());
    95. }
    96. }
    97.  
    98. public void spawnCustomEntity(Location loc)
    99. {
    100. net.minecraft.server.v1_7_R1.World nmsWorld = ((CraftWorld) loc.getWorld()).getHandle();
    101. CustomZombie customZombie = new CustomZombie(nmsWorld);
    102. customZombie.setPosition(loc.getX(), loc.getY(), loc.getZ());
    103. nmsWorld.addEntity(customZombie);
    104. }
    105.  
    106. @SuppressWarnings({ "rawtypes", "unchecked" })
    107. protected static void addCustomEntity(Class entityClass, String name, int id)
    108. {
    109. if (mapStringToClassField == null || mapStringToIdField == null || mapClassToStringField == null || mapClassToIdField == null)
    110. {
    111. return;
    112. }
    113. else
    114. {
    115. try
    116. {
    117. Map mapStringToClass = (Map) mapStringToClassField.get(null);
    118. Map mapStringToId = (Map) mapStringToIdField.get(null);
    119. Map mapClasstoString = (Map) mapClassToStringField.get(null);
    120. Map mapClassToId = (Map) mapClassToIdField.get(null);
    121.  
    122. mapStringToClass.put(name, entityClass);
    123. mapStringToId.put(name, Integer.valueOf(id));
    124. mapClasstoString.put(entityClass, name);
    125. mapClassToId.put(entityClass, Integer.valueOf(id));
    126.  
    127. mapStringToClassField.set(null, mapStringToClass);
    128. mapStringToIdField.set(null, mapStringToId);
    129. mapClassToStringField.set(null, mapClasstoString);
    130. mapClassToIdField.set(null, mapClassToId);
    131. }
    132. catch (Exception e)
    133. {
    134. e.printStackTrace();
    135. }
    136. }
    137. }
    138. }
    139.  


    CustomZombie class:

    Code:java
    1.  
    2. package example.test.custommob;
    3.  
    4. import net.minecraft.server.v1_7_R1.EntityZombie;
    5. import net.minecraft.server.v1_7_R1.GenericAttributes;
    6. import net.minecraft.server.v1_7_R1.World;
    7.  
    8. import org.bukkit.Material;
    9. import org.bukkit.inventory.ItemStack;
    10.  
    11. public class CustomZombie extends EntityZombie
    12. {
    13. public CustomZombie(/*net.minecraft.server.v1_7_R1.*/ World world)
    14. {
    15. super(world);
    16.  
    17. this.getAttributeInstance(GenericAttributes.a).setValue(100); //Max Health
    18. this.setHealth(100);
    19.  
    20. this.fireProof = true;
    21. }
    22.  
    23. @Override
    24. public String getName() //This will change the name in death messages along with other things
    25. {
    26. return "Tough Zombie";
    27. }
    28.  
    29. @Override
    30. public void dropDeathLoot(boolean flag, int i)//'i' is the looting level used and 'flag' is if it was killed by a player
    31. {
    32. this.world.getWorld().dropItem(this.getBukkitEntity().getLocation(), new ItemStack(Material.DIAMOND));
    33. }
    34. }
    35.  
    36.  
     
  26. Offline

    Codex Arcanum

    When I attempt to spawn a custom skeleton with the above technique, my client crashes with the following error.
    Code:
    java.lang.NullPointerException: Ticking screen
    at bkc.a(SourceFile:501)
    at fw.a(SourceFile:97)
    at fw.a(SourceFile:15)
    at eg.a(SourceFile:166)
    at bbp.c(SourceFile:78)
    at azl.p(SourceFile:1318)
    at azl.ah(SourceFile:757)
    at azl.f(SourceFile:711)
    at net.minecraft.client.main.Main.main(SourceFile:148)
    
    Any thoughts?
     
  27. Offline

    Jogy34


    I'm not completely sure but I think that may be because you are using a different networkId for your skeleton than the one for a skeleton. Not all network Ids work with all entities so try using 51 (the network id for a skeleton) and see if that still happens. If you are already using that then could you post your code as I don't have any other idea as to what that would be caused by.
     
  28. Offline

    Codex Arcanum

    Well, I figured out what the problem when I posted here last was - nothing wrong with your tutorial, I was just doing something derpy and not following instructions correctly. Then, I continued having the same problem after I fixed the first error, which was due to using the wrong NetID (neither Zombies nor Skeletons like being spawned with the wrong one). Once I solved my those two problems though, I had a weird experience where the skeleton would spawn and would just stand there and glare at me, and would float off into space if I touched it. Found out the trouble that caused that was that I was overriding the h() method without calling super.h(). Hopefully that information will help someone else at some point. Anyway, my problems are now solved, you have a good tutorial here, thanks for the time you spent trying to diagnose my problems :).
     
  29. Offline

    phondeux

    I can't get a modded skeleton to spawn. :(

    Here's my code;
    Code:java
    1. package com.phondeux.skeletonwizards;
    2.  
    3. import java.io.File;
    4. import java.io.IOException;
    5. import java.io.InputStream;
    6. import java.lang.reflect.Field;
    7. import java.util.Map;
    8. import java.util.logging.Level;
    9. import java.util.logging.Logger;
    10.  
    11. import org.bukkit.Location;
    12. import org.bukkit.configuration.file.FileConfiguration;
    13. import org.bukkit.configuration.file.YamlConfiguration;
    14. import org.bukkit.craftbukkit.v1_7_R1.CraftWorld;
    15. import org.bukkit.plugin.java.JavaPlugin;
    16.  
    17.  
    18. @SuppressWarnings({ "rawtypes", "unchecked" })
    19.  
    20. public class SkeletonWizards extends JavaPlugin
    21. {
    22. protected static Field mapStringToClassField, mapClassToStringField, mapClassToIdField, mapStringToIdField;
    23. protected static Field mapIdToClassField;
    24.  
    25. protected static void addCustomEntity(Class entityClass, String name, int id) {
    26. if (mapStringToClassField == null || mapStringToIdField == null || mapClassToStringField == null || mapClassToIdField == null) {
    27. return;
    28. } else {
    29. try {
    30. Map mapStringToClass = (Map) mapStringToClassField.get(null);
    31. Map mapStringToId = (Map) mapStringToIdField.get(null);
    32. Map mapClasstoString = (Map) mapClassToStringField.get(null);
    33. Map mapClassToId = (Map) mapClassToIdField.get(null);
    34.  
    35. mapStringToClass.put(name, entityClass);
    36. mapStringToId.put(name, Integer.valueOf(id));
    37. mapClasstoString.put(entityClass, name);
    38. mapClassToId.put(entityClass, Integer.valueOf(id));
    39.  
    40. mapStringToClassField.set(null, mapStringToClass);
    41. mapStringToIdField.set(null, mapStringToId);
    42. mapClassToStringField.set(null, mapClasstoString);
    43. mapClassToIdField.set(null, mapClassToId);
    44. } catch (Exception e) {
    45. e.printStackTrace();
    46. }
    47. }
    48. }
    49.  
    50.  
    51. static {
    52. try {
    53. mapStringToClassField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("c");
    54. mapClassToStringField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("d");
    55. mapIdToClassField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("e");
    56. mapClassToIdField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("f");
    57. mapStringToIdField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("g");
    58. mapStringToClassField.setAccessible(true);
    59. mapClassToStringField.setAccessible(true);
    60. mapIdToClassField.setAccessible(true);
    61. mapClassToIdField.setAccessible(true);
    62. mapStringToIdField.setAccessible(true);
    63. } catch (Exception e) {
    64. e.printStackTrace();
    65. }
    66. }
    67.  
    68.  
    69. public static final Logger log = Logger.getLogger("Minecraft");
    70.  
    71. private FileConfiguration customConfig = null;
    72. private File configFile = null;
    73.  
    74. public void onLoad() {
    75. addCustomEntity(SkeletonWizard.class, "SkeletonWizard", 51);
    76. }
    77.  
    78. public void onEnable() {
    79. //Load gates from config
    80. reloadCustomConfig();
    81.  
    82. log.info("[SkeletonWizards] Enabled.");
    83. }
    84.  
    85. public void onDisable() {
    86. log.info("[SkeletonWizards] Disabled.");
    87. // Save gates to config file
    88. saveCustomConfig();
    89. }
    90.  
    91. public void reloadCustomConfig() {
    92. if (configFile == null) {
    93. configFile = new File(getDataFolder(), "customConfig.yml");
    94. }
    95. customConfig = YamlConfiguration.loadConfiguration(configFile);
    96.  
    97. // Look for defaults in the jar
    98. InputStream defConfigStream = this.getResource("customConfig.yml");
    99. if (defConfigStream != null) {
    100. YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);
    101. customConfig.setDefaults(defConfig);
    102. }
    103. }
    104.  
    105. public FileConfiguration getCustomConfig() {
    106. if (customConfig == null) {
    107. this.reloadCustomConfig();
    108. }
    109. return customConfig;
    110. }
    111.  
    112. public void saveCustomConfig() {
    113. if (customConfig == null || configFile == null) {
    114. return;
    115. }
    116. try {
    117. getCustomConfig().save(configFile);
    118. } catch (IOException ex) {
    119. this.getLogger().log(Level.SEVERE, "Could not save config to " + configFile, ex);
    120. }
    121. }
    122.  
    123. public void spawnCustomEntity(Location loc) {
    124. net.minecraft.server.v1_7_R1.World nmsWorld = ((CraftWorld) loc.getWorld()).getHandle();
    125. SkeletonWizard customSkeleton = new SkeletonWizard(nmsWorld);
    126. customSkeleton.setPosition(loc.getX(), loc.getY(), loc.getZ());
    127. nmsWorld.addEntity(customSkeleton);
    128. }
    129. }


    And the SkeletonWizard class
    Code:java
    1. package com.phondeux.skeletonwizards;
    2.  
    3. import org.bukkit.Material;
    4. import org.bukkit.inventory.ItemStack;
    5.  
    6. import net.minecraft.server.v1_7_R1.EntitySkeleton;
    7. import net.minecraft.server.v1_7_R1.GenericAttributes;
    8. import net.minecraft.server.v1_7_R1.World;
    9.  
    10. public class SkeletonWizard extends EntitySkeleton {
    11. public SkeletonWizard(/**/ World world) {
    12. super(world);
    13.  
    14. this.getAttributeInstance(GenericAttributes.a).setValue(20);
    15. this.setHealth(20);
    16.  
    17. this.fireProof = true;
    18. }
    19.  
    20. @Override
    21. public String getName() {
    22. return "Skeleton Wizard";
    23. }
    24.  
    25. @Override
    26. public void dropDeathLoot(boolean flag, int i) {
    27. this.world.getWorld().dropItem(this.getBukkitEntity().getLocation(), new ItemStack(Material.BLAZE_ROD));
    28. }
    29.  
    30. }
    31.  
     
Thread Status:
Not open for further replies.

Share This Page