package de.oppermann.bastian.spleef.util;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.NotFoundException;
import org.bukkit.Bukkit;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import de.oppermann.bastian.spleef.SpleefMain;
public class PetMaker {
private static boolean nmsFailed = false;
private static Class<?> CLASS_ENTITY_INSENTIENT; // should be final but java sucks
private static Class<?> CLASS_PATHFINDER_GOAL; // should be final but java sucks
private static Class<?> CLASS_PATHFINDER_GOAL_FLOAT; // should be final but java sucks
private static Class<?> CLASS_PATHFINDER_GOAL_SELECTOR; // should be final but java sucks
private static Class<?> CLASS_CRAFT_LIVING_ENTITY; // should be final but java sucks
private static Class<?> CLASS_UNSAFE_LIST; // should be final but java sucks
private static String VERSION; // the craftbukkit version (should also be final but ...)
static {
String path = Bukkit.getServer().getClass().getPackage().getName();
VERSION = path.substring(path.lastIndexOf(".") + 1, path.length());
try {
CLASS_ENTITY_INSENTIENT = Class.forName("net.minecraft.server." + VERSION + ".EntityInsentient");
CLASS_PATHFINDER_GOAL = Class.forName("net.minecraft.server." + VERSION + ".PathfinderGoal");
CLASS_PATHFINDER_GOAL_FLOAT = Class.forName("net.minecraft.server." + VERSION + ".PathfinderGoalFloat");
CLASS_PATHFINDER_GOAL_SELECTOR = Class.forName("net.minecraft.server." + VERSION + ".PathfinderGoalSelector");
CLASS_CRAFT_LIVING_ENTITY = Class.forName("org.bukkit.craftbukkit." + VERSION + ".entity.CraftLivingEntity");
CLASS_UNSAFE_LIST = Class.forName("org.bukkit.craftbukkit." + VERSION + ".util.UnsafeList");
} catch (ClassNotFoundException e) {
// incompatible version
e.printStackTrace();
SpleefMain.getInstance().log(Level.SEVERE, "Could not access NMS classes. Please use a plugin version which is compatible with your server version for full functionality.");
nmsFailed = true;
}
}
private static Class<?> CLASS_PATHFINDER_GOAL_WALK_TO_TILE;
static {
try {
CtClass clazz = ClassPool.getDefault().get("net.minecraft.server." + VERSION + ".PathfinderGoal");
CtClass subClass = ClassPool.getDefault().makeClass("PathfinderGoalWalktoTile", clazz);
CtField entityField = CtField.make("private net.minecraft.server." + VERSION + ".EntityInsentient entity;", subClass);
CtField pathField = CtField.make("private net.minecraft.server." + VERSION + ".PathEntity path;", subClass);
CtField playerField = CtField.make("private org.bukkit.entity.Player p;", subClass);
subClass.addField(entityField);
subClass.addField(pathField);
subClass.addField(playerField);
CtConstructor constructor = CtNewConstructor.make(
"public PathfinderGoalWalktoTile(Object entitycreature, org.bukkit.entity.Player p) { " +
"this.entity = (net.minecraft.server." + VERSION + ".EntityInsentient) entitycreature; " +
"this.p = p; " +
"} "
, subClass);
subClass.addConstructor(constructor);
CtMethod aMethod = CtNewMethod.make(
"public boolean a() { " +
"org.bukkit.block.Block target = null; " +
"try { " +
"target = p.getTargetBlock(null, 20); " +
"} catch (IllegalStateException e) { " +
"target = p.getLocation().getBlock(); " +
"}" +
"org.bukkit.Location targetLocation = target.getLocation(); " +
"targetLocation.setY(p.getLocation().getY()); " +
"if (targetLocation.getWorld() != entity.getBukkitEntity().getWorld()) { " +
"entity.getBukkitEntity().teleport(targetLocation); " +
"return false; " +
"} " +
"if (targetLocation.distanceSquared(entity.getBukkitEntity().getLocation()) > 25*25) { " +
"entity.getBukkitEntity().teleport(targetLocation); " +
"} " +
"boolean flag = this.entity.getNavigation().m(); " +
"this.path = this.entity.getNavigation().a(targetLocation.getX() + 1, targetLocation.getY(), targetLocation.getZ()); " +
"if (this.path != null) { " +
"this.c(); " +
"} " +
"return this.path != null; " +
"} ",
subClass);
subClass.addMethod(aMethod);
CtMethod cMethod = CtNewMethod.make(
"public void c() { " +
"this.entity.getNavigation().a(this.path, 1D); " +
"} "
, subClass);
subClass.addMethod(cMethod);
CLASS_PATHFINDER_GOAL_WALK_TO_TILE = subClass.toClass();
} catch (NotFoundException | CannotCompileException e) {
// incompatible version
e.printStackTrace();
SpleefMain.getInstance().log(Level.SEVERE, "Could not access NMS classes. Please use a plugin version which is compatible with your server version for full functionality.");
nmsFailed = true;
}
}
private static Field gsa;
private static Field goalSelector;
private static Field targetSelector;
static {
try {
gsa = CLASS_PATHFINDER_GOAL_SELECTOR.getDeclaredField("b");
gsa.setAccessible(true);
goalSelector = CLASS_ENTITY_INSENTIENT.getDeclaredField("goalSelector");
goalSelector.setAccessible(true);
targetSelector = CLASS_ENTITY_INSENTIENT.getDeclaredField("targetSelector");
targetSelector.setAccessible(true);
} catch (NoSuchFieldException | SecurityException e) {
// incompatible version
e.printStackTrace();
SpleefMain.getInstance().log(Level.SEVERE, "Could not access NMS classes. Please use a plugin version which is compatible with your server version for full functionality.");
nmsFailed = true;
}
}
public static boolean setGuide(LivingEntity entity, Player guide) {
if (nmsFailed) {
return false;
}
try {
Object nms_entity = CLASS_CRAFT_LIVING_ENTITY.getMethod("getHandle").invoke(entity);
Object goal = goalSelector.get(nms_entity);
Object target = targetSelector.get(nms_entity);
gsa.set(goal, CLASS_UNSAFE_LIST.newInstance());
gsa.set(target, CLASS_UNSAFE_LIST.newInstance());
goal.getClass().getMethod("a", int.class, CLASS_PATHFINDER_GOAL).invoke(goal, 0, CLASS_PATHFINDER_GOAL_FLOAT.getConstructor(CLASS_ENTITY_INSENTIENT).newInstance(nms_entity));
goal.getClass().getMethod("a", int.class, CLASS_PATHFINDER_GOAL).invoke(goal, 1, CLASS_PATHFINDER_GOAL_WALK_TO_TILE.getConstructor(Object.class, Player.class).newInstance(nms_entity, guide));
return true;
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException | InstantiationException e) {
// incompatible version
e.printStackTrace();
SpleefMain.getInstance().log(Level.SEVERE, "Could not access NMS classes. Please use a plugin version which is compatible with your server version for full functionality.");
nmsFailed = true;
return false;
}
}
}