Armor Stand Follow Player

Discussion in 'Spigot Plugin Development' started by TheFancyWhale, Apr 2, 2015.

  1. I want the armor stand to mimic the player's moves exactly. Any ideas? Also, it cannot be by setting the passenger of the player. Here is what I have tried:
    Code (Text):
    @EventHandler
        public void onMove(PlayerMoveEvent event){
            if (!map.containsKey(event.getPlayer())) return;
            map.get(event.getPlayer()).setVelocity(event.getPlayer().getVelocity());
        }
    This method seems to only set the armor stand's velocity in the y direction for some reason.
    Other method:
    Code (Text):
    @EventHandler
        public void onMove(PlayerMoveEvent event){
            if (!map.containsKey(event.getPlayer())) return;
       
            map.get(event.getPlayer()).teleport(event.getPlayer());

        }
    Works, but it lags too much. The armor stand isn't instantly at the player so it does not exactly mimic.

    Any ideas are greatly appreciated!
     
  2. What you could do is creating a custom nms entity that teleports itself automatically to the player on the update. This way it lags less and you can customize the movement a bit better.

    I've looked into the source of spigot 1.8.3 and a project of mine. And I created a custom nms entity class that you could use. You still need to change a phew things!

    This should work but it's not tested. So if it fails say it, because it works when implemented on my own type of entity.
    Code (Text):
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.UUID;

    import net.minecraft.server.v1_8_R2.EntityArmorStand;
    import net.minecraft.server.v1_8_R2.EntityPlayer;
    import net.minecraft.server.v1_8_R2.EntityTypes;
    import net.minecraft.server.v1_8_R2.Vector3f;
    import net.minecraft.server.v1_8_R2.World;

    import org.bukkit.Bukkit;
    import org.bukkit.craftbukkit.v1_8_R2.CraftWorld;
    import org.bukkit.craftbukkit.v1_8_R2.entity.CraftPlayer;
    import org.bukkit.entity.Player;
    import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;

    public class EntityArmorStandCustom extends EntityArmorStand {

        // Using an UUID to store the player. Storing a player instance is not
        // recommended
        public UUID player;

        public EntityArmorStandCustom(World world, Player p) {
            super(world);
            player = p.getUniqueId();
        }

        // This is the update method of the entity
        @Override
        public void m() {

            // Getting the nms class of the player to have easier access to some
            // data.
            EntityPlayer p = ((CraftPlayer) Bukkit.getPlayer(player)).getHandle();
            // Setting the position
            setPosition(p.locX, p.locY, p.locZ);
            //Setting the yaw and pitch of the armor stand. If it fails we get another change to set it with the codes below.
            this.yaw = p.yaw;
            this.pitch = p.pitch;
            // Setting the yaw of the armor stand. I don't know for sure if this
            // works.
            setBodyPose(new Vector3f(0.0F, p.yaw, 0.0F));
            // Setting the pitch of the head. I also don't know for sure if this
            // works.
            setHeadPose(new Vector3f(p.pitch, 0.0F, 0.0F));

            // If you want to still do the normal entity update instead of just
            // standing totally still. Use this line.
            // super.m();
        }

        // This method has to be called to spawn a new instance of the armor stand.
        public static EntityArmorStandCustom SPAWN(Player p) {
            // Getting the nms world to add the entity
            net.minecraft.server.v1_8_R2.World w = ((CraftWorld) p.getWorld())
                    .getHandle();
            // Creating the new instance of the entity
            EntityArmorStandCustom armorStand = new EntityArmorStandCustom(w, p);
            // Adding the entity to the world
            w.addEntity(armorStand, SpawnReason.CUSTOM);
            armorStand.setLocation(p.getLocation().getX(), p.getLocation().getY(),
                    p.getLocation().getZ(), p.getLocation().getYaw(), p
                            .getLocation().getPitch());
            // Returning the armor stand instance. If you want to get the Bukkit api
            // for the entity you can use getEntity() method.
            return armorStand;
        }

        // This method has to be called on onEnable() in the main plugin class! This
        // has to be called to add the entity in the registry of types to prefent
        // crashing.
        public static void registerEntity() {
            try {
                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("customArmorStand", EntityArmorStandCustom.class);

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

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

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

                Field g = entityTypeClass.getDeclaredField("g");
                g.setAccessible(true);
                HashMap<String, Integer> g_map = (HashMap) g.get(null);
                g_map.put("customArmorStand", Integer.valueOf(63));

            } catch (Exception exc) {
                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[] {
                                        EntityArmorStandCustom.class,
                                        "customArmorStand", Integer.valueOf(63) });
                            }
                        }
                    } catch (Exception exe) {
                        exc.addSuppressed(exe);
                    }
                    exc.printStackTrace();
                }
            }
        }
    }
     
     
    • Like Like x 1
  3. Thanks for the suggestion but this doesn't seem to work for me. Nothing happens when I call the spawn method.
     
  4. Have you called the registerEntity in the onEnable method in your main plugin class. Else it indeed wont work
     
  5. Yes I did.
     
    • Like Like x 1
  6. Just a quick heads up for everyone reading this, the code provided by bram0101 works perfectly fine for me on a 1.10 spigot server.
    I did not change anything in the code, apart from the imports (Import from NMS v1.10 rather than v1.8).
     
    • Optimistic Optimistic x 2
  7. Update his location every tick
     
  8. I don't really know what you mean with this, but the armor stand updates it's location when it's update function is called. The updateAll function updates all known armorstands, so you have to make your own schedular that calls this method every tick.
     
  9. when i use this the armorstand is invisible?