NMS Villager NPC UnMovable Spawning

Discussion in 'Spigot Plugin Development' started by Drank, Jun 23, 2015.

  1. Hi Spigot,

    Recently I made a custom villager with NMS. I made that he cannot move and he is invulnerable. But spawning doesn't work. I don't know why.
    This is my NMS class:
    Code (Text):



    import java.lang.reflect.Field;

    import java.util.List;

    import net.minecraft.server.v1_8_R3.DamageSource;
    import net.minecraft.server.v1_8_R3.EntityHuman;
    import net.minecraft.server.v1_8_R3.EntityVillager;
    import net.minecraft.server.v1_8_R3.PathfinderGoalFloat;
    import net.minecraft.server.v1_8_R3.PathfinderGoalLookAtPlayer;
    import net.minecraft.server.v1_8_R3.PathfinderGoalLookAtTradingPlayer;
    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 net.minecraft.server.v1_8_R3.World;

    @SuppressWarnings("rawtypes")
    public class CustomVillager extends EntityVillager {

            public CustomVillager(World world) {
            super(world);
           
            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(1, new PathfinderGoalLookAtTradingPlayer(this));
            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));
         }
       
         public void m() {
             this.fireProof = true;
             super.m();
         }

         
         @Override
         public boolean damageEntity(DamageSource damagesource, float f) {
             return false;
         }  
       
         @Override
         public void g(double d0, double d1, double d2) {
             return;
         }
       
         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;
        }  
    [B]}


    My spawning CLASS:

    Code (Text):
    import java.util.ArrayList;
    import java.util.List;

    import org.bukkit.ChatColor;
    import org.bukkit.Location;
    import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
    import org.bukkit.entity.Villager;

    public class NPC {
       
        public List<CustomVillager> npcs = new ArrayList<CustomVillager>();
       
        public void spawnNPC(Location loc, String type){
            CustomVillager cv = new CustomVillager(((CraftWorld) loc.getWorld()).getHandle());
            cv.spawnIn(((CraftWorld) loc.getWorld()).getHandle());
            Villager vil = (Villager) cv.getBukkitEntity();
            cv.teleportTo(loc, true);
            if (type.equals("BANKER")){
                vil.setCustomName(ChatColor.GOLD + "Banker");
            } else if (type.equals("CAR_DEALER")){
                vil.setCustomName(ChatColor.GOLD + "Car Dealer");
            } else if (type.equals("GUN_DEALER")){
                vil.setCustomName(ChatColor.GOLD + "Gun Dealer");
            } else if (type.equals("HOME_AGENT")){
                vil.setCustomName(ChatColor.GOLD + "Home Agent");
            } else if (type.equals("STORE")){
                vil.setCustomName(ChatColor.GOLD + "Store");
            }
       
            vil.setCustomNameVisible(true);
            npcs.add(cv);
        }
       
        public void deleteNPC(CustomVillager cv){
            npcs.remove(cv);
           
            Villager vill = (Villager) cv.getBukkitEntity();
            vill.remove();
        }
       

    }
     
    [/B]

    THANKS FOR HELPING! <3
     
  2. The problem is that you need to register your custom entity in minecraft before you can use it. Registering isn't that hard, you only have to use one method. Normally I wouldn't spoon feed people, but this is a bit hard to explain, so this is the code to register:
    Code (Text):
    Class<EntityTypes> entityTypeClass = EntityTypes.class;

                Field c = entityTypeClass.getDeclaredField("c");
                c.setAccessible(true);
                HashMap<String, Class<?>> c_map = (HashMap) c.get(null);
                c_map.put("pvpdragon", Dragon.class);

                Field d = entityTypeClass.getDeclaredField("d");
                d.setAccessible(true);
                HashMap<Class<?>, String> d_map = (HashMap) d.get(null);
                d_map.put(Dragon.class, "pvpdragon");

                Field e = entityTypeClass.getDeclaredField("e");
                e.setAccessible(true);
                HashMap<Integer, Class<?>> e_map = (HashMap) e.get(null);
                e_map.put(Integer.valueOf(63), Dragon.class);

                Field f = entityTypeClass.getDeclaredField("f");
                f.setAccessible(true);
                HashMap<Class<?>, Integer> f_map = (HashMap) f.get(null);
                f_map.put(Dragon.class, Integer.valueOf(63));

                Field g = entityTypeClass.getDeclaredField("g");
                g.setAccessible(true);
                HashMap<String, Integer> g_map = (HashMap) g.get(null);
                g_map.put("pvpdragon", Integer.valueOf(63));
    You need to change "pvpdragon" with the name of your custom entity, you need to change Dragon.class with your custom entity class, and you need to change the 63 with the entity id of the entity that you made your custom version, so the entity id of a villager.
     
  3. Thanks for you reply, think this will work. But where do I need to put it? In the constructor of the CustomVillager class or in the Main class?!

    Reflection is awesome, but hard.
     
  4. This needs to be called before any instances has been made of the entity class. I personally put it in a static method in the same class as the spawn method, and call the register method in the onEnable() of the plugin class.
     
  5. @bram0101 It doesn't work. Here is my spawning class:
    Code (Text):
       
        public List<CustomVillager> npcs = new ArrayList<CustomVillager>();
       
        public void spawnNPC(Location loc, String type){
            CustomVillager cv = new CustomVillager(((CraftWorld) loc.getWorld()).getHandle());
            cv.setLocation(loc.getX(), loc.getX(), loc.getZ(), loc.getYaw(), loc.getPitch());
            ((LivingEntity) cv.getBukkitEntity()).setRemoveWhenFarAway(false);
            Villager vil = (Villager) cv.getBukkitEntity();
            if (type.equals("BANKER")){
                vil.setCustomName(ChatColor.GOLD + "Banker");
            } else if (type.equals("CAR_DEALER")){
                vil.setCustomName(ChatColor.GOLD + "Car Dealer");
            } else if (type.equals("GUN_DEALER")){
                vil.setCustomName(ChatColor.GOLD + "Gun Dealer");
            } else if (type.equals("HOME_AGENT")){
                vil.setCustomName(ChatColor.GOLD + "Home Agent");
            } else if (type.equals("STORE")){
                vil.setCustomName(ChatColor.GOLD + "Store");
            }
       
            vil.setCustomNameVisible(true);
            ((CraftWorld) loc.getWorld()).getHandle().addEntity(cv, SpawnReason.CUSTOM);
            npcs.add(cv);
        }
       
        public void deleteNPC(CustomVillager cv){
            npcs.remove(cv);
           
            Villager vill = (Villager) cv.getBukkitEntity();
            vill.remove();
        }
       
        @SuppressWarnings({ "rawtypes", "unchecked" })
        public static void registerNPC(){
            Class<EntityTypes> entityTypeClass = EntityTypes.class;
       
            try {
                Field c = entityTypeClass.getDeclaredField("c");
                c.setAccessible(true);
                HashMap<String, Class<?>> c_map = (HashMap) c.get(null);
                c_map.put("villager", CustomVillager.class);

                Field d = entityTypeClass.getDeclaredField("d");
                d.setAccessible(true);
                HashMap<Class<?>, String> d_map = (HashMap) d.get(null);
                d_map.put(CustomVillager.class, "villager");

                Field e = entityTypeClass.getDeclaredField("e");
                e.setAccessible(true);
                HashMap<Integer, Class<?>> e_map = (HashMap) e.get(null);
                e_map.put(Integer.valueOf(120), CustomVillager.class);

                Field f = entityTypeClass.getDeclaredField("f");
                f.setAccessible(true);
                HashMap<Class<?>, Integer> f_map = (HashMap) f.get(null);
                f_map.put(CustomVillager.class, Integer.valueOf(120));

                Field g = entityTypeClass.getDeclaredField("g");
                g.setAccessible(true);
                HashMap<String, Integer> g_map = (HashMap) g.get(null);
                g_map.put("villager", Integer.valueOf(120));
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
       
     
     
  6. What you could do is instead of multiple catch statements, just use one and as Exception just use Exception. And then add this in the catch for if it failes.
    Code (Text):
    Field d;
                int d_map;
                Method[] e;
                Class[] paramTypes = { Class.class, String.class, Integer.TYPE };
                try {
                    Method method = EntityTypes.class.getDeclaredMethod("addMapping", paramTypes);
                    method.setAccessible(true);
                } catch (Exception ex) {
                    exc.addSuppressed(ex);
                    try {
                        d_map = (e = EntityTypes.class.getDeclaredMethods()).length;
                        for (int d1 = 0; d1 < d_map; d1++) {
                            Method method = e[d1];
                            if (Arrays.equals(paramTypes, method.getParameterTypes())) {
                                method.invoke(null, new Object[] { Dragon.class, "pvpdragon", Integer.valueOf(63) });
                            }
                        }
                    } catch (Exception exe) {
                        exc.addSuppressed(exe);
                    }
                    exc.printStackTrace();
                }
    Here you also need to change some of those things.
     
  7. Thanks for your help! I managed it myself <3
     
  8. ok! If it's fixed, adding in front of the title that it's solved is useful to indicate other people.