Skip to content

Instantly share code, notes, and snippets.

@daviga404
Created November 5, 2013 17:14
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save daviga404/7322545 to your computer and use it in GitHub Desktop.
Save daviga404/7322545 to your computer and use it in GitHub Desktop.
NMS Reflection: This gist is an example of using reflection to use net.minecraft.server classes to set a block, without the need to import the net.minecraft.server.X.* packages, where X is the current Bukkit version/revision (i.e. v1_6_R2). This way, there will be no need to recompile plugins every time Bukkit releases a new revision.
package com.daviga404.NMSReflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin implements Listener {
// The original classes in reflected variable form, named as they would normally be.
private Class<?> CraftWorld, World;
// The methods in the classes (CraftWorld#getHandle, World#setTypeIdAndData)
private Method getHandle, setTypeIdAndData;
public void onEnable() {
try {
// Setup the reflection as the plugin enables, this will save time later.
setupReflection();
getServer().getPluginManager().registerEvents(this, this);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
@EventHandler
public void onPlayerInteract(PlayerInteractEvent e) {
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
Block b = e.getClickedBlock();
int typeId = 89;
int data = 0;
try {
// Simply pass the Bukkit Block, a type ID and data to the method.
setBlockNatively(b, typeId, data);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
e1.printStackTrace();
}
}
}
// Stores the reflected classes & methods in variable for access later
private void setupReflection() throws NoSuchMethodException, SecurityException {
CraftWorld = getNMSClass("org.bukkit.craftbukkit.%s.CraftWorld");
World = getNMSClass("net.minecraft.server.%s.World");
getHandle = CraftWorld.getDeclaredMethod("getHandle", new Class<?>[]{});
setTypeIdAndData = World.getDeclaredMethod("setTypeIdAndData", int.class, int.class, int.class, int.class, int.class, int.class);
}
// Mimics the original World#setTypeIdAndData method in the NMS class.
private void setBlockNatively(org.bukkit.block.Block b, int typeId, int data) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Object craftWorld = getHandle.invoke(CraftWorld.cast(b.getWorld()), new Object[]{});
setTypeIdAndData.invoke(craftWorld, b.getX(), b.getY(), b.getZ(), typeId, data, 0);
}
/* Uses regex and reflection to find all packages which contain the NMS version,
then retrieves the class from the argument by the fully qualified name. */
private Class<?> getNMSClass(String nmsClass) {
String version = null;
Pattern pat = Pattern.compile("net\\.minecraft\\.(?:server)?\\.(v(?:\\d_)+R\\d)");
for (Package p : Package.getPackages()) {
String name = p.getName();
Matcher m = pat.matcher(name);
if (m.matches()) version = m.group(1);
}
if (version == null) return null;
try {
return Class.forName(String.format(nmsClass, version));
} catch (ClassNotFoundException e) {
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment