package com.supaham.commons.bukkit.utils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* Utility methods for working with {@code java.reflect}. This class contains methods such as
* {@link #getServerVersion()}, and more, as well as the {@link PackageType} class for version
* packages such {@code nms} and {@code obc}.
*
* @since 0.1
*/
public class ReflectionUtils extends com.supaham.commons.utils.ReflectionUtils {
private static final Map<String, Class> classes = new HashMap<>();
private static final Map<String, Constructor> constructors = new HashMap<>();
private static final Map<String, Method> methods = new HashMap<>();
private static final Map<String, Field> fields = new HashMap<>();
// This cannot be done in a static block as PackageType needs to initialize ReflectionUtils first.
private static void init() throws Exception {
PackageType nms = PackageType.MINECRAFT_SERVER;
PackageType obc = PackageType.CRAFTBUKKIT;
classes.put("entityPlayer", nms.getClassSafe("EntityPlayer"));
fields.put("playerConnection", getField(classes.get("entityPlayer"), "playerConnection"));
methods.put("sendPacket", getMethod(fields.get("playerConnection").getType(), "sendPacket"));
Class<?> clazz = nms.getClassSafe("Vec3D");
classes.put("Vec3D", clazz);
constructors.put("Vec3D",
clazz.getDeclaredConstructor(double.class, double.class, double.class));
fields.put("Vec3Dx", getField(clazz, "x"));
fields.put("Vec3Dy", getField(clazz, "y"));
fields.put("Vec3Dz", getField(clazz, "z"));
classes.put("BlockPosition", nms.getClassSafe("BlockPosition"));
clazz = nms.getClassSafe("BaseBlockPosition");
fields.put("BaseBlockPositionx", getField(clazz, "a"));
fields.put("BaseBlockPositiony", getField(clazz, "b"));
fields.put("BaseBlockPositionz", getField(clazz, "c"));
}
public static void sendPacket(Player player, Object object) {
try {
Object connection = fields.get("playerConnection").get(getHandle(player));
methods.get("sendPacket").invoke(connection, object);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
public static Object toNMSVec3D(Vector vector) {
Class<?> vec3D = PackageType.MINECRAFT_SERVER.getClassSafe("Vec3D");
try {
Constructor<?> ctor = constructors.get("Vec3D");
return ctor.newInstance(vector.getX(), vector.getY(), vector.getZ());
} catch (InvocationTargetException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
return null;
}
}
public static Vector fromNMSVec3D(Object object) {
if (!object.getClass().equals(PackageType.MINECRAFT_SERVER.getClassSafe("Vec3D"))) {
return null;
}
try {
double x = (double) fields.get("Vec3Dx").get(object);
double y = (double) fields.get("Vec3Dy").get(object);
double z = (double) fields.get("Vec3Dz").get(object);
return new Vector(x, y, z);
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
public static Vector fromNMSBlockPosition(Object object) {
if (!object.getClass().equals(PackageType.MINECRAFT_SERVER.getClassSafe("BlockPosition"))) {
return null;
}
try {
int x = (int) fields.get("BaseBlockPositionx").get(object);
int y = (int) fields.get("BaseBlockPositiony").get(object);
int z = (int) fields.get("BaseBlockPositionz").get(object);
return new Vector(x, y, z);
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
public static Entity getBukkitEntity(Object obj) {
try {
return (Entity) getMethod(obj.getClass(), "getBukkitEntity").invoke(obj);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String getServerVersion() {
// org.bukkit.craftbukkit.v1_7_R4.entity
// org.bukkit.craftbukkit.v1_8_R2.entity;
return Bukkit.getServer().getClass().getPackage().getName().substring(23);
}
public static boolean isServer18OrHigher() {
String ver = getServerVersion();
return !ver.startsWith("v1_7");
}
public static Class<?> getNMSClass(String className) {
return PackageType.MINECRAFT_SERVER.getClassSafe(className);
}
public static Class<?> getOBCClass(String className) {
return PackageType.CRAFTBUKKIT.getClassSafe(className);
}
public static Object getHandle(Object obj) {
try {
return getMethod(obj.getClass(), "getHandle").invoke(obj);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* An enumeration with versioned packages such as {@code nms} and {@code obc}.
*/
public enum PackageType {
/**
* {@code net.minecraft.server.server_version} package.
*/
MINECRAFT_SERVER("net.minecraft.server." + getServerVersion()),
/**
* {@code org.bukkit.craftbukkit.server_version} package.
*/
CRAFTBUKKIT("org.bukkit.craftbukkit." + getServerVersion()),;
private final String path;
static {
try {
ReflectionUtils.init();
} catch (Exception e) {
e.printStackTrace();
}
}
PackageType(String path) {
this.path = path;
}
@Override
public String toString() {
return this.path;
}
public Class<?> getClass(String className) throws ClassNotFoundException {
return Class.forName(this + "." + className);
}
public Class<?> getClassSafe(String className) {
try {
return Class.forName(this + "." + className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
public String getPath() {
return path;
}
}
}