Skip to content

Instantly share code, notes, and snippets.

@Alvin-LB
Last active October 17, 2016 14:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Alvin-LB/dff21ea72cec5e60e1f5965b38f5cc32 to your computer and use it in GitHub Desktop.
Save Alvin-LB/dff21ea72cec5e60e1f5965b38f5cc32 to your computer and use it in GitHub Desktop.
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);
}
}
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();
}
}
}
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;
}
}
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