package me.kcm; import me.kcm.events.*; import net.minecraft.launchwrapper.IClassTransformer; import net.minecraft.launchwrapper.Launch; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FrameNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.JumpInsnNode; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.LineNumberNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.VarInsnNode; public class KajetansTransformer implements IClassTransformer { private final boolean obfuscated; public KajetansTransformer() { Boolean ldev = (Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment"); if(ldev == null) { obfuscated = true; } else { obfuscated = !ldev; } //System.out.println(obfuscated); } private void printPatch(String s) { System.out.println("|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾"); System.out.println("| Patching " + s); System.out.println("|___________________________________________________"); } @Override public byte[] transform(String old, String searge, byte[] bytes) { switch(searge) { /*case "net.minecraft.entity.EntityLivingBase": printPatch(LivingDamageCalculationEvent.class.getSimpleName()); return patchDamageHook(old, bytes);*/ case "net.minecraft.entity.player.EntityPlayerMP": printPatch(PlayerTabListNameEvent.class.getSimpleName()); return patchTabList(old, bytes); case "net.minecraft.entity.Entity": printPatch(FarmlandTrampleEvent.class.getSimpleName()); return patchEntityCanTrample(old, bytes); case "net.minecraft.server.management.PlayerList": printPatch("PlayerList"); return patchPlayerList(old, bytes); case "net.minecraft.server.MinecraftServer": printPatch("MinecraftServer"); return patchMinecraftServer(old, bytes); case "net.minecraft.network.NetHandlerPlayServer": printPatch("NetHandlerPlayServer"); return patchNetHandlerPlayServer(old, bytes); /*case "net.minecraft.item.crafting.CraftingManager": printPatch("CraftingManager"); return patchCraftingManager(old, bytes);*/ } return bytes; } /*ListIterator list = ins.iterator(); while(list.hasNext()) { AbstractInsnNode next = list.next(); System.out.println(getString(next)); }*/ public byte[] patchDamageHook(String c, byte[] bytes) { try { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(bytes); classReader.accept(classNode, 0); // EntityLivingBase <--> vp // damageEntity <--> func_70665_d <--> d MethodNode mn; if(obfuscated) { mn = classNode.methods.stream().filter(me -> me.name.equals("d")).filter(me -> "(Lur;F)V".equals(me.desc)).findAny().get(); } else { mn = classNode.methods.stream().filter(me -> me.name.equals("damageEntity")).findAny().get(); } InsnList ins = mn.instructions; ins.insert(ins.get(3), new VarInsnNode(Opcodes.FLOAD, 2)); if(obfuscated) { ins.insert(ins.get(4), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onDamageCalculation", "(Lvp;Lur;F)V", false)); } else { ins.insert(ins.get(4), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onDamageCalculation", "(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/util/DamageSource;F)V", false)); } while(ins.size() > 8) { ins.remove(ins.get(6)); } ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); classNode.accept(writer); return writer.toByteArray(); } catch(Exception ex) { ex.printStackTrace(); } return bytes; } public byte[] patchTabList(String c, byte[] bytes) { try { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(bytes); classReader.accept(classNode, 0); // EntityPlayerMP <--> oq // getTabListDisplayName <--> func_175396_E <--> K // MD: oq/K ()Lhh; net/minecraft/entity/player/EntityPlayerMP/func_175396_E ()Lnet/minecraft/util/text/ITextComponent; MethodNode mn; if(obfuscated) { mn = classNode.methods.stream().filter(me -> me.name.equals("K")).filter(me -> "()Lhh;".equals(me.desc)).findAny().get(); } else { mn = classNode.methods.stream().filter(me -> me.name.equals("getTabListDisplayName")).findAny().get(); } InsnList ins = mn.instructions; ins.remove(ins.get(2)); ins.insert(ins.get(1), new VarInsnNode(Opcodes.ALOAD, 0)); if(obfuscated) { ins.insert(ins.get(2), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onGetTabListDisplayName", "(Loq;)Lhh;", false)); } else { ins.insert(ins.get(2), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onGetTabListDisplayName", "(Lnet/minecraft/entity/player/EntityPlayerMP;)Lnet/minecraft/util/text/ITextComponent;", false)); } // patching setEntityActionState // MD: net/minecraft/entity/player/EntityPlayerMP/setEntityActionState (FFZZ)V oq/a (FFZZ)V /*if(obfuscated) { mn = classNode.methods.stream().filter(me -> me.name.equals("a")).filter(me -> "(FFZZ)V".equals(me.desc)).findAny().get(); } else { mn = classNode.methods.stream().filter(me -> me.name.equals("setEntityActionState")).findAny().get(); } ins = mn.instructions; if(obfuscated) { ins.insert(ins.get(1), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onSetEntityActionState", "(Loq;FFZZ)V", false)); } else { ins.insert(ins.get(1), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onSetEntityActionState", "(Lnet/minecraft/entity/player/EntityPlayerMP;FFZZ)V", false)); } ins.insert(ins.get(1), new VarInsnNode(Opcodes.ILOAD, 4)); ins.insert(ins.get(1), new VarInsnNode(Opcodes.ILOAD, 3)); ins.insert(ins.get(1), new VarInsnNode(Opcodes.FLOAD, 2)); ins.insert(ins.get(1), new VarInsnNode(Opcodes.FLOAD, 1)); ins.insert(ins.get(1), new VarInsnNode(Opcodes.ALOAD, 0)); java.util.ListIterator list = ins.iterator(); while(list.hasNext()) { System.out.println(getString(list.next())); }*/ ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); classNode.accept(writer); return writer.toByteArray(); } catch(Exception ex) { ex.printStackTrace(); } return bytes; } public byte[] patchEntityCanTrample(String c, byte[] bytes) { try { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(bytes); classReader.accept(classNode, 0); // CL: vg net/minecraft/entity/Entity // Entity <--> vg // canTrample is forge function // Lnet/minecraft/world/World;Lnet/minecraft/block/Block;Lnet/minecraft/util/math/BlockPos;F // World <--> amu // Block <--> aow // BlockPos <--> et MethodNode mn = classNode.methods.stream().filter(me -> me.name.equals("canTrample")).findAny().get(); InsnList ins = mn.instructions; int i = ins.size(); LabelNode label = null; int counter = 0; while(counter < 3) { i--; if(ins.get(i) instanceof LabelNode) { counter++; label = (LabelNode) ins.get(i); } } while(ins.get(i).getOpcode() != Opcodes.IFLE) { i--; } ins.insert(ins.get(i), new JumpInsnNode(Opcodes.IFEQ, label)); if(obfuscated) { ins.insert(ins.get(i), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onEntityCanTrample", "(Lvg;Lamu;Laow;Let;F)Z", false)); } else { ins.insert(ins.get(i), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onEntityCanTrample", "(Lnet/minecraft/entity/Entity;Lnet/minecraft/world/World;" + "Lnet/minecraft/block/Block;Lnet/minecraft/util/math/BlockPos;F)Z", false)); } ins.insert(ins.get(i), new VarInsnNode(Opcodes.FLOAD, 4)); ins.insert(ins.get(i), new VarInsnNode(Opcodes.ALOAD, 3)); ins.insert(ins.get(i), new VarInsnNode(Opcodes.ALOAD, 2)); ins.insert(ins.get(i), new VarInsnNode(Opcodes.ALOAD, 1)); ins.insert(ins.get(i), new VarInsnNode(Opcodes.ALOAD, 0)); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); classNode.accept(writer); return writer.toByteArray(); } catch(Exception ex) { ex.printStackTrace(); } return bytes; } public byte[] patchPlayerList(String c, byte[] bytes) { try { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(bytes); classReader.accept(classNode, 0); int index = 0; AbstractInsnNode node; // ----------------------------------------------------------------- // first part - inserting PlayerConnectionEvent hook // ----------------------------------------------------------------- // EntityPlayerMP <--> oq // initializeConnectionToPlayer <--> func_72355_a <--> a //MD: pl/a (Lgw;Loq;)V net/minecraft/server/management/PlayerList/func_72355_a // (Lnet/minecraft/network/NetworkManager;Lnet/minecraft/entity/player/EntityPlayerMP;)V // CL: pa net/minecraft/network/NetHandlerPlayServer // public void initializeConnectionToPlayer(NetworkManager netManager, EntityPlayerMP playerIn, NetHandlerPlayServer nethandlerplayserver) // its something else: (Lgw;Loq;Lpa;)V // it seems forge is already overwriting this method MethodNode mn = classNode.methods.stream().filter(me -> me.name.equals("initializeConnectionToPlayer")).findAny().get(); InsnList ins = mn.instructions; // connection <--> field_71135_a <--> a // FD: oq/a net/minecraft/entity/player/EntityPlayerMP/field_71135_a // searching for line: p.connection = nethandlerplayserver; String search = obfuscated ? "a" : "connection"; String playerOwner = obfuscated ? "oq" : "net/minecraft/entity/player/EntityPlayerMP"; boolean found = false; while(index < ins.size()) { node = ins.get(index); if(node instanceof FieldInsnNode) { FieldInsnNode field = (FieldInsnNode) node; if(field.name.equals(search) && field.owner.equals(playerOwner)) { found = true; break; } } index++; } if(!found) { System.out.println("Start of player connection was not found"); return bytes; } node = ins.get(index); if(obfuscated) { ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onPlayerConnection", "(Loq;)V", false)); } else { ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onPlayerConnection", "(Lnet/minecraft/entity/player/EntityPlayerMP;)V", false)); } ins.insert(node, new VarInsnNode(Opcodes.ALOAD, 2)); // ----------------------------------------------------------------- // end of first part - inserting PlayerConnectionEvent hook // second part - removing join message // ----------------------------------------------------------------- // func_70005_c_ // MD: aed/h_ ()Ljava/lang/String; net/minecraft/entity/player/EntityPlayer/func_70005_c_ ()Ljava/lang/String; // Searching for: if (playerIn.getName().equalsIgnoreCase(s)) String start = "equalsIgnoreCase"; String startDesc = "(Ljava/lang/String;)Z"; // func_148539_a // MD: pl/a (Lhh;)V net/minecraft/server/management/PlayerList/func_148539_a (Lnet/minecraft/util/text/ITextComponent;)V String end = obfuscated ? "a" : "sendMessage"; String endDesc = obfuscated ? "(Lhh;)V" : "(Lnet/minecraft/util/text/ITextComponent;)V"; found = false; while(index < ins.size()) { node = ins.get(index); if(node instanceof MethodInsnNode) { MethodInsnNode mnode = (MethodInsnNode) node; if(mnode.name.equals(start) && mnode.desc.equals(startDesc)) { found = true; break; } } index++; } if(!found) { System.out.println("Start of player join message was not found"); return bytes; } // moving to real start of the block "p.getName()..." index -= 3; while(true) { node = ins.get(index); if(node instanceof MethodInsnNode) { MethodInsnNode mnode = (MethodInsnNode) node; if(mnode.name.equals(end) && mnode.desc.equals(endDesc)) { ins.remove(node); break; } } ins.remove(node); } // ----------------------------------------------------------------- // end of second part - removing join message // third part - inserting PlayerPreRespawnEvent hook // ----------------------------------------------------------------- // recreatePlayerEntity <--> func_72368_a <--> a // MD: pl/a (Loq;IZ)Loq; net/minecraft/server/management/PlayerList/func_72368_a // (Lnet/minecraft/entity/player/EntityPlayerMP;IZ)Lnet/minecraft/entity/player/EntityPlayerMP; if(obfuscated) { mn = classNode.methods.stream().filter(me -> me.name.equals("a")).filter(me -> "(Loq;IZ)Loq;".equals(me.desc)).findAny().get(); } else { mn = classNode.methods.stream().filter(me -> me.name.equals("recreatePlayerEntity")).findAny().get(); } ins = mn.instructions; node = ins.get(1); if(obfuscated) { ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onPlayerPreRespawn", "(Loq;IZ)V", false)); } else { ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onPlayerPreRespawn", "(Lnet/minecraft/entity/player/EntityPlayerMP;IZ)V", false)); } ins.insert(node, new VarInsnNode(Opcodes.ILOAD, 3)); ins.insert(node, new VarInsnNode(Opcodes.ILOAD, 2)); ins.insert(node, new VarInsnNode(Opcodes.ALOAD, 1)); // ----------------------------------------------------------------- // end of third part - inserting PlayerPreRespawnEvent hook // ----------------------------------------------------------------- ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); classNode.accept(writer); return writer.toByteArray(); } catch(Exception ex) { ex.printStackTrace(); } return bytes; } public byte[] patchMinecraftServer(String c, byte[] bytes) { try { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(bytes); classReader.accept(classNode, 0); // createCommandManager <--> func_175582_h <--> i // MD: net/minecraft/server/MinecraftServer/i ()Ldh; // MD: chd/i ()Ldh; net/minecraft/server/integrated/IntegratedServer/func_175582_h // ()Lnet/minecraft/command/ServerCommandManager; MethodNode mn; if(obfuscated) { mn = classNode.methods.stream().filter(me -> me.name.equals("i")).filter(me -> "()Ldh;".equals(me.desc)).findAny().get(); } else { mn = classNode.methods.stream().filter(me -> me.name.equals("createCommandManager")).findAny().get(); } InsnList ins = mn.instructions; ins.remove(ins.get(2)); ins.remove(ins.get(2)); ins.remove(ins.get(3)); /*if(obfuscated) { ins.insert(ins.get(2), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onCreateCommandManager", "(Ldh;)Lme/kcm/command/ModServerCommandManager;", false)); } else {*/ ins.insert(ins.get(2), new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onCreateCommandManager", "(Lnet/minecraft/server/MinecraftServer;)Lme/kcm/command/ModServerCommandManager;", false)); //} ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); classNode.accept(writer); return writer.toByteArray(); } catch(Exception ex) { ex.printStackTrace(); } return bytes; } public byte[] patchNetHandlerPlayServer(String c, byte[] bytes) { try { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(bytes); classReader.accept(classNode, 0); // public void onDisconnect(ITextComponent reason) // onDisconnect <--> func_147231_a <--> a // MD: pa/a (Lhh;)V net/minecraft/network/NetHandlerPlayServer/func_147231_a (Lnet/minecraft/util/text/ITextComponent;)V MethodNode mn; if(obfuscated) { mn = classNode.methods.stream().filter(me -> me.name.equals("a")).filter(me -> "(Lhh;)V".equals(me.desc)).findAny().get(); } else { mn = classNode.methods.stream().filter(me -> me.name.equals("onDisconnect")).findAny().get(); } InsnList ins = mn.instructions; // VarInsnNode 25 2 0 // FieldInsnNode 180 4 Lnet/minecraft/server/MinecraftServer; serverController net/minecraft/network/NetHandlerPlayServer // MethodInsnNode 182 5 ()Lnet/minecraft/server/management/PlayerList; false getPlayerList net/minecraft/server/MinecraftServer // VarInsnNode 25 2 2 // MethodInsnNode 182 5 (Lnet/minecraft/util/text/ITextComponent;)V false sendMessage net/minecraft/server/management/PlayerList // sendMessage <--> func_148539_a <--> a // MD: pl/a (Lhh;)V net/minecraft/server/management/PlayerList/func_148539_a (Lnet/minecraft/util/text/ITextComponent;)V int index = 0; AbstractInsnNode node; String search = obfuscated ? "a" : "sendMessage"; String owner = obfuscated ? "pl" : "net/minecraft/server/management/PlayerList"; boolean found = false; while(index < ins.size()) { node = ins.get(index); if(node instanceof MethodInsnNode) { MethodInsnNode field = (MethodInsnNode) node; if(field.name.equals(search) && field.owner.equals(owner)) { found = true; break; } } index++; } if(!found) { System.out.println("Start of sendMessage was not found"); return bytes; } index -= 4; for(int i = 0; i < 5; i++) { ins.remove(ins.get(index)); } ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); classNode.accept(writer); return writer.toByteArray(); } catch(Exception ex) { ex.printStackTrace(); } return bytes; } public byte[] patchCraftingManager(String c, byte[] bytes) { try { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(bytes); classReader.accept(classNode, 0); MethodNode mn; if(obfuscated) { mn = classNode.methods.stream().filter(me -> me.name.equals("b")).filter(me -> "(Lafy;Lamu;)Lakt;".equals(me.desc)).findAny().get(); } else { mn = classNode.methods.stream().filter(me -> me.name.equals("findMatchingRecipe")).findAny().get(); } InsnList ins = mn.instructions; // sendMessage <--> func_148539_a <--> a // MD: aku/b (Lafy;Lamu;)Lakt; net/minecraft/item/crafting/CraftingManager/func_192413_b // (Lnet/minecraft/inventory/InventoryCrafting;Lnet/minecraft/world/World;)Lnet/minecraft/item/crafting/IRecipe; AbstractInsnNode node = ins.get(1); ins.insert(node, new InsnNode(Opcodes.ARETURN)); if(obfuscated) { ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onfindMatchingRecipe", "(Lafy;Lamu;)Lakt;", false)); } else { ins.insert(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/kcm/events/Hooks", "onfindMatchingRecipe", "(Lnet/minecraft/inventory/InventoryCrafting;Lnet/minecraft/world/World;)Lnet/minecraft/item/crafting/IRecipe;", false)); } ins.insert(node, new VarInsnNode(Opcodes.ALOAD, 1)); ins.insert(node, new VarInsnNode(Opcodes.ALOAD, 0)); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); classNode.accept(writer); return writer.toByteArray(); } catch(Exception ex) { ex.printStackTrace(); } return bytes; } public String getString(AbstractInsnNode node) { StringBuilder sb = new StringBuilder(); sb.append(node.getClass().getSimpleName()); sb.append(" "); sb.append(node.getOpcode()); sb.append(" "); sb.append(node.getType()); sb.append(" "); if(node instanceof LineNumberNode) { LineNumberNode n = (LineNumberNode) node; sb.append(n.line); sb.append(" "); sb.append(n.start); } else if(node instanceof VarInsnNode) { VarInsnNode n = (VarInsnNode) node; sb.append(n.var); } else if(node instanceof MethodInsnNode) { MethodInsnNode n = (MethodInsnNode) node; sb.append(n.desc); sb.append(" "); sb.append(n.itf); sb.append(" "); sb.append(n.name); sb.append(" "); sb.append(n.owner); } else if(node instanceof FrameNode) { FrameNode n = (FrameNode) node; if(n.local != null) { n.local.forEach(s -> {sb.append(s); sb.append(" ");}); } if(n.stack != null) { n.stack.forEach(s -> {sb.append(s); sb.append(" ");}); } } else if(node instanceof InsnNode) { } else if(node instanceof FieldInsnNode) { FieldInsnNode n = (FieldInsnNode) node; sb.append(n.desc); sb.append(" "); sb.append(n.name); sb.append(" "); sb.append(n.owner); } return sb.toString(); } }