package forgeperms.asm; import java.io.IOException; import java.util.HashSet; import java.util.Set; import net.minecraft.launchwrapper.IClassTransformer; import net.minecraft.launchwrapper.LaunchClassLoader; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; /** * Patching system. Mostly copied from https://github.com/matthewprenger/ServerTools/blob/develop/src/main/java/com/matthewprenger/servertools/core/asm/STClassTransformer.java */ public class FPClassTransformer implements IClassTransformer { private static FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE; private static final Set<PatchNote> patches = new HashSet<PatchNote>(); static { PatchNote chPatch = new PatchNote("net.minecraft.command.CommandHandler", "forgeperms.asm.FPCommandHandler"); chPatch.addMethodToPatch(new MethodNote("executeCommand", "func_71556_a", "(Lnet/minecraft/command/ICommandSender;Ljava/lang/String;)I")); chPatch.addMethodToPatch(new MethodNote("getPossibleCommands", "func_71558_b", "(Lnet/minecraft/command/ICommandSender;Ljava/lang/String;)Ljava/util/List;")); chPatch.addMethodToPatch(new MethodNote("getPossibleCommands", "func_71557_a", "(Lnet/minecraft/command/ICommandSender;)Ljava/util/List;")); addPatch(chPatch); } @Override public byte[] transform(String name, String transformedName, byte[] bytes) { if (bytes == null) return null; for (PatchNote patchNote : patches) { if (patchNote.sourceClass.equals(transformedName)) { return transform(name, patchNote, bytes); } } return bytes; } private static byte[] transform(String obfName, PatchNote patchNote, byte[] bytes) { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(bytes); classReader.accept(classNode, 0); if (patchNote.methodsToPatch.isEmpty()) return bytes; for (MethodNote methodNote : patchNote.methodsToPatch) { MethodNode sourceMethod = null; MethodNode replacementMethod = null; try { for (MethodNode method : classNode.methods) { if (methodNote.srgMethodName.equals(remapper.mapMethodName(obfName, method.name, method.desc))) { sourceMethod = method; break; } else if (methodNote.methodName.equals(method.name) && methodNote.deobfDesc.equals(method.desc)) { sourceMethod = method; } } ClassNode replacementClass = loadClass(patchNote.replacementClass); for (MethodNode method : replacementClass.methods) { if (methodNote.srgMethodName.equals(remapper.mapMethodName(patchNote.replacementClass, method.name, method.desc))) { replacementMethod = method; break; } else if (methodNote.methodName.equals(method.name) && methodNote.deobfDesc.equals(method.desc)) { replacementMethod = method; break; } } } catch (Throwable t) { t.printStackTrace(System.err); } if (sourceMethod != null && replacementMethod != null) { System.out.println("[ForgePermsCore] Successfully Mapped Method to be Replaced"); System.out.println(String.format(" Source: %s@%s Replacement: %s@%s", sourceMethod.name, sourceMethod.desc, replacementMethod.name, replacementMethod.desc)); classNode.methods.remove(sourceMethod); classNode.methods.add(replacementMethod); } else { return bytes; } } ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(classWriter); return classWriter.toByteArray(); } public static void addPatch(PatchNote patchNote) { patches.add(patchNote); } private static ClassNode loadClass(String className) throws IOException { LaunchClassLoader loader = (LaunchClassLoader) FPClassTransformer.class.getClassLoader(); ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(loader.getClassBytes(className)); classReader.accept(classNode, 0); return classNode; } public static class PatchNote { public final String sourceClass; public final String replacementClass; public final Set<MethodNote> methodsToPatch = new HashSet<MethodNote>(); public PatchNote(String sourceClass, String replacementClass) { this.sourceClass = sourceClass; this.replacementClass = replacementClass; } public void addMethodToPatch(MethodNote methodNote) { methodsToPatch.add(methodNote); } } public static class MethodNote { public final String methodName; public final String srgMethodName; public final String deobfDesc; public MethodNote(String methodName, String srgMethodName, String deobfDesc) { this.methodName = methodName; this.srgMethodName = srgMethodName; this.deobfDesc = deobfDesc; } } }