Last active
August 29, 2015 14:26
-
-
Save ConnorChristie/e5b3d3f797b38e55bc3a to your computer and use it in GitHub Desktop.
NMS Helper class for Bukkit!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package me.chiller.blockparticles.util; | |
import java.lang.reflect.Constructor; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import org.bukkit.Bukkit; | |
public class NMSHelper | |
{ | |
private static String version = ""; | |
private static Map<String, Class<?>> classes = new HashMap<String, Class<?>>(); | |
static | |
{ | |
version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; | |
} | |
/** | |
* Imports the specified class, if found, then returns a Java class object for the specified class name. | |
* | |
* @param name The fully qualified class name including the package name | |
* @return The class, if found, otherwise throws exception | |
* @throws ClassNotFoundException | |
*/ | |
public static Class<?> importClass(String name) throws ClassNotFoundException | |
{ | |
Class<?> realClass = getClassFromName(name); | |
classes.put(name, realClass); | |
return realClass; | |
} | |
/** | |
* Returns a Java class object for the specified class name. | |
* | |
* @param name The class name, either fully qualified or just the simple name | |
* @return The class, if found, otherwise throws exception | |
* @throws Exception | |
*/ | |
public static Class<?> getClass(String name) throws Exception | |
{ | |
for (String clazz : classes.keySet()) | |
{ | |
if (clazz.toLowerCase().contains(name.toLowerCase())) | |
{ | |
return classes.get(clazz); | |
} | |
} | |
throw new Exception("Class " + name + " not found, try including the package name, for instance: net.minecraft.server._version_." + name); | |
} | |
/** | |
* Creates an instance builder for the specified class | |
* | |
* @param clazz The class's name | |
* @return The created instance builder | |
* @throws Exception | |
*/ | |
public static InstanceBuilder buildInstance(String clazz) throws Exception | |
{ | |
return buildInstance(getClass(clazz)); | |
} | |
/** | |
* Creates an instance builder for the specified class | |
* | |
* @param clazz The class object | |
* @return The created instance builder | |
* @throws Exception | |
*/ | |
public static InstanceBuilder buildInstance(Class<?> clazz) | |
{ | |
return new InstanceBuilder(clazz); | |
} | |
/** | |
* Creates a method builder instance for a static method | |
* | |
* @param clazz The class's name | |
* @return The created method builder | |
* @throws Exception | |
*/ | |
public static MethodBuilder buildStaticMethod(String clazz) throws Exception | |
{ | |
return buildStaticMethod(getClass(clazz)); | |
} | |
/** | |
* Creates a method builder instance for a static method | |
* | |
* @param clazz The class object | |
* @return The created method builder | |
*/ | |
public static MethodBuilder buildStaticMethod(Class<?> clazz) | |
{ | |
return new MethodBuilder(clazz); | |
} | |
/** | |
* Creates a method builder instance for a method | |
* | |
* @param instance The object the method is going to be called on | |
* @return The created method builder | |
*/ | |
public static MethodBuilder buildMethod(Object instance) | |
{ | |
return new MethodBuilder(instance); | |
} | |
public static class InstanceBuilder | |
{ | |
private List<VersionMethod> versionInstances = new ArrayList<VersionMethod>(); | |
private Class<?> clazz; | |
protected InstanceBuilder(Class<?> clazz) | |
{ | |
this.clazz = clazz; | |
} | |
/** | |
* Adds an instance to the instance builder for the specified version | |
* Your plugin will instantiate this instance if the bukkit version matches | |
* | |
* @param version Bukkit version for the method to be called on | |
* @param argTypes Optional, argument types for the instance | |
* @return The current instance builder | |
*/ | |
public InstanceBuilder addVersionInstance(String version, Class<?>... argTypes) | |
{ | |
versionInstances.add(new VersionMethod(version, "instance", argTypes)); | |
return this; | |
} | |
/** | |
* Adds an instance to the instance builder for any version | |
* Your plugin will instantiate this instance if you did not add a version instance for the bukkit version | |
* | |
* @param argTypes Optional, argument types for the instance | |
* @return The current instance builder | |
*/ | |
public InstanceBuilder addUniversalInstance(String method, Class<?>... argTypes) | |
{ | |
versionInstances.add(new VersionMethod("*", method, argTypes)); | |
return this; | |
} | |
/** | |
* Adds the specified arguments for the specified version | |
* If you use this, you do not have to specify arguments in InstanceBuilder.execute(); | |
* | |
* @param version Bukkit version of the method for the arguments to be added to | |
* @param args The arguments for the instance | |
* @return The current instance builder | |
*/ | |
public InstanceBuilder addArguments(String version, Object... args) | |
{ | |
VersionMethod versionMethod = getInstanceForVersion(version); | |
versionMethod.addArgs(args); | |
return this; | |
} | |
public Object newInstance(Object... args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException | |
{ | |
VersionMethod versionMethod = getInstanceForVersion(version); | |
Object[] usingArgs = args.length == 0 ? versionMethod.getArgs() : args; | |
Class<?>[] argTypes = versionMethod.hasArgTypes() ? versionMethod.getArgTypes() : getArgTypes(usingArgs); | |
Constructor<?> constructor = clazz.getConstructor(argTypes); | |
return constructor.newInstance(usingArgs); | |
} | |
public MethodBuilder getMethodBuilder(Object... args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException | |
{ | |
return new MethodBuilder(newInstance(args)); | |
} | |
private VersionMethod getInstanceForVersion(String version) | |
{ | |
for (VersionMethod vMethod : versionInstances) | |
{ | |
if (vMethod.isVersion(version)) | |
{ | |
return vMethod; | |
} | |
} | |
for (VersionMethod vMethod : versionInstances) | |
{ | |
if (vMethod.isUniversal()) | |
{ | |
return vMethod; | |
} | |
} | |
return null; | |
} | |
} | |
public static class MethodBuilder | |
{ | |
private List<VersionMethod> versionMethods = new ArrayList<VersionMethod>(); | |
private Class<?> clazz; | |
private Object instance = null; | |
protected MethodBuilder(Class<?> clazz) | |
{ | |
this.clazz = clazz; | |
} | |
protected MethodBuilder(Object instance) | |
{ | |
this.instance = instance; | |
this.clazz = instance != null ? instance.getClass() : null; | |
} | |
/** | |
* Adds a method to the method builder for the specified version | |
* Your plugin will call this method if the bukkit version matches | |
* | |
* @param version Bukkit version for the method to be called on | |
* @param method Method to be called | |
* @param argTypes Optional, argument types for the method | |
* @return The current method builder | |
*/ | |
public MethodBuilder addVersionMethod(String version, String method, Class<?>... argTypes) | |
{ | |
versionMethods.add(new VersionMethod(version, method, argTypes)); | |
return this; | |
} | |
/** | |
* Adds a method to the method builder for any version | |
* Your plugin will call this method if you did not add a version method for the bukkit version | |
* | |
* @param method Method to be called | |
* @param argTypes Optional, argument types for the method | |
* @return The current method builder | |
*/ | |
public MethodBuilder addUniversalMethod(String method, Class<?>... argTypes) | |
{ | |
versionMethods.add(new VersionMethod("*", method, argTypes)); | |
return this; | |
} | |
/** | |
* Adds the specified arguments for the specified version | |
* If you use this, you do not have to specify arguments in MethodBuilder.execute(); | |
* | |
* @param version Bukkit version of the method for the arguments to be added to | |
* @param args The arguments for the method | |
* @return The current method builder | |
*/ | |
public MethodBuilder addArguments(String version, Object... args) | |
{ | |
VersionMethod versionMethod = getMethodForVersion(version); | |
versionMethod.addArgs(args); | |
return this; | |
} | |
/** | |
* Executes the method for the current version of Bukkit running | |
* Arguments are optional, not needed if MethodBuilder.addArguments() was called | |
* | |
* @param args Optional, the arguments for the method | |
* @return Called method's return, null if method is of void type | |
* @throws NoSuchMethodException | |
* @throws IllegalAccessException | |
* @throws IllegalArgumentException | |
* @throws InvocationTargetException | |
*/ | |
public Object execute(Object... args) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException | |
{ | |
VersionMethod method = getMethodForVersion(version); | |
if (method == null) | |
{ | |
throw new NoSuchMethodException("The developer of this plugin does not support your version: " + version + ", contact the developer to update the plugin."); | |
} | |
Object[] usingArgs = args.length == 0 ? method.getArgs() : args; | |
Class<?>[] argTypes = method.hasArgTypes() ? method.getArgTypes() : getArgTypes(usingArgs); | |
Method m = getMethod(clazz, method.getMethod(), argTypes); | |
if (m != null) | |
{ | |
return m.invoke(instance, usingArgs); | |
} | |
throw new NoSuchMethodException("Method " + method + "(" + argTypesToString(argTypes) + ") not found in any imported classes, are you sure you have the right arg types?"); | |
} | |
private VersionMethod getMethodForVersion(String version) | |
{ | |
for (VersionMethod vMethod : versionMethods) | |
{ | |
if (vMethod.isVersion(version)) | |
{ | |
return vMethod; | |
} | |
} | |
for (VersionMethod vMethod : versionMethods) | |
{ | |
if (vMethod.isUniversal()) | |
{ | |
return vMethod; | |
} | |
} | |
return null; | |
} | |
} | |
public static class VersionMethod | |
{ | |
private String version = "*"; | |
private String method; | |
private Class<?>[] argTypes = new Class<?>[0]; | |
private Object[] args = new Object[0]; | |
public VersionMethod(String version, String method) | |
{ | |
this.version = version; | |
this.method = method; | |
} | |
public VersionMethod(String version, String method, Class<?>... argTypes) | |
{ | |
this.version = version; | |
this.method = method; | |
this.argTypes = argTypes; | |
} | |
public VersionMethod(String method) | |
{ | |
this.method = method; | |
} | |
public VersionMethod(String method, Class<?>... argTypes) | |
{ | |
this.method = method; | |
this.argTypes = argTypes; | |
} | |
public boolean isVersion(String version) | |
{ | |
if (version.replace("_", ".").toLowerCase().contains(this.version.toLowerCase())) | |
{ | |
return true; | |
} else if (version.toLowerCase().contains(this.version.toLowerCase())) | |
{ | |
return true; | |
} | |
return false; | |
} | |
public boolean isUniversal() | |
{ | |
return version.equals("*"); | |
} | |
public String getMethod() | |
{ | |
return method; | |
} | |
public boolean hasArgTypes() | |
{ | |
return argTypes.length != 0; | |
} | |
public Class<?>[] getArgTypes() | |
{ | |
return argTypes; | |
} | |
public Object[] getArgs() | |
{ | |
return args; | |
} | |
protected void addArgs(Object... args) | |
{ | |
this.args = args; | |
} | |
} | |
/* | |
* Reflection utility methods | |
*/ | |
private static Class<?> getClassFromName(String clazz) throws ClassNotFoundException | |
{ | |
return Class.forName(clazz.replace("_version_", version)); | |
} | |
private static Method getMethod(Class<?> clazz, String method, Class<?>[] argTypes) | |
{ | |
try | |
{ | |
return clazz.getMethod(method, argTypes); | |
} catch (Exception e) | |
{ | |
return null; | |
} | |
} | |
private static Class<?>[] getArgTypes(Object... args) | |
{ | |
if (args != null) | |
{ | |
Class<?>[] argTypes = new Class<?>[args.length]; | |
for (int i = 0; i < args.length; i++) | |
{ | |
argTypes[i] = args[i] != null ? args[i].getClass() : null; | |
} | |
return argTypes; | |
} | |
return null; | |
} | |
private static String argTypesToString(Class<?>[] argTypes) | |
{ | |
String str = ""; | |
for (int i = 0; i < argTypes.length; i++) | |
{ | |
str += (i != 0 ? ", " : "") + argTypes[i].getSimpleName(); | |
} | |
return str; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment