This class will help you to use reflection API to access to org.bukkit.craftbukkit.* and net.minecraft.server.* it works with any version of bukkit Code: Code:java import org.bukkit.Bukkit;import org.bukkit.Server; import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.Collections;import java.util.List; /** * @author DPOH-VAR * @version 1.0 */@SuppressWarnings("UnusedDeclaration")public class ReflectionUtils { /** prefix of bukkit classes */ private static String preClassB = "org.bukkit.craftbukkit"; /** prefix of minecraft classes */ private static String preClassM = "net.minecraft.server"; /** boolean value, TRUE if server uses forge or MCPC+ */ private static boolean forge = false; /** check server version and class names */ static { if(Bukkit.getServer()!=null) { if(Bukkit.getVersion().contains("MCPC")||Bukkit.getVersion().contains("Forge")) forge = true; Server server = Bukkit.getServer(); Class<?> bukkitServerClass = server.getClass(); String[] pas = bukkitServerClass.getName().split("\\."); if (pas.length == 5) { String verB = pas[3]; preClassB += "."+verB; } try { Method getHandle = bukkitServerClass.getDeclaredMethod("getHandle"); Object handle = getHandle.invoke(server); Class handleServerClass = handle.getClass(); pas = handleServerClass.getName().split("\\."); if (pas.length == 5) { String verM = pas[3]; preClassM += "."+verM; } } catch (Exception ignored) { } } } /** * @return true if server has forge classes */ public static boolean isForge(){ return forge; } /** * Get class for name. * Replace {nms} to net.minecraft.server.V*. * Replace {cb} to org.bukkit.craftbukkit.V*. * Replace {nm} to net.minecraft * @param classes possible class paths * @return RefClass object * @throws RuntimeException if no class found */ public static RefClass getRefClass(String... classes){ for (String className: classes) try { className = className .replace("{cb}", preClassB) .replace("{nms}", preClassM) .replace("{nm}", "net.minecraft"); return getRefClass(Class.forName(className)); } catch (ClassNotFoundException ignored) { } throw new RuntimeException("no class found"); } /** * get RefClass object by real class * @param clazz class * @return RefClass based on passed class */ public static RefClass getRefClass(Class clazz) { return new RefClass(clazz); } /** * RefClass - utility to simplify work with reflections. */ public static class RefClass { private final Class<?> clazz; /** * get passed class * @return class */ public Class<?> getRealClass() { return clazz; } private RefClass(Class<?> clazz) { this.clazz = clazz; } /** * see {@link Class#isInstance(Object)} * @param object the object to check * @return true if object is an instance of this class */ public boolean isInstance(Object object){ return clazz.isInstance(object); } /** * get existing method by name and types * @param name name * @param types method parameters. can be Class or RefClass * @return RefMethod object * @throws RuntimeException if method not found */ public RefMethod getMethod(String name, Object... types) { try { Class[] classes = new Class[types.length]; int i=0; for (Object e: types) { if (e instanceof Class) classes[i++] = (Class)e; else if (e instanceof RefClass) classes[i++] = ((RefClass) e).getRealClass(); else classes[i++] = e.getClass(); } try { return new RefMethod(clazz.getMethod(name, classes)); } catch (NoSuchMethodException ignored) { return new RefMethod(clazz.getDeclaredMethod(name, classes)); } } catch (Exception e) { throw new RuntimeException(e); } } /** * get existing constructor by types * @param types parameters. can be Class or RefClass * @return RefMethod object * @throws RuntimeException if constructor not found */ public RefConstructor getConstructor(Object... types) { try { Class[] classes = new Class[types.length]; int i=0; for (Object e: types) { if (e instanceof Class) classes[i++] = (Class)e; else if (e instanceof RefClass) classes[i++] = ((RefClass) e).getRealClass(); else classes[i++] = e.getClass(); } try { return new RefConstructor(clazz.getConstructor(classes)); } catch (NoSuchMethodException ignored) { return new RefConstructor(clazz.getDeclaredConstructor(classes)); } } catch (Exception e) { throw new RuntimeException(e); } } /** * find method by type parameters * @param types parameters. can be Class or RefClass * @return RefMethod object * @throws RuntimeException if method not found */ public RefMethod findMethod(Object... types) { Class[] classes = new Class[types.length]; int t=0; for (Object e: types) { if (e instanceof Class) classes[t++] = (Class)e; else if (e instanceof RefClass) classes[t++] = ((RefClass) e).getRealClass(); else classes[t++] = e.getClass(); } List<Method> methods = new ArrayList<>(); Collections.addAll(methods, clazz.getMethods()); Collections.addAll(methods, clazz.getDeclaredMethods()); findMethod: for (Method m: methods) { Class<?>[] methodTypes = m.getParameterTypes(); if (methodTypes.length != classes.length) continue; for (int i=0; i<classes.length; i++) { if (!classes.equals(methodTypes)) continue findMethod; return new RefMethod(m); } } throw new RuntimeException("no such method"); } /** * find method by name * @param names possible names of method * @return RefMethod object * @throws RuntimeException if method not found */ public RefMethod findMethodByName(String... names) { List<Method> methods = new ArrayList<>(); Collections.addAll(methods, clazz.getMethods()); Collections.addAll(methods, clazz.getDeclaredMethods()); for (Method m: methods) { for (String name: names) { if (m.getName().equals(name)) { return new RefMethod(m); } } } throw new RuntimeException("no such method"); } /** * find method by return value * @param type type of returned value * @throws RuntimeException if method not found * @return RefMethod */ public RefMethod findMethodByReturnType(RefClass type) { return findMethodByReturnType(type.clazz); } /** * find method by return value * @param type type of returned value * @return RefMethod * @throws RuntimeException if method not found */ public RefMethod findMethodByReturnType(Class type) { if (type==null) type = void.class; List<Method> methods = new ArrayList<>(); Collections.addAll(methods, clazz.getMethods()); Collections.addAll(methods, clazz.getDeclaredMethods()); for (Method m: methods) { if (type.equals(m.getReturnType())) { return new RefMethod(m); } } throw new RuntimeException("no such method"); } /** * find constructor by number of arguments * @param number number of arguments * @return RefConstructor * @throws RuntimeException if constructor not found */ public RefConstructor findConstructor(int number) { List<Constructor> constructors = new ArrayList<>(); Collections.addAll(constructors, clazz.getConstructors()); Collections.addAll(constructors, clazz.getDeclaredConstructors()); for (Constructor m: constructors) { if (m.getParameterTypes().length == number) return new RefConstructor(m); } throw new RuntimeException("no such constructor"); } /** * get field by name * @param name field name * @return RefField * @throws RuntimeException if field not found */ public RefField getField(String name) { try { try { return new RefField(clazz.getField(name)); } catch (NoSuchFieldException ignored) { return new RefField(clazz.getDeclaredField(name)); } } catch (Exception e) { throw new RuntimeException(e); } } /** * find field by type * @param type field type * @return RefField * @throws RuntimeException if field not found */ public RefField findField(RefClass type) { return findField(type.clazz); } /** * find field by type * @param type field type * @return RefField * @throws RuntimeException if field not found */ public RefField findField(Class type) { if (type==null) type = void.class; List<Field> fields = new ArrayList<>(); Collections.addAll(fields, clazz.getFields()); Collections.addAll(fields, clazz.getDeclaredFields()); for (Field f: fields) { if (type.equals(f.getType())) { return new RefField(f); } } throw new RuntimeException("no such field"); } } /** * Method wrapper */ public static class RefMethod { private final Method method; /** * @return passed method */ public Method getRealMethod(){ return method; } /** * @return owner class of method */ public RefClass getRefClass(){ return new RefClass(method.getDeclaringClass()); } /** * @return class of method return type */ public RefClass getReturnRefClass(){ return new RefClass(method.getReturnType()); } private RefMethod (Method method) { this.method = method; method.setAccessible(true); } /** * apply method to object * @param e object to which the method is applied * @return RefExecutor with method call(...) */ public RefExecutor of(Object e) { return new RefExecutor(e); } /** * call static method * @param params sent parameters * @return return value */ public Object call(Object... params) { try{ return method.invoke(null,params); } catch (Exception e) { throw new RuntimeException(e); } } public class RefExecutor { Object e; public RefExecutor(Object e) { this.e = e; } /** * apply method for selected object * @param params sent parameters * @return return value * @throws RuntimeException if something went wrong */ public Object call(Object... params) { try{ return method.invoke(e,params); } catch (Exception e) { throw new RuntimeException(e); } } } } /** * Constructor wrapper */ public static class RefConstructor { private final Constructor constructor; /** * @return passed constructor */ public Constructor getRealConstructor(){ return constructor; } /** * @return owner class of method */ public RefClass getRefClass(){ return new RefClass(constructor.getDeclaringClass()); } private RefConstructor (Constructor constructor) { this.constructor = constructor; constructor.setAccessible(true); } /** * create new instance with constructor * @param params parameters for constructor * @return new object * @throws RuntimeException if something went wrong */ public Object create(Object... params) { try{ return constructor.newInstance(params); } catch (Exception e) { throw new RuntimeException(e); } } } public static class RefField { private Field field; /** * @return passed field */ public Field getRealField(){ return field; } /** * @return owner class of field */ public RefClass getRefClass(){ return new RefClass(field.getDeclaringClass()); } /** * @return type of field */ public RefClass getFieldRefClass(){ return new RefClass(field.getType()); } private RefField (Field field) { this.field = field; field.setAccessible(true); } /** * apply fiend for object * @param e applied object * @return RefExecutor with getter and setter */ public RefExecutor of(Object e) { return new RefExecutor(e); } public class RefExecutor { Object e; public RefExecutor(Object e) { this.e = e; } /** * set field value for applied object * @param param value */ public void set(Object param) { try{ field.set(e,param); } catch (Exception e) { throw new RuntimeException(e); } } /** * get field value for applied object * @return value of field */ public Object get() { try{ return field.get(e); } catch (Exception e) { throw new RuntimeException(e); } } } } }} How to use: 1. get class Method getRefClass allows you to get class by name. RefClass classCraftPlayer = getRefClass("{cb}.entity.CraftPlayer"); use this patterns to replace package name: {nms} -> net.minecraft.server.v_* {cb} -> org.bukkit.craftbukkit.v_* {nm} -> net.minecraft You can specify more than one name: private static final RefClass classParticlePacket = getRefClass( "{nms}.Packet63WorldParticles", // bukkit 1.6"{nms}.PacketPlayOutWorldParticles", // bukkit 1.7"{nm}.network.packet.Packet63WorldParticles", // forge); method returns a wrapper over the class 2. get method RefMethod methodGetHandle = classCraftPlayer.getMethod("getHandle"); RefMethod is wrapper of method org.bukkit.craftbukkit.CraftPlayer.getHandle() getMethod(String, Object...) takes values: - method name - parameter types (java.lang.Class or RefClass objects) You can find method by name, by return value, by type parameters. See the documentation 3. call method call non-static method: result = myRefMethod.of(object).call(parameters...) call static method: result = myRefMethod.call(parameters...) result = myRefMethod.of(null).call(parameters...) 4. get field RefField myField = myRefClass.getField("name"); You can find field by class: myRefClass.fildField(int.class); - fild field of type "int" 5. change value of field Object value = myField.of(object).get(); myField.of(object).set(value); use null instead of object to access static fields. Don't change value of final fields. 6. use constructor RefConstructor myConstructor = myRefClass.getConstuctor(types...) You can find constructor by number of arguments: findConstructor(int) Create new object with this constructor: Object newInstance = myConstructor.create(parameters...) All of these methods can throw RuntimeException Example: This class allows you to play custom effects using Packet WorldParticles Code:java import org.bukkit.Location;import org.bukkit.entity.Player; import static ReflectionUtils.*; public class Particle { private static final RefClass classParticlePacket = getRefClass( "{nms}.Packet63WorldParticles", // 1.6 "{nms}.PacketPlayOutWorldParticles" // 1.7 ); private static final RefField fieldPacketName = classParticlePacket.getField("a"); private static final RefField fieldPacketLocX = classParticlePacket.getField("b"); private static final RefField fieldPacketLocY = classParticlePacket.getField("c"); private static final RefField fieldPacketLocZ = classParticlePacket.getField("d"); private static final RefField fieldPacketDifX = classParticlePacket.getField("e"); private static final RefField fieldPacketDifY = classParticlePacket.getField("f"); private static final RefField fieldPacketDifZ = classParticlePacket.getField("g"); private static final RefField fieldPacketSpeed = classParticlePacket.getField("h"); private static final RefField fieldPacketAmount = classParticlePacket.getField("i"); private static final RefConstructor particlesConstructor = classParticlePacket.getConstructor(); private static final RefClass classPacket = getRefClass("{nms}.Packet"); private static final RefClass classCraftPlayer = getRefClass("{cb}.entity.CraftPlayer"); private static final RefMethod methodGetHandle = classCraftPlayer.getMethod("getHandle"); private static final RefClass classEntityPlayer = getRefClass("{nms}.EntityPlayer"); private static final RefField fieldPlayerConnection = classEntityPlayer.getField("playerConnection"); private static final RefClass classPlayerConnection = getRefClass("{nms}.PlayerConnection"); private static final RefMethod methodSendPacket = classPlayerConnection.findMethod(classPacket); private String name; public Particle(String name) { this.name = name; } public float dx = 0; public float dy = 0; public float dz = 0; public float speed = 0; public int amount = 1; public void send(Location location){ Object packet = particlesConstructor.create(); fieldPacketName.of(packet).set(name); fieldPacketLocX.of(packet).set((float)location.getX()); fieldPacketLocY.of(packet).set((float)location.getY()); fieldPacketLocZ.of(packet).set((float)location.getZ()); fieldPacketDifX.of(packet).set(dx); fieldPacketDifY.of(packet).set(dy); fieldPacketDifZ.of(packet).set(dz); fieldPacketSpeed.of(packet).set(speed); fieldPacketAmount.of(packet).set(amount); for (Player player: location.getWorld().getPlayers()) { if (player.getLocation().distance(location) > 64) continue; Object handle = methodGetHandle.of(player).call(); Object connection = fieldPlayerConnection.of(handle).get(); methodSendPacket.of(connection).call(packet); } } } Particle particle = new Particle("witchMagic"); particle.amount = 10; particle.dy = 1; particle.send(location); //play particle effect
I did it hastily Code:java if (player.getLocation().distanceSquared(location) > 4096) continue; but distanceSquared will work faster
The most expensive operations in the loop is Object handle = methodGetHandle.of(player).call(); Object connection = fieldPlayerConnection.of(handle).get(); methodSendPacket.of(connection).call(packet); because they calls Reflection API
invalid version barrier exists for a reason, anything that intended to break version barrier is harmful and useless for public plugins.
please explain how you going to predict what name "field a of class b" will have in next version of bukkit. looks like you are very very expirienced in this DPOH-VAR As long as you do not understand meaning of version barrier you will think that you are smarter then bukkit team and try to overcome it, this is way to disaster.
I suggest to find field by type. Code:groovy // Example: class in net.minectaft.server.v*.class MinecraftClass{ private double value = 0;} // Example: similar class in net.minectaft. (mcpc)class MCPCClass{ private double field_1234 = 0;} // using ReflectionUtilsRefField field = myClass.findField(double.class);double value = field.of(myObject).get();// may be a problem if there are two or more fields of type double// but it happens in rare cases I know why developers have separated server to bukkit API and craftbukkit. Bukkit API is backward compatible. Any plugin using only Bukit API will work after update the server. But craftbukkit does not guarantee any compatibility. And version barrier only warns about it. If I want to use NBT classes in my plugin (net.minecraft.server.v*.NBTBase, net.minectaft.nbt.NBTBase) > need to create a intermediate class for each craftbukkit version from 1.4.6 to 1.7.9? UPD: ReflectionUtils.java v 1.2 What is new: 1) public static RefClass ReflectionUtils.getRefClass(String pattern) pattern contains a possible classes, separated by "," Example: Code:groovy // get EntityPlayer class. If class is not found, throw exceptionRefClass classEntityPlayer = getRefClass("{nms}.EntityPlayer, {nm}.entity.player.EntityPlayer");// get EntityPlayer class. If class is not found, return null (last argument)RefClass classEntityPlayer = getRefClass("{nms}.EntityPlayer, {nm}.entity.player.EntityPlayer, null"); 2) public RefMethod RefClass.findMethod(MethodCondition... condition) find method by special conditions Example: Code:groovy // find method that takes NBTTagCompound// and method name ends with "a"RefMethod write = classTileEntity.findMethod( new MethodCondition() .withTypes("{nms}.NBTTagCompound, {nm}.nbt.NBTTagCompound, {NBTTagCompound}") .withSuffix("a")); class MethodCondition has methods: withForge(boolean forge) // check checks whether your server is forge withName(String name) // check full name of method withPrefix(String prefix) // check prefix of method name withSuffix(String suffix) // check suffix of method name withReturnType(Class returnType) // check return type of method withReturnType(RefClass returnType) // as previous, using RefClass withReturnType(String pattern) // as previous, but you can specify pattern of return type withTypes(Object... types) // check parameter types. "types" can be: Class, RefClass, String pattern withAbstract(boolean modAbstract) // check abstract modifier withFinal(boolean modFinal) // check final modifier withStatic(boolean modStatic) // check static modifier withIndex(int index) // use it if there is more than one method found. This check occurs in the last. 3) ReflectionUtils.addReplacement(String key, String value) add new replacement for using in patterns Default replacements is: nm -> net.minectaft nms -> net.minecraft.server.%current_version% cb -> org.bukkit.craftbukkit.%current_version% Example: Code:groovy addReplacement("nbt", "net.minecraft.nbt");RefClass classNBTTagCompound = getRefClass("{nbt}.NBTTagCompound"); 4) ReflectionUtils.addReplacementsYaml(File file) load replacements from yaml file. used in PowerNBT to fix forge classes EDIT by Moderator: merged posts, please use the edit button instead of double posting.
I suggest to find field by type. DPOH-VAR public class z { boolean a boolean b boolean c } changed to by adding String field and changing type of one field from boolean to int public class x { String A boolean b boolean c int d boolean u } How you going to predict name of field by type in such case? and dont suggest magic, i already reserved that method.
Which is kind of the point. You have exactly 0% certainty that the obfuscation won't change in the next major release so that grabbing a field by name or type won't get completely the wrong field. The worst case is that you get a field of the same type which does something completely unexpected, and ends up destroying someone's world (and before you laugh at that, it has already happened).
Not all NMS method are obfuscated, but they will still break every Minecraft update if developer won't use reflection. And of course, the plugin should run more version checks than just getting variable with type...