Solved Spawning Single Custom Mobs

Discussion in 'Plugin Development' started by travelingdp, Dec 7, 2013.

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

    travelingdp

    Hello, everyone. I've been working on a new plugin, and I'm having a little trouble understanding exactly how to deal with custom mobs. There are two parts to my question.

    First off, I have spent quite some time studying this tutorial:
    Tutorial - How to Customize the Behaviour of a Mob or Entity

    So, please, do not start off by just telling me to "read this thread" because I have already read through the entire thread several times. However, if you want to help explain certain parts of that tutorial, that is fine.

    Okay, so to start, here's the CustomEntity enum I've started with:
    Code:java
    1. public enum CustomEntity {
    2.  
    3. COW("Cow", 92, EntityType.COW, EntityCow.class, CustomCow.class);
    4.  
    5. private String name;
    6. private int id;
    7. private EntityType entityType;
    8. private Class<? extends EntityInsentient> nmsClass;
    9. private Class<? extends EntityInsentient> customClass;
    10.  
    11. private CustomEntity(String name, int id, EntityType entityType, Class<? extends EntityInsentient> nmsClass, Class<? extends EntityInsentient> customClass){
    12. this.name = name;
    13. this.id = id;
    14. this.entityType = entityType;
    15. this.nmsClass = nmsClass;
    16. this.customClass = customClass;
    17. }
    18.  
    19. public String getName(){
    20. return this.name;
    21. }
    22.  
    23. public int getID(){
    24. return this.id;
    25. }
    26.  
    27. public EntityType getEntityType(){
    28. return this.entityType;
    29. }
    30.  
    31. public Class<? extends EntityInsentient> getNMSClass(){
    32. return this.nmsClass;
    33. }
    34.  
    35. public Class<? extends EntityInsentient> getCustomClass(){
    36. return this.customClass;
    37. }
    38.  
    39. public static void registerEntities(){
    40. for (CustomEntity entity : values()){
    41. try{
    42. Method a = EntityTypes.class.getDeclaredMethod("a", new Class<?>[]{Class.class, String.class, int.class});
    43. a.setAccessible(true);
    44. a.invoke(null, entity.getCustomClass(), entity.getName(), entity.getID());
    45. }catch (Exception e){
    46. e.printStackTrace();
    47. }
    48. }
    49. }
    50.  
    51. }


    and this is my CustomCow class, which currently doesn't do anything:
    Code:java
    1. public class CustomCow extends EntityCow {
    2.  
    3. public CustomCow(World world) {
    4. super(world);
    5. }
    6.  
    7. }


    I made these based off the tutorial linked above. There is one difference though. I don't do the part with all the BiomeMeta stuff because I don't think I want to have that part. I don't want to replace all the mobs that spawn in the world, but I want to be able to spawn single instances of my custom mobs. Am I correct in assuming I don't need the BiomeMeta part?

    Next, I have an EventListener that checks to see if a player right-clicks with a specified item. If so, it should spawn the custom mob at that location. Here is that class:
    Code:java
    1. public class CustomListener implements Listener {
    2.  
    3. @EventHandler
    4. public void onPlayerInteractEvent(PlayerInteractEvent event) {
    5.  
    6. ItemStack stack = event.getItem();
    7.  
    8. if (stack.isSimilar(CustomItems.ITEM_A)) { //CustomItems is an enum with some predetermined ItemStacks
    9.  
    10. event.setCancelled(true); //Cancel any interaction if player is holding CustomItems.ITEM_A
    11.  
    12. if (event.getAction == Action.RIGHT_CLICK_BLOCK) {
    13.  
    14. //Here I get the blocks around the clicked block
    15. //I left this part out because it is not the problem
    16.  
    17. if (/* Here I make sure there is room to spawn the mob */) {
    18.  
    19. Location loc = event.getClickedBlock().getLocation();
    20. loc.setY(loc.getY() + 1.1); //Set location to slightly above clicked block so mob wont spawn inside block
    21.  
    22. World world = loc.getWorld();
    23. world.spawnEntity(/* What should I do to spawn a CustomCow at this location? */);
    24.  
    25. } else {
    26. event.getPlayer().sendMessage(ChatColor.DARK_RED + "You don't have room to spawn that here!");
    27. }
    28.  
    29. }
    30.  
    31. }
    32.  
    33. }
    34.  
    35. }


    What should I do to spawn the CustomCow (or any other CustomEntity) at that location?

    My other question is this: What do I need to do in my CustomCow class to make that mob stand still.
     
  2. Offline

    Deleted user

    travelingdp
    Rather than making an enum...I'd suggest make a method that spawn 's a custom cow at a location
    Like: customCowSpawn(Location loc, int amount, int health, int maxhealth, etc...){}
     
  3. Offline

    travelingdp

    The reason I'm using an enum is because eventually I'm going to include all mob types. I don't want to create a separate method for each mob. That's not the problem though. I have to replace the entity's NMS class with my custom class, which is explained in the tutorial posted above (this is also a reason for using an enum). I need to know how to specify in my custom class that the custom mob should not move or basically do anything that a mob would normally do. After that, I need to know how to spawn my custom mob instead of the normal one. For example, in the case of a cow, I'd like to spawn a cow at the clicked location, but replace its NMS class (EntityCow.class) with my CustomCow class so that it behaves the way I want it.

    Bump.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 5, 2016
  4. Offline

    Deleted user

    travelingdp
    It would help if I knew more how to use enums ;)
     
  5. Offline

    travelingdp

    I don't think the enum is my biggest problem here. I have my enum set up the way it needs to be, I just need to know what method I need to override in my custom class to make a mob stand still and then I need to know how to spawn a mob but replace it's behavior class with my custom class. So if I spawn a cow, creeper, or any other mob I decide to put in my enum, it shouldn't attack or run around or anything, it should just stand there.

    Bump.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 5, 2016
  6. Offline

    Axe2760

    travelingdp spawning in single custom mobs is actually quite an easy task. You cannot (or if you could, you should not) use the bukkit's spawn entity method and try to replace it with your entity; instead, you simply just create an instance of your mob, set the position, and use nms's world object's method to add in your mob like so:
    PHP:
    public void spawnCustomEntity(CustomEntity entityLocation lnet.minecraft.server.version.World world){
      
    EntityInsentient entity = (EntityInsentient)entity.getNMSClass().newInstance();
      
    entity.setPosition(l.getX(), l.getY(), l.getZ());
      
    world.addEntity(entity); //I don't recall if it is add(Entity entity) or addEntity(Entity entity), so you may have to change it
    }
    Regarding your other question, you can try overriding the move(...) method that entities have to remove moving, however in the past I've found it to be buggy.

    Edit: also, to get the nms world, cast org.bukkit.world.World to CraftWorld, and call CraftWorld.getHandle();
     
    reider45 likes this.
  7. Offline

    travelingdp

    Axe2760 Awesome, thanks! I figured it was simpler than I was making it.
     
  8. Offline

    Axe2760

  9. Offline

    travelingdp

    Axe2760 I'm having some trouble. I'm getting this error:
    Code:
    09.12 09:52:19 [Server] SEVERE at net.minecraft.server.v1_6_R3.PlayerInteractManager.interact(PlayerInteractManager.java:373)
    09.12 09:52:19 [Server] SEVERE at org.bukkit.craftbukkit.v1_6_R3.event.CraftEventFactory.callPlayerInteractEvent(CraftEventFactory.java:190)
    09.12 09:52:19 [Server] SEVERE at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:462)
    09.12 09:52:19 [Server] SEVERE at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:477)
    09.12 09:52:19 [Server] SEVERE at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62)
    09.12 09:52:19 [Server] SEVERE at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:425)
    09.12 09:52:19 [Server] SEVERE at java.lang.reflect.Method.invoke(Unknown Source)
    09.12 09:52:19 [Server] SEVERE at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    09.12 09:52:19 [Server] SEVERE at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    09.12 09:52:19 [Server] SEVERE at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    09.12 09:52:19 [Server] SEVERE at com.yesluh.pokemobs.BallListner.onPlayerInteractEvent(BallListner.java:68)
    09.12 09:52:19 [Server] SEVERE at com.yesluh.pokemobs.BallListner.spawnCustomEntity(BallListner.java:101)
    09.12 09:52:19 [Server] SEVERE at java.lang.Class.newInstance(Unknown Source)
    09.12 09:52:19 [Server] SEVERE at java.lang.Class.newInstance0(Unknown Source)
    09.12 09:52:19 [Server] SEVERE java.lang.InstantiationException: net.minecraft.server.v1_6_R3.EntityCow
    I have a PlayerInteractEvent listener, and within that, here is the block that calls the spawnCustomEntity() method:

    Code:java
    1. if (entity != null) {
    2.  
    3. if (p.equalsIgnoreCase(owner)) {
    4.  
    5. if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
    6.  
    7. Block blocka = event.getClickedBlock().getRelative(BlockFace.UP, 1); //These lines are just to check if there is room to spawn the mob
    8. Block blockb = event.getClickedBlock().getRelative(BlockFace.UP, 2);
    9. Block blockc = event.getClickedBlock().getRelative(BlockFace.UP, 3);
    10. Block blockd = event.getClickedBlock().getRelative(BlockFace.UP, 4);
    11.  
    12. if (blocka.getType() == Material.AIR && blockb.getType() == Material.AIR && blockc.getType() == Material.AIR && blockd.getType() == Material.AIR) {
    13.  
    14. Location loc = event.getClickedBlock().getLocation();
    15. loc.setY(loc.getY() + 1.5);
    16. CraftWorld world = (CraftWorld) loc.getWorld();
    17.  
    18. try {
    19. spawnCustomEntity(entity, name, type, owner, loc, world.getHandle()); // This is line 68
    20. } catch (InstantiationException e) {
    21. e.printStackTrace();
    22. } catch (IllegalAccessException e) {
    23. e.printStackTrace();
    24. }
    25.  
    26. } else {
    27.  
    28. event.getPlayer().sendMessage(ChatColor.DARK_RED + "You don't have room to spawn your mob here!");
    29.  
    30. }
    31.  
    32.  
    33. }
    34.  
    35. } else {
    36.  
    37. event.getPlayer().sendMessage(ChatColor.DARK_RED + "You don't own that mob!");
    38.  
    39. }


    Here is the spawnCustomEntity() method:
    Code:java
    1. public void spawnCustomEntity(CustomEntity entity, String name, String type, String owner, Location loc, net.minecraft.server.v1_6_R3.World world) throws InstantiationException, IllegalAccessException {
    2.  
    3. EntityInsentient e = (EntityInsentient) entity.getNMSClass().newInstance(); //This is line 101
    4. e.setPosition(loc.getX(), loc.getY(), loc.getZ());
    5.  
    6. e.setCustomName(name);
    7. e.setCustomNameVisible(true);
    8.  
    9. world.addEntity(e);
    10.  
    11. }


    Any idea as to what's causing the InstantiationException?
     
  10. Offline

    Axe2760

    travelingdp sorry, I had a major derp moment. It should be entity.getCustomClass(), not getNMSClass, sorry. My bad, haha
     
  11. Offline

    Ronbo

    Axe2760
    I'm trying to the same thing right now, and using getCustomClass() instead still seems to throw an InstantiationException. Any ideas?
     
  12. Offline

    Axe2760

    Ronbo perhaps I screwed it up somehow, but I thought in theory it should work. I'll test it out, as I haven't coded in bukkit for a month... :eek: Very sorry, if that is true disregard my post! :p I'll check my code, but I could have been certain it worked before. Is your class abstract?
     
  13. Offline

    Ronbo

    Axe2760
    Fixed it, but it's very ugly.

    Code:
    try {
                                    ei = (EntityInsentient)(CustomEntityVillager.class.getDeclaredConstructor(net.minecraft.server.v1_7_R1.World.class).newInstance(world));
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
    A class's newInstance() tries to use a default no-param constructor which wouldn't exist in this case. You can just get the other constructor and use newInstance from that with the parameters.

    Since we're on the subject, could you take a look at this if you're familiar with it?
    Code:
    try {
     
    Field a = this.goalSelector.getClass().getDeclaredField("a");
    a.setAccessible(true);
     
    Field modifiers = Field.class.getDeclaredField("modifiers");
    modifiers.setAccessible(true);
       modifiers.setInt(a, a.getModifiers() & ~Modifier.FINAL);
     
       a.set(this.goalSelector, new ArrayList());
     
    } catch(Exception e) {
    e.printStackTrace();
    }
    I'm trying to clear PathfinderGoalSelection's list of goals so that I can add my own, but unfortunately this doesn't work out very well - I get an IllegalAccessException at the set() call. I even tried to take out the final modifier so that I could change it but it didn't do anything.
     
  14. Offline

    Cirno

    "a" is a org.bukkit.craftbukkit.util.UnsafeList list, not standard ArrayLists().
    Other than that, "a" isn't a final field, at least, in the 1.6.x builds. For 1.7.2, "a" is a Logger.
     
  15. Offline

    Ronbo

    Cirno

    Yeah I just realized that myself :p I was only trying ArrayList because it wasn't working with UnsafeList. I was using a 1.6.x source as a reference so I was like "wat what is going on here". Then I noticed that it said something about the Logger and I checked and they changed "a", which is kind of trolly but w/e. It works fine now :)
     
  16. Offline

    travelingdp

    Cirno Ronbo yeah, I figured I should use getCustomClass() instead, but I was still getting the error. I'll try this once I get the chance, thanks!

    Now I'm getting bigger problems. No errors server-side, but crashes on the client side.

    Wait, no, I fixed it. I forgot to actually register my custom classes on enable lol. It's always the little things.

    Thanks for the help, guys! :)

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 5, 2016
  17. Offline

    Axe2760

    Haha yes I derped out, and forgot the constructor :p sorry about that, glad to know it worked though! :p
     
Thread Status:
Not open for further replies.

Share This Page