[Tutorial] Creating custom entities with PathfinderGoals

Discussion in 'Spigot Plugin Development' started by XlordalX, May 11, 2014.

  1. I just want to return entity back when they loose target (No nearby players).
     
  2. @GodPlay
    Code (Text):
    if(entity.getGoalTarget() == null)
    {
    //Put entity back to location
    }
     
  3. Well, when I try and do this, it says EntityZombie can't be resolved to a type. What does this mean and can anyone fix it?
     
  4. Show us your code
     
  5. Do you want the entity to stand still even when it's being pushed or do you really want the effect of the entity walking back to its spawn location?
     
  6. Yes, I want to return back entity to its spawn location by walking (no teleports).
    Yes, I understand how to check, but which, maybe event, could be used for checking?
     
  7. Just add a Pathfindergoal which checks if the range is too far away from the entity's spawn and then use its navigation to walk back.
     
  8. Ok, I will create PathFinderGoal class and where I should check it, in which method?
     
  9. In a() return true and in e() put the code.
     
    • Like Like x 1
  10. I am having issues, when I run my command to spawn my zombie, I get an internal error, I tried debugging it, but it didn't help. So here is my code and console for anyone to help if they can.
    Code (Text):
    package me.kippy.entity;

    import java.util.logging.Logger;

    import org.bukkit.Bukkit;
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandSender;
    import org.bukkit.entity.Player;
    import org.bukkit.event.Listener;
    import org.bukkit.plugin.java.JavaPlugin;

    public class Core extends JavaPlugin implements Listener{
        private Logger logger;
       
        @Override
        public void onDisable() {
            this.logger.info("DISABLED!");
        }
       
        @Override
        public void onEnable() {
            logger = getLogger();
            this.logger.info("ENABLED!");
        }
       
        public boolean onCommand(CommandSender sender, Command cmd, String cl, String[] args) {
            if(sender instanceof Player) {
                System.out.println("1");
                Player p = (Player) sender;
                System.out.println("2");
                if(cl.equalsIgnoreCase("zombie")) {
                    System.out.println("3");
                    Entities.spawnEntity(new CustomZombie(p.getWorld()), p.getLocation());
                    System.out.println("4");
                }
            }
           
            return false;
           
        }

    }
    Code (Text):
    package me.kippy.entity;

    import java.lang.reflect.Field;
    import java.util.Map;

    import net.minecraft.server.v1_8_R1.Entity;

    import org.bukkit.Location;
    import org.bukkit.craftbukkit.v1_8_R1.CraftWorld;

    public enum Entities {
       
        CUSTOM_ZOMBIE("Zombie", 54, CustomZombie.class);
       
        private Entities(String name, int id, Class<? extends Entity> custom) {
            addToMaps(custom, name, id);
        }
       
        public static void spawnEntity(Entity entity, Location loc) {
            System.out.println("5");
            entity.setLocation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
            System.out.println("6");
            ((CraftWorld)loc.getWorld()).getHandle().addEntity(entity);
            System.out.println("7");
        }
       
        private static void addToMaps(Class clazz, String name, int id) {
            //getPrivateField is the method from above.
            //Remove the lines with // in front of them if you want to override default entities (You'd have to remove the default entity from the map first though).
            ((Map)getPrivateField("c", net.minecraft.server.v1_8_R1.EntityTypes.class, null)).put(name, clazz);
            ((Map)getPrivateField("d", net.minecraft.server.v1_8_R1.EntityTypes.class, null)).put(clazz, name);
            ((Map)getPrivateField("e", net.minecraft.server.v1_8_R1.EntityTypes.class, null)).put(Integer.valueOf(id), clazz);
            ((Map)getPrivateField("f", net.minecraft.server.v1_8_R1.EntityTypes.class, null)).put(clazz, Integer.valueOf(id));
            ((Map)getPrivateField("g", net.minecraft.server.v1_8_R1.EntityTypes.class, null)).put(name, Integer.valueOf(id));
        }
       
        public static Object getPrivateField(String fieldName, Class clazz, Object object) {
            Field field;
            Object o = null;

            try
            {
                field = clazz.getDeclaredField(fieldName);

                field.setAccessible(true);

                o = field.get(object);
            }
            catch(NoSuchFieldException e)
            {
                e.printStackTrace();
            }
            catch(IllegalAccessException e)
            {
                e.printStackTrace();
            }

            return o;
        }

    }
     
    Code (Text):
    package me.kippy.entity;

    import java.awt.List;
    import java.lang.reflect.Field;

    import org.bukkit.craftbukkit.v1_8_R1.CraftWorld;

    import net.minecraft.server.v1_8_R1.EntityZombie;
    import net.minecraft.server.v1_8_R1.PathfinderGoalSelector;

    public class CustomZombie extends EntityZombie {
        @SuppressWarnings("deprecation")
        public CustomZombie(org.bukkit.World world) //You can also directly use the nms world class but this is easier if you are spawning this entity.
        {
            super(((CraftWorld)world).getHandle());
           
            List goalB = (List)getPrivateField("b", PathfinderGoalSelector.class, goalSelector); goalB.clear();
            List goalC = (List)getPrivateField("c", PathfinderGoalSelector.class, goalSelector); goalC.clear();
            List targetB = (List)getPrivateField("b", PathfinderGoalSelector.class, targetSelector); targetB.clear();
            List targetC = (List)getPrivateField("c", PathfinderGoalSelector.class, targetSelector); targetC.clear();
           
        }
       
        public static Object getPrivateField(String fieldName, Class clazz, Object object) {
            Field field;
            Object o = null;

            try
            {
                field = clazz.getDeclaredField(fieldName);

                field.setAccessible(true);

                o = field.get(object);
            }
            catch(NoSuchFieldException e)
            {
                e.printStackTrace();
            }
            catch(IllegalAccessException e)
            {
                e.printStackTrace();
            }

            return o;
        }
       
    }
    Code (Text):
    [11:12:21] [Server thread/INFO]: Kippy issued server command: /zombie
    [11:12:21] [Server thread/INFO]: 1
    [11:12:21] [Server thread/INFO]: 2
    [11:12:21] [Server thread/INFO]: 3
    [11:12:21] [Server thread/ERROR]: null
    org.bukkit.command.CommandException: Unhandled exception executing command 'zombie' in plugin Entities v1.0
        at org.bukkit.command.PluginCommand.execute(PluginCommand.java:46) ~[spigot.jar:git-Spigot-29dbaa7-262c777]
        at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:141) ~[spigot.jar:git-Spigot-29dbaa7-262c777]
        at org.bukkit.craftbukkit.v1_8_R1.CraftServer.dispatchCommand(CraftServer.java:642) ~[spigot.jar:git-Spigot-29dbaa7-262c777]
        at net.minecraft.server.v1_8_R1.PlayerConnection.handleCommand(PlayerConnection.java:1115) [spigot.jar:git-Spigot-29dbaa7-262c777]
        at net.minecraft.server.v1_8_R1.PlayerConnection.a(PlayerConnection.java:950) [spigot.jar:git-Spigot-29dbaa7-262c777]
        at net.minecraft.server.v1_8_R1.PacketPlayInChat.a(PacketPlayInChat.java:26) [spigot.jar:git-Spigot-29dbaa7-262c777]
        at net.minecraft.server.v1_8_R1.PacketPlayInChat.a(PacketPlayInChat.java:53) [spigot.jar:git-Spigot-29dbaa7-262c777]
        at net.minecraft.server.v1_8_R1.PacketHandleTask.run(SourceFile:13) [spigot.jar:git-Spigot-29dbaa7-262c777]
        at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) [?:1.8.0_40]
        at java.util.concurrent.FutureTask.run(Unknown Source) [?:1.8.0_40]
        at net.minecraft.server.v1_8_R1.MinecraftServer.z(MinecraftServer.java:683) [spigot.jar:git-Spigot-29dbaa7-262c777]
        at net.minecraft.server.v1_8_R1.DedicatedServer.z(DedicatedServer.java:316) [spigot.jar:git-Spigot-29dbaa7-262c777]
        at net.minecraft.server.v1_8_R1.MinecraftServer.y(MinecraftServer.java:623) [spigot.jar:git-Spigot-29dbaa7-262c777]
        at net.minecraft.server.v1_8_R1.MinecraftServer.run(MinecraftServer.java:526) [spigot.jar:git-Spigot-29dbaa7-262c777]
        at java.lang.Thread.run(Unknown Source) [?:1.8.0_40]
    Caused by: java.lang.ClassCastException: org.bukkit.craftbukkit.v1_8_R1.util.UnsafeList cannot be cast to java.awt.List
        at me.kippy.entity.CustomZombie.<init>(CustomZombie.java:17) ~[?:?]
        at me.kippy.entity.Core.onCommand(Core.java:33) ~[?:?]
        at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[spigot.jar:git-Spigot-29dbaa7-262c777]
        ... 14 more
     
     
  11. Use java.util.List instead of java.awt.List
     
  12. Thank you so much, it works now.
     
  13. My path finding isn't working. He just stands there. Here is my code:

    Code (Text):
    package me.kippy.entity;

    import org.bukkit.Location;

    import net.minecraft.server.v1_8_R1.EntityInsentient;
    import net.minecraft.server.v1_8_R1.Navigation;
    import net.minecraft.server.v1_8_R1.PathEntity;
    import net.minecraft.server.v1_8_R1.PathfinderGoal;

    public class PathfinderGoalWalkToLoc extends PathfinderGoal {
       
           private double speed;

           private EntityInsentient entity;

           private Location loc;

           private Navigation navigation;

           public PathfinderGoalWalkToLoc(EntityInsentient entity, Location loc, double speed) {
             this.entity = entity;
             this.loc = loc;
             this.navigation = (Navigation) this.entity.getNavigation();
             this.speed = speed;
           }
       
        public boolean a() {
           
            return true;
           
        }
       
        public void c() {
            PathEntity pathEntity = this.navigation.a(loc.getX(), loc.getY(), loc.getZ());

            this.navigation.a(pathEntity, speed);
        }

    }
    Code (Text):
    package me.kippy.entity;

    import java.util.List;
    import java.lang.reflect.Field;

    import org.bukkit.Bukkit;
    import org.bukkit.Location;
    import org.bukkit.craftbukkit.v1_8_R1.CraftWorld;

    import net.minecraft.server.v1_8_R1.EntitySkeleton;
    import net.minecraft.server.v1_8_R1.EntitySpider;
    import net.minecraft.server.v1_8_R1.EntityZombie;
    import net.minecraft.server.v1_8_R1.PathfinderGoalFloat;
    import net.minecraft.server.v1_8_R1.PathfinderGoalHurtByTarget;
    import net.minecraft.server.v1_8_R1.PathfinderGoalLookAtPlayer;
    import net.minecraft.server.v1_8_R1.PathfinderGoalMeleeAttack;
    import net.minecraft.server.v1_8_R1.PathfinderGoalMoveThroughVillage;
    import net.minecraft.server.v1_8_R1.PathfinderGoalMoveTowardsRestriction;
    import net.minecraft.server.v1_8_R1.PathfinderGoalNearestAttackableTarget;
    import net.minecraft.server.v1_8_R1.PathfinderGoalRandomLookaround;
    import net.minecraft.server.v1_8_R1.PathfinderGoalRandomStroll;
    import net.minecraft.server.v1_8_R1.PathfinderGoalSelector;

    public class CustomZombie extends EntityZombie {
        public CustomZombie(org.bukkit.World world) //You can also directly use the nms world class but this is easier if you are spawning this entity.
        {
            super(((CraftWorld)world).getHandle());
           
            List goalB = (List)getPrivateField("b", PathfinderGoalSelector.class, goalSelector); goalB.clear();
            List goalC = (List)getPrivateField("c", PathfinderGoalSelector.class, goalSelector); goalC.clear();
            List targetB = (List)getPrivateField("b", PathfinderGoalSelector.class, targetSelector); targetB.clear();
            List targetC = (List)getPrivateField("c", PathfinderGoalSelector.class, targetSelector); targetC.clear();
           
            this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this));
            this.goalSelector.a(8, new PathfinderGoalWalkToLoc(this, new Location(Bukkit.getWorld("world"), 584, 63, 138), 0.5D));
           
        }
       
        public static Object getPrivateField(String fieldName, Class clazz, Object object) {
            Field field;
            Object o = null;

            try
            {
                field = clazz.getDeclaredField(fieldName);

                field.setAccessible(true);

                o = field.get(object);
            }
            catch(NoSuchFieldException e)
            {
                e.printStackTrace();
            }
            catch(IllegalAccessException e)
            {
                e.printStackTrace();
            }

            return o;
        }
       
    }
     
  14. 2 things that are pretty simple that might have caused an issue:
    1. Is the mob spawning in the same world? Otherwise it can't walk to that position
    2. Is the mob spawning at the same location you want it to walk to?
     
  15. @TheFancyWhale
    Mobs sometimes have methods for AI's. Not everything is inside the Pathfinders.

    Bat's random flying is in method E(), and just as I've tried looking for the enderman's tp'ing and whatnot, it was also in the E().

    You'll need to actually look into the EntityEnderDragon code in the server and manually read what's going on. Then you can just go ahead and Override whatever you need.
     
    • Useful Useful x 1
  16. Okay. Thanks for the advice! I will read into the code and report back.
    Yep, you were correct. I did some more research and the dragon's random flying method is also E(). I have now got it working. Thanks again!
     
    #116 TheFancyWhale, Jul 17, 2015
    Last edited: Jul 17, 2015
  17. 1. Yes
    2. No
     
  18. Hi there! I am getting SO many errors in my code!

    Even after copy and pasting and changing what needed to be changed, the errors remain.

    My Main.java
    Code (Text):
    import java.util.Map;

    import org.bukkit.Location;
    import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
    import org.bukkit.entity.Entity;
    import org.bukkit.plugin.java.JavaPlugin;

    public class Main extends JavaPlugin{
       
        @Override
        public void onEnable() {
            getLogger().info("Plugin Enabled!");
        }
       
        @Override
        public void onDisable() {
            getLogger().info("Plugin Disabled!");
        }
       
        //-----Custom Mobs-----\\
        public enum EntityTypes
        {
            //NAME("Entity name", Entity ID, yourcustomclass.class);
            CUSTOM_ZOMBIE("Zombie", 54, Bandit.class); //You can add as many as you want.

            private EntityTypes(String name, int id, Class<? extends Entity> custom)
            {
                addToMaps(custom, name, id);
            }

          public static void spawnEntity(Entity entity, Location loc)
           {
             entity.setLocation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
             ((CraftWorld)loc.getWorld()).getHandle().addEntity(entity);
           }

            private static void addToMaps(Class clazz, String name, int id)
            {
                //getPrivateField is the method from above.
                //Remove the lines with // in front of them if you want to override default entities (You'd have to remove the default entity from the map first though).
                ((Map)getPrivateField("c", net.minecraft.server.v1_8_R3.EntityTypes.class, null)).put(name, clazz);
                ((Map)getPrivateField("d", net.minecraft.server.v1_8_R3.EntityTypes.class, null)).put(clazz, name);
                //((Map)getPrivateField("e", net.minecraft.server.v1_7_R4.EntityTypes.class, null)).put(Integer.valueOf(id), clazz);
                ((Map)getPrivateField("f", net.minecraft.server.v1_8_R3.EntityTypes.class, null)).put(clazz, Integer.valueOf(id));
                //((Map)getPrivateField("g", net.minecraft.server.v1_7_R4.EntityTypes.class, null)).put(name, Integer.valueOf(id));
            }
        }
    }
     
    Bandit.java
    Code (Text):
    import java.lang.reflect.Field;
    import java.util.List;

    import net.minecraft.server.v1_8_R3.EntityHuman;
    import net.minecraft.server.v1_8_R3.EntityZombie;
    import net.minecraft.server.v1_8_R3.PathfinderGoalFloat;
    import net.minecraft.server.v1_8_R3.PathfinderGoalHurtByTarget;
    import net.minecraft.server.v1_8_R3.PathfinderGoalLookAtPlayer;
    import net.minecraft.server.v1_8_R3.PathfinderGoalMeleeAttack;
    import net.minecraft.server.v1_8_R3.PathfinderGoalMoveThroughVillage;
    import net.minecraft.server.v1_8_R3.PathfinderGoalMoveTowardsRestriction;
    import net.minecraft.server.v1_8_R3.PathfinderGoalNearestAttackableTarget;
    import net.minecraft.server.v1_8_R3.PathfinderGoalRandomLookaround;
    import net.minecraft.server.v1_8_R3.PathfinderGoalRandomStroll;
    import net.minecraft.server.v1_8_R3.PathfinderGoalSelector;

    import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;

    public class Bandit extends EntityZombie{

        @SuppressWarnings({ "unchecked", "rawtypes" })
        public Bandit(org.bukkit.World world) {
            super(((CraftWorld)world).getHandle());
           
            List goalB = (List)getPrivateField("b", PathfinderGoalSelector.class, goalSelector); goalB.clear();
            List goalC = (List)getPrivateField("c", PathfinderGoalSelector.class, goalSelector); goalC.clear();
            List targetB = (List)getPrivateField("b", PathfinderGoalSelector.class, targetSelector); targetB.clear();
            List targetC = (List)getPrivateField("c", PathfinderGoalSelector.class, targetSelector); targetC.clear();
           
            this.goalSelector.a(0, new PathfinderGoalFloat(this));
            this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, EntityHuman.class, 1.0D, false));
            this.goalSelector.a(5, new PathfinderGoalMoveTowardsRestriction(this, 1.0D));
            this.goalSelector.a(6, new PathfinderGoalMoveThroughVillage(this, 1.0D, false));
            this.goalSelector.a(7, new PathfinderGoalRandomStroll(this, 1.0D));
            this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F));
            this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this));
            this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true));
            this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, true));
        }
       
        @SuppressWarnings({"unused","rawtypes" })
        public static Object getPrivateField(String fieldName, Class clazz, Object object) {
           
            Field field;
            Object o = null;
           
            try {
                field = clazz.getDeclaredField(fieldName);
               
                field.setAccessible(true);
               
                o = field.get(object);
            } catch(NoSuchFieldException nsfe) {
                nsfe.printStackTrace();
            } catch(IllegalAccessException iae) {
                iae.printStackTrace();
            }
           
            return 0;
        }
       
    }
    The errors are as follows:
    1.) The constructor Main.EntityTypes(String, int, Class<Bandit>) is undefined Main.java line 34
    2.) The method addEntity(Entity) in the type World is not applicable for the arguments (Entity) Main.java line 44
    3.)The method getPrivateField(String, Class<EntityTypes>, null) is undefined for the type Main.EntityTypes Main.java line 51
    4.) The method getPrivateField(String, Class<EntityTypes>, null) is undefined for the type Main.EntityTypes Main.java line 52
    5.) The method getPrivateField(String, Class<EntityTypes>, null) is undefined for the type Main.EntityTypes Main.java line 54
    6.) The method getPrivateField(String, Class<EntityTypes>, null) is undefined for the type Main.EntityTypes Main.java line 43

    Please help! I really would like custom pathfinding mobs!
     
  19. @EpicChicken
    You make a separate enum class. You combined it with your main class. An enum is a type of class and you need a separate class file for it. Try that and see if it works.
     
  20. Hi! Thanks for your help so far! I've almost got it to work. Now I just get this error: The constructor EntityTypes(String, int, Class<Bandit>) is undefined

    Here's my enum code:
    Code (Text):
    import java.lang.reflect.Field;
    import java.util.Map;

    import org.bukkit.Location;
    import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
    import org.bukkit.entity.Entity;

    public enum EntityTypes {
        //NAME("Entity name", Entity ID, yourcustomclass.class);
        BANDIT("Zombie", 54, Bandit.class); //You can add as many as you want.

        private EntityTypes(String name, int id, Class<? extends Entity> custom)
        {
            addToMaps(custom, name, id);
        }

      public static void spawnEntity(Entity entity, Location loc)
       {
         ((net.minecraft.server.v1_8_R3.Entity) entity).setLocation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
         ((CraftWorld)loc.getWorld()).getHandle().addEntity((net.minecraft.server.v1_8_R3.Entity) entity);
       }

        private static void addToMaps(Class clazz, String name, int id)
        {
            //getPrivateField is the method from above.
            //Remove the lines with // in front of them if you want to override default entities (You'd have to remove the default entity from the map first though).
            ((Map)getPrivateField("c", net.minecraft.server.v1_8_R3.EntityTypes.class, null)).put(name, clazz);
            ((Map)getPrivateField("d", net.minecraft.server.v1_8_R3.EntityTypes.class, null)).put(clazz, name);
            //((Map)getPrivateField("e", net.minecraft.server.v1_7_R4.EntityTypes.class, null)).put(Integer.valueOf(id), clazz);
            ((Map)getPrivateField("f", net.minecraft.server.v1_8_R3.EntityTypes.class, null)).put(clazz, Integer.valueOf(id));
            //((Map)getPrivateField("g", net.minecraft.server.v1_7_R4.EntityTypes.class, null)).put(name, Integer.valueOf(id));
           
           
        }
        public static Object getPrivateField(String fieldName, Class clazz, Object object) {
            Field field;
            Object o = null;

            try
            {
                field = clazz.getDeclaredField(fieldName);

                field.setAccessible(true);

                o = field.get(object);
            }
            catch(NoSuchFieldException e)
            {
                e.printStackTrace();
            }
            catch(IllegalAccessException e)
            {
                e.printStackTrace();
            }

            return o;
        }
    }