[Tutorial] Creating custom entities with PathfinderGoals

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

  1. Sorry if this has already been asked, but does this tutorial work with Spigot 1.8?
     
  2. It works for me.
     
  3. Yeah it works, sorry should have posted that here. I successfully made a plugin using a combination of it and some other tutorials. The PathFinder part certainly works.
     
    • Like Like x 1
  4. NMS custom entities seems to be the hardest aspect of plugin creation. I'm excited to try this out later! Thanks
     
  5. Bumping an old thread, is this method now deprecated?

    The spawnEntity specifically here doesn't seem to work for me:

    Code (Text):
    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);
        }
     
    Not giving me the option of setLocation.
     
  6. You are using the wrong class.
     
  7. Yep, noticed that 4 mins after the post. Thanks anyway!
     
  8. Going to ask one more question, is there any way to set a player as a passenger?

    I know it's possible with the normal entity but not with the new one.

    Edit: Fixed it.
     
    #69 Bladian, May 2, 2015
    Last edited: May 2, 2015
  9. New problem: No matter what I do it always spawns the same entity, even if I change it.

    I'm not sure if this is a result of the enum CUSTOM_CHICKEN being unused. Any help?

    Here is the class:

    Code (Text):
    public enum EntityTypes {

        CUSTOM_CHICKEN("Chicken", 93, CustomChicken.class);


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

        public static void spawnEntity(Entity entity, Location loc, Player p) {
            entity.setLocation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
            ((CraftWorld) loc.getWorld()).getHandle().addEntity(entity);
            org.bukkit.entity.Entity animal = entity.getBukkitEntity();
            animal.setPassenger(p);
        }

        private static void addToMaps(Class clazz, String name, int id)
        {

            ((Map)getPrivateField("c", net.minecraft.server.v1_8_R2.EntityTypes.class, null)).put(name, clazz);
            ((Map)getPrivateField("d", net.minecraft.server.v1_8_R2.EntityTypes.class, null)).put(clazz, name);
            ((Map)getPrivateField("e", net.minecraft.server.v1_8_R2.EntityTypes.class, null)).put(Integer.valueOf(id), clazz);
            ((Map)getPrivateField("f", net.minecraft.server.v1_8_R2.EntityTypes.class, null)).put(clazz, Integer.valueOf(id));
            ((Map)getPrivateField("g", net.minecraft.server.v1_8_R2.EntityTypes.class, null)).put(name, Integer.valueOf(id));
        }
    }
    Custom chicken enum remains unused, any ideas why?
     
  10. And I get this error:

    Code (Text):
    [21:24:01 WARN]: java.lang.NoSuchMethodException: me.mounts.mounts.CustomChicken.<init>(net.minecraft.server.v1_8_R2.World)
    [21:24:01 WARN]:     at java.lang.Class.getConstructor0(Class.java:2763)
    [21:24:01 WARN]:     at java.lang.Class.getConstructor(Class.java:1693)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.EntityTypes.a(SourceFile:182)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.ChunkRegionLoader.loadEntities(ChunkRegionLoader.java:410)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.chunkio.ChunkIOProvider.callStage2(ChunkIOProvider.java:46)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.chunkio.ChunkIOProvider.callStage2(ChunkIOProvider.java:1)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.util.AsynchronousExecutor$Task.finish(AsynchronousExecutor.java:188)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.util.AsynchronousExecutor.finishActive(AsynchronousExecutor.java:347)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.chunkio.ChunkIOExecutor.tick(ChunkIOExecutor.java:34)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.MinecraftServer.A(MinecraftServer.java:729)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.DedicatedServer.A(DedicatedServer.java:368)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.MinecraftServer.z(MinecraftServer.java:651)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.MinecraftServer.run(MinecraftServer.java:554)
    [21:24:01 WARN]:     at java.lang.Thread.run(Thread.java:695)
    [21:24:01 WARN]: Skipping Entity with id Chicken
    [21:24:01 WARN]: java.lang.NoSuchMethodException: me.mounts.mounts.CustomChicken.<init>(net.minecraft.server.v1_8_R2.World)
    [21:24:01 WARN]:     at java.lang.Class.getConstructor0(Class.java:2763)
    [21:24:01 WARN]:     at java.lang.Class.getConstructor(Class.java:1693)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.EntityTypes.a(SourceFile:182)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.ChunkRegionLoader.loadEntities(ChunkRegionLoader.java:410)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.chunkio.ChunkIOProvider.callStage2(ChunkIOProvider.java:46)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.chunkio.ChunkIOProvider.callStage2(ChunkIOProvider.java:1)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.util.AsynchronousExecutor$Task.finish(AsynchronousExecutor.java:188)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.util.AsynchronousExecutor.finishActive(AsynchronousExecutor.java:347)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.chunkio.ChunkIOExecutor.tick(ChunkIOExecutor.java:34)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.MinecraftServer.A(MinecraftServer.java:729)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.DedicatedServer.A(DedicatedServer.java:368)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.MinecraftServer.z(MinecraftServer.java:651)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.MinecraftServer.run(MinecraftServer.java:554)
    [21:24:01 WARN]:     at java.lang.Thread.run(Thread.java:695)
    [21:24:01 WARN]: Skipping Entity with id Chicken
    [21:24:01 WARN]: java.lang.NoSuchMethodException: me.mounts.mounts.CustomChicken.<init>(net.minecraft.server.v1_8_R2.World)
    [21:24:01 WARN]:     at java.lang.Class.getConstructor0(Class.java:2763)
    [21:24:01 WARN]:     at java.lang.Class.getConstructor(Class.java:1693)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.EntityTypes.a(SourceFile:182)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.ChunkRegionLoader.loadEntities(ChunkRegionLoader.java:410)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.chunkio.ChunkIOProvider.callStage2(ChunkIOProvider.java:46)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.chunkio.ChunkIOProvider.callStage2(ChunkIOProvider.java:1)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.util.AsynchronousExecutor$Task.finish(AsynchronousExecutor.java:188)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.util.AsynchronousExecutor.finishActive(AsynchronousExecutor.java:347)
    [21:24:01 WARN]:     at org.bukkit.craftbukkit.v1_8_R2.chunkio.ChunkIOExecutor.tick(ChunkIOExecutor.java:34)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.MinecraftServer.A(MinecraftServer.java:729)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.DedicatedServer.A(DedicatedServer.java:368)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.MinecraftServer.z(MinecraftServer.java:651)
    [21:24:01 WARN]:     at net.minecraft.server.v1_8_R2.MinecraftServer.run(MinecraftServer.java:554)
    [21:24:01 WARN]:     at java.lang.Thread.run(Thread.java:695)
    [21:24:01 WARN]: Skipping Entity with id Chicken
     
  11. Are you changing the entity ID and class?
     
  12. Make sure your entity has a constructor which takes the net.minecraft.server.World as parameter.
     
  13. I'll show you all my classes:

    Entity Class:

    Code (Text):
    package me.mounts.mounts;

    /**
    * Created by maxmigliorini on 02/05/15.
    */

    import net.minecraft.server.v1_8_R2.*;
    import org.bukkit.craftbukkit.v1_8_R2.CraftWorld;

    import java.lang.reflect.Field;

    public class CustomChicken extends EntityChicken {


        public CustomChicken(org.bukkit.World world) {
            super(((CraftWorld) world).getHandle());

            this.goalSelector.a(0, new PathfinderGoalFloat(this));
        }

        @Override
        public void g(float f, float f1) {
            if (this.passenger != null && this.passenger instanceof EntityHuman) {
                this.lastYaw = this.yaw = this.passenger.yaw;
                this.pitch = this.passenger.pitch * 0.5F;
                this.setYawPitch(this.yaw, this.pitch);
                this.aK = this.aI = this.yaw;
                f = ((EntityLiving) this.passenger).aZ * 0.5F;
                f1 = ((EntityLiving) this.passenger).ba;
                if (f1 <= 0.0F) {
                    f1 *= 0.25F;
                }
                Field jump = null;
                try {
                    jump = EntityLiving.class.getDeclaredField("aY");
                } catch (NoSuchFieldException e1) {
                    jump.setAccessible(true);
                    if (jump != null) {
                        try {
                            if (!jump.getBoolean(this.passenger)) {
                            } else {
                                double jumpHeight = 0.5D;
                                this.motY = jumpHeight;
                            }
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                        this.S = 1.0F;
                        this.aM = this.bI() * 0.1F;
                        if (!this.world.isClientSide) {
                            this.k((float) this.getAttributeInstance(GenericAttributes.d).getValue());
                            super.g(f, f1);
                        }

                        this.aA = this.aB;
                        double d0 = this.locX - this.lastX;
                        double d1 = this.locZ - this.lastZ;
                        float f4 = MathHelper.sqrt(d0 * d0 + d1 * d1) * 4.0F;
                        if (f4 > 1.0F) {
                            f4 = 1.0F;
                        }

                        this.aB += (f4 - this.aB) * 0.4F;
                        this.aC += this.aB;
                    } else {
                        this.S = 0.5F;
                        this.aM = 0.02F;
                        super.g(f, f1);
                    }

                }
            }
        }
    }
    The Utils Class:

    Code (Text):
    package me.mounts.mounts;

    import java.lang.reflect.Field;

    /**
    * Created by maxmigliorini on 02/05/15.
    */
    public class Utils {

        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;
        }
    }
    The Entity Types Enum:

    Code (Text):
    package me.mounts.mounts;

    import net.minecraft.server.v1_8_R2.Entity;
    import org.bukkit.Location;
    import org.bukkit.craftbukkit.v1_8_R2.CraftWorld;
    import org.bukkit.entity.Player;

    import java.util.Map;

    import static me.mounts.mounts.Utils.getPrivateField;

    /**
    * Created by maxmigliorini on 02/05/15.
    */

    public enum EntityTypes {

        CUSTOM_CHICKEN("Chicken", 93, CustomChicken.class);


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

        public static void spawnEntity(Entity entity, Location loc, Player p) {
            entity.setLocation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
            ((CraftWorld) loc.getWorld()).getHandle().addEntity(entity);
            org.bukkit.entity.Entity animal = entity.getBukkitEntity();
            animal.setPassenger(p);
        }

        private static void addToMaps(Class clazz, String name, int id)
        {

            ((Map)getPrivateField("c", net.minecraft.server.v1_8_R2.EntityTypes.class, null)).put(name, clazz);
            ((Map)getPrivateField("d", net.minecraft.server.v1_8_R2.EntityTypes.class, null)).put(clazz, name);
            ((Map)getPrivateField("e", net.minecraft.server.v1_8_R2.EntityTypes.class, null)).put(Integer.valueOf(id), clazz);
            ((Map)getPrivateField("f", net.minecraft.server.v1_8_R2.EntityTypes.class, null)).put(clazz, Integer.valueOf(id));
            ((Map)getPrivateField("g", net.minecraft.server.v1_8_R2.EntityTypes.class, null)).put(name, Integer.valueOf(id));
        }
    }
     
  14. Change this:
    Code (Text):
        public CustomChicken(org.bukkit.World world) {
            super(((CraftWorld) world).getHandle());

            this.goalSelector.a(0, new PathfinderGoalFloat(this));
        }
    To this:
    Code (Text):
        public CustomChicken(net.minecraft.server.v1_8_R2.World world)
        {
            super(world);
            this.goalSelector.a(0, new PathfinderGoalFloat(this));
        }

        public CustomChicken(org.bukkit.World world)
        {
            this(((CraftWorld)world).getHandle());
        }
    Also you said it always spawns the same entity, which entity?
     
  15. A chicken that never every moves. It basically stays still no matter what.

    The entity does spawn but it's a one that follows my direction, but doesn't move.
     
  16. Oh my.... I'll be using this in a ton of plugins... Thanks!
     
  17. Is the entity enum supposed to be unused?
     
  18. Really nice tutorial. Good work. :)
     
  19. Yes it's just there to call the constructor and register the entity. This could be done on several other ways but this just seemed the cleanest.