232 lines
7.8 KiB
Java
232 lines
7.8 KiB
Java
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;
|
|
}
|
|
|
|
} |