Created
November 5, 2013 17:14
-
-
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.
This file contains 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 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