Last active
October 17, 2016 14:49
-
-
Save Alvin-LB/dff21ea72cec5e60e1f5965b38f5cc32 to your computer and use it in GitHub Desktop.
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.bringholm.magmaevent; | |
import com.bringholm.magmaevent.bukkitutils.BukkitReflectionUtils; | |
import jdk.internal.org.objectweb.asm.ClassReader; | |
import jdk.internal.org.objectweb.asm.ClassWriter; | |
import jdk.internal.org.objectweb.asm.Label; | |
import jdk.internal.org.objectweb.asm.Opcodes; | |
import jdk.internal.org.objectweb.asm.tree.*; | |
import tk.ivybits.agent.Tools; | |
import java.io.IOException; | |
import java.lang.instrument.*; | |
import java.security.ProtectionDomain; | |
import java.util.ArrayList; | |
import java.util.List; | |
public class Agent implements ClassFileTransformer { | |
private static Instrumentation instrumentation; | |
private static Agent transformer; | |
private static Class<?> classToTransform = BukkitReflectionUtils.getNMSClass("BlockMagma"); | |
private static List<AbstractInsnNode> insnNodes = new ArrayList<>(); | |
static { | |
insnNodes.add(new VarInsnNode(Opcodes.ALOAD, 5)); | |
insnNodes.add(new VarInsnNode(Opcodes.ALOAD, 1)); | |
insnNodes.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/bringholm/magmaevent/MagmaEvent", "callEvent", "(Ljava/lang/Object;Ljava/lang/Object;)Z", false)); | |
LabelNode labelNode = new LabelNode(new Label()); | |
insnNodes.add(new JumpInsnNode(Opcodes.IFEQ, labelNode)); | |
insnNodes.add(new InsnNode(Opcodes.RETURN)); | |
insnNodes.add(labelNode); | |
insnNodes.add(new FrameNode(Opcodes.F_SAME, 0, null, 0, null)); | |
} | |
public static void agentmain(String string, Instrumentation instrument) { | |
instrumentation = instrument; | |
transformer = new Agent(); | |
instrumentation.addTransformer(transformer); | |
try { | |
instrumentation.redefineClasses(new ClassDefinition(classToTransform, Tools.getBytesFromClass(classToTransform))); | |
killAgent(); | |
} catch (ClassNotFoundException | UnmodifiableClassException | IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
@Override | |
public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classBuffer) throws IllegalClassFormatException { | |
if (!loader.equals(ClassLoader.getSystemClassLoader())) { | |
return classBuffer; | |
} else { | |
if (className.equals(classToTransform.getName().replace(".", "/"))) { | |
return transform(className, classBuffer); | |
} else { | |
return classBuffer; | |
} | |
} | |
} | |
private static byte[] transform(String className, byte[] classBuffer) { | |
System.out.println("[MagmaEvent] Transforming: " + className); | |
ClassNode classNode = new ClassNode(); | |
ClassReader classReader = new ClassReader(classBuffer); | |
classReader.accept(classNode, 0); | |
transform(classNode); | |
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); | |
classNode.accept(classWriter); | |
return classWriter.toByteArray(); | |
} | |
private static void transform(ClassNode classNode) { | |
for (MethodNode method : classNode.methods) { | |
if (method.name.equals("b") && method.desc.equals("(L" + BukkitReflectionUtils.getNMSVersion().replace(".", "/") + "/World;L" + BukkitReflectionUtils.getNMSVersion().replace(".", "/") + "/BlockPosition;L" + BukkitReflectionUtils.getNMSVersion().replace(".", "/") + "/IBlockData;Ljava/util/Random;)V")) { | |
AbstractInsnNode targetNode = null; | |
for (AbstractInsnNode instruction : method.instructions.toArray()) { | |
if (instruction instanceof FrameNode) { | |
if (instruction.getOpcode() == -1) { | |
targetNode = instruction; | |
break; | |
} | |
} | |
} | |
if (targetNode != null) { | |
for (AbstractInsnNode instruction : insnNodes) { | |
method.instructions.insert(targetNode, instruction); | |
targetNode = targetNode.getNext(); | |
} | |
} else { | |
System.out.println("[MagmaEvent] Failed to add Event to Magma Block class!"); | |
} | |
} | |
} | |
} | |
private static void killAgent() { | |
instrumentation.removeTransformer(transformer); | |
} | |
} |
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.bringholm.magmaevent; | |
import com.bringholm.magmaevent.bukkitutils.BukkitReflectionUtils; | |
import com.sun.tools.attach.AgentInitializationException; | |
import com.sun.tools.attach.AgentLoadException; | |
import com.sun.tools.attach.AttachNotSupportedException; | |
import tk.ivybits.agent.AgentLoader; | |
import tk.ivybits.agent.Tools; | |
import java.io.File; | |
import java.io.IOException; | |
public class AgentAttacher { | |
public static void attachAgent(ClassLoader loader) { | |
try { | |
Tools.setCacheDir("plugins" + File.separator + "MagmaEvent" + File.separator + "tmp"); | |
Tools.loadAgentLibrary(loader); | |
AgentLoader.attachAgentToJVM(Tools.getCurrentPID(), Agent.class, Tools.class, MagmaEvent.class, BukkitReflectionUtils.class); | |
} catch (IOException | AttachNotSupportedException | AgentLoadException | AgentInitializationException e) { | |
e.printStackTrace(); | |
} | |
} | |
} |
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.bringholm.magmaevent.bukkitutils; | |
import org.bukkit.Bukkit; | |
import java.lang.reflect.*; | |
public class BukkitReflectionUtils { | |
public static Class<?> getNMSClass(String clazz) { | |
return getClass(getNMSVersion() + "." + clazz); | |
} | |
public static Class<?> getCBClass(String clazz) { | |
return getClass(getCBVersion() + "." + clazz); | |
} | |
public static Object invokeDeclaredMethod(Object object, String methodName, Object... parameters) { | |
try { | |
Method method = object.getClass().getDeclaredMethod(methodName, convertToMethodParameters(parameters)); | |
method.setAccessible(true); | |
return method.invoke(object, parameters); | |
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static Object invokeMethod(Object object, String methodName, Object... parameters) { | |
try { | |
Method method = object.getClass().getMethod(methodName, convertToMethodParameters(parameters)); | |
return method.invoke(object, parameters); | |
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static Object invokeDeclaredConstructor(Class<?> clazz, Object... parameters) { | |
try { | |
Constructor constructor = clazz.getDeclaredConstructor(convertToMethodParameters(parameters)); | |
constructor.setAccessible(true); | |
return constructor.newInstance(parameters); | |
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static Object invokeConstructor(Class<?> clazz, Object... parameters) { | |
try { | |
Constructor constructor = clazz.getConstructor(convertToMethodParameters(parameters)); | |
return constructor.newInstance(parameters); | |
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static Object getDeclaredField(Object object, String fieldName) { | |
try { | |
Field field = object.getClass().getDeclaredField(fieldName); | |
field.setAccessible(true); | |
return field.get(object); | |
} catch (NoSuchFieldException | IllegalAccessException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static Object getField(Object object, String fieldName) { | |
try { | |
Field field = object.getClass().getField(fieldName); | |
return field.get(object); | |
} catch (IllegalAccessException | NoSuchFieldException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static Object getDeclaredStaticField(Class<?> clazz, String fieldName) { | |
try { | |
Field field = clazz.getDeclaredField(fieldName); | |
field.setAccessible(true); | |
return field.get(null); | |
} catch (NoSuchFieldException | IllegalAccessException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static Object getStaticField(Class<?> clazz, String fieldName) { | |
try { | |
Field field = clazz.getField(fieldName); | |
return field.get(null); | |
} catch (NoSuchFieldException | IllegalAccessException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static void setDeclaredField(Object object, String fieldName, Object value) { | |
try { | |
Field field = object.getClass().getDeclaredField(fieldName); | |
field.setAccessible(true); | |
field.set(object, value); | |
} catch (NoSuchFieldException | IllegalAccessException e) { | |
e.printStackTrace(); | |
} | |
} | |
public static void setField(Object object, String fieldName, Object value) { | |
try { | |
Field field = object.getClass().getField(fieldName); | |
field.set(object, value); | |
} catch (NoSuchFieldException | IllegalAccessException e) { | |
e.printStackTrace(); | |
} | |
} | |
public static Object getAndInvokeNMSConstructor(String className, Object... parameters) { | |
Class<?> clazz = getNMSClass(className); | |
return invokeConstructor(clazz, parameters); | |
} | |
public static Object getAndInvokeDeclaredNMSConstructor(String className, Object... parameters) { | |
Class<?> clazz = getNMSClass(className); | |
return invokeDeclaredConstructor(clazz, parameters); | |
} | |
public static Object getAndInvokeCBConstructor(String className, Object... parameters) { | |
Class<?> clazz = getCBClass(className); | |
return invokeConstructor(clazz, parameters); | |
} | |
public static Object getAndInvokeDeclaredCBConstructor(String className, Object parameters) { | |
Class<?> clazz = getCBClass(className); | |
return invokeDeclaredConstructor(clazz, parameters); | |
} | |
public static void modifyFinalField(Field field, Object target, Object newValue) { | |
try { | |
field.setAccessible(true); | |
Field modifierField = Field.class.getField("modifiers"); | |
modifierField.setAccessible(true); | |
modifierField.setInt(field, field.getModifiers() & ~Modifier.FINAL); | |
field.set(target, newValue); | |
} catch (NoSuchFieldException | IllegalAccessException e) { | |
e.printStackTrace(); | |
} | |
} | |
private static Class<?> getClass(String clazz) { | |
try { | |
return Class.forName(clazz); | |
} catch (ClassNotFoundException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static String getNMSVersion() { | |
String version = Bukkit.getServer().getClass().getPackage().getName(); | |
return "net.minecraft.server" + version.substring(version.lastIndexOf(".")); | |
} | |
public static String getCBVersion() { | |
return Bukkit.getServer().getClass().getPackage().getName(); | |
} | |
private static Class<?>[] convertToMethodParameters(Object... parameters) { | |
Class<?>[] classes = new Class<?>[parameters.length]; | |
for (int i = 0; i < parameters.length; i++) { | |
Class<?> clazz = parameters[i].getClass(); | |
if (clazz == Boolean.class) { | |
clazz = boolean.class; | |
} else if (clazz == Byte.class) { | |
clazz = byte.class; | |
} else if (clazz == Character.class) { | |
clazz = char.class; | |
} else if (clazz == Double.class) { | |
clazz = double.class; | |
} else if (clazz == Float.class) { | |
clazz = float.class; | |
} else if (clazz == Integer.class) { | |
clazz = int.class; | |
} else if (clazz == Long.class) { | |
clazz = long.class; | |
} else if (clazz == Short.class) { | |
clazz = short.class; | |
} else if (clazz == Void.class) { | |
clazz = void.class; | |
} | |
classes[i] = clazz; | |
} | |
return classes; | |
} | |
} |
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.bringholm.magmaevent; | |
import com.bringholm.magmaevent.bukkitutils.BukkitReflectionUtils; | |
import org.bukkit.Bukkit; | |
import org.bukkit.World; | |
import org.bukkit.block.Block; | |
import org.bukkit.event.Listener; | |
import org.bukkit.event.block.BlockBurnEvent; | |
import org.bukkit.plugin.java.JavaPlugin; | |
public class MagmaEvent extends JavaPlugin implements Listener { | |
public MagmaEvent() { | |
AgentAttacher.attachAgent(this.getClassLoader()); | |
} | |
public static boolean callEvent(Object blockPos, Object world) { | |
Object bukkitWorld = BukkitReflectionUtils.invokeMethod(world, "getWorld"); | |
Block block = ((World) bukkitWorld).getBlockAt((int) BukkitReflectionUtils.invokeMethod(blockPos, "getX"), (int) BukkitReflectionUtils.invokeMethod(blockPos, "getY"), (int) BukkitReflectionUtils.invokeMethod(blockPos, "getZ")); | |
BlockBurnEvent event = new BlockBurnEvent(block); | |
Bukkit.getServer().getPluginManager().callEvent(event); | |
return event.isCancelled(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment