package eu.univento.core.api.fakeplayer; import com.mojang.authlib.GameProfile; import eu.univento.core.Core; import net.minecraft.server.v1_8_R3.*; import org.bukkit.Location; import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitTask; import java.lang.reflect.Field; import java.util.Arrays; /** * @author joethei * @version 0.3 */ public class FakePlayer { private static final double MOVE_SPEED = 4.3D / 20; private Player player; private int entityId; private GameProfile gameProfile; private DataWatcher dataWatcher; private Location location; private ArmorStand armorStand; private LivingEntity target; private BukkitTask task; private boolean moveable; public FakePlayer(GameProfile gameProfile, boolean moveable, Player player) { this.player = player; this.moveable = moveable; this.entityId = (int) get(Entity.class, null, "entityCount"); set(Entity.class, null, "entityCount", this.entityId + 1); this.gameProfile = gameProfile; this.dataWatcher = new DataWatcher(null); byte status = 0; this.dataWatcher.a(0, status); this.dataWatcher.a(10, (byte) 127); this.dataWatcher.a(6, 20F); } private final BukkitRunnable tickTask = new BukkitRunnable() { private int ticksLiving = 0; @Override public void run() { if(target != null) { followEntity(target); ticksLiving++; } } }; private byte changeMask(byte bitMask, int bit, boolean state) { if(state) return bitMask |= 1 << bit; else return bitMask &= ~(1 << bit); } public void setTarget(LivingEntity target) { this.target = target; } public void clearTarget() { this.target = null; } public void setFire(boolean fire) { setStatus(0, fire); } public void setSneaking(boolean sneaking) { setStatus(1, sneaking); } public void setSprinting(boolean sprinting) { setStatus(3, sprinting); } public void setUseItem(boolean useItem) { setStatus(4, useItem); } public void setInvisible(boolean invisible) { setStatus(5, invisible); } private void setStatus(int data, boolean bool) { DataWatcher dataWatcher = this.dataWatcher; byte status = 0; status = changeMask(status, data, bool); dataWatcher.a(0, status); dataWatcher.a(10, (byte) 127); dataWatcher.a(6, 20F); } public void followEntity(LivingEntity entity) { double diffX = entity.getLocation().getX() - location.getX(); double diffY = (entity.getLocation().getY() + entity.getEyeHeight() * 0.9D) - location.getY() + 1.6F; double diffZ = entity.getLocation().getZ() - location.getZ(); double hypoXZ = Math.sqrt(diffX * diffX + diffZ * diffZ); float yaw = (float) (Math.atan2(diffZ, diffX) * 180D / Math.PI) - 90F; float pitch = (float) -(Math.atan2(diffY, hypoXZ) * 18D / Math.PI); look(yaw, pitch); if(moveable) { if(Math.abs(diffX) > 3D || Math.abs(diffZ) > 3D) { yaw = (float) Math.toRadians(yaw); double x = Math.sin(yaw); double z = Math.cos(yaw); move(x * MOVE_SPEED, 0, z * MOVE_SPEED); } } } private void move(double x, int y, double z) { sendPackets(new PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook(this.entityId, (byte) toFixedPointNumber(x), (byte) toFixedPointNumber(y), (byte) toFixedPointNumber(z), toAngle(location.getYaw()), toAngle(location.getPitch()), true)); this.location.add(toFixedPointNumber(x) / 32D, toFixedPointNumber(y) / 32D, toFixedPointNumber(z) / 32D); this.armorStand.teleport(this.location); } public void spawn(Location location) { PacketPlayOutNamedEntitySpawn playerSpawn = new PacketPlayOutNamedEntitySpawn(); set(playerSpawn, "a", this.entityId); set(playerSpawn, "b", this.gameProfile.getId()); set(playerSpawn, "c", toFixedPointNumber(location.getX())); set(playerSpawn, "d", toFixedPointNumber(location.getY())); set(playerSpawn, "e", toFixedPointNumber(location.getZ())); set(playerSpawn, "f", toAngle(location.getYaw())); set(playerSpawn, "g", toAngle(location.getPitch())); set(playerSpawn, "h", 0); set(playerSpawn, "i", this.dataWatcher); PacketPlayOutPlayerInfo playerInfo = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER); set(playerInfo, "b", Arrays.asList(playerInfo.new PlayerInfoData(this.gameProfile, 0, WorldSettings.EnumGamemode.NOT_SET, new ChatComponentText(this.gameProfile.getName())))); sendPackets(playerInfo, playerSpawn); this.location = location; this.armorStand = (ArmorStand) location.getWorld().spawnEntity(location, EntityType.ARMOR_STAND); this.armorStand.setGravity(true); this.armorStand.setVisible(false); this.task = tickTask.runTaskTimer(Core.getInstance(), 0L, 1L); } public void despawn() { PacketPlayOutPlayerInfo playerInfo = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER); set(playerInfo, "b", Arrays.asList(playerInfo.new PlayerInfoData(this.gameProfile, 0, null, null))); sendPackets(new PacketPlayOutEntityDestroy(this.entityId), playerInfo); this.armorStand.remove(); this.armorStand = null; this.task.cancel(); } public void removeTablist() { PacketPlayOutPlayerInfo playerInfo = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER); set(playerInfo, "b", Arrays.asList(playerInfo.new PlayerInfoData(this.gameProfile, 0, null, null))); sendPackets(new PacketPlayOutEntityDestroy(this.entityId), playerInfo); } private void look(float yaw, float pitch) { PacketPlayOutEntityHeadRotation headRotation = new PacketPlayOutEntityHeadRotation(); set(headRotation, "a", this.entityId); set(headRotation, "b", toAngle(yaw)); sendPackets(headRotation, new PacketPlayOutEntity.PacketPlayOutEntityLook(this.entityId, toAngle(yaw), toAngle(pitch), false)); this.location.setYaw(yaw); this.location.setPitch(pitch); } private int toFixedPointNumber(double value) { return (int) Math.floor(value * 32D); } private byte toAngle(float value) { return (byte) ((int) (value * 256.0F / 360.F)); } public void sendPackets(Packet... packets) { for(Packet packet : packets) ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); } private void set(Object instance, String name, Object value) { set(instance.getClass(), instance, name, value); } private void set(Class clazz, Object instance, String name, Object value) { try{ Field field = clazz.getDeclaredField(name); field.setAccessible(true); field.set(instance, value); }catch (Exception e) { e.printStackTrace(); } } private Object get(Object instance, String name) { return get(instance.getClass(), instance, name); } private Object get(Class clazz, Object instance, String name) { try{ Field field = clazz.getDeclaredField(name); field.setAccessible(true); return field.get(instance); }catch (Exception e) { e.printStackTrace(); } return null; } }