package lumien.extendedpotions.asm; import static org.objectweb.asm.Opcodes.*; import net.minecraft.launchwrapper.IClassTransformer; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; 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.FieldNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.VarInsnNode; public class ClassTransformer implements IClassTransformer { Logger logger = LogManager.getLogger("ExtendedPotionsCore"); public ClassTransformer() { logger.log(Level.DEBUG, "Starting Class Transformation"); } @Override public byte[] transform(String name, String transformedName, byte[] basicClass) { if (transformedName.equals("net.minecraft.network.play.server.S1DPacketEntityEffect")) { return patchEffectPacket(basicClass); } else if (transformedName.equals("net.minecraft.network.play.server.S1EPacketRemoveEntityEffect")) { return patchRemoveEffectPacket(basicClass); } else if (transformedName.equals("net.minecraft.client.network.NetHandlerPlayClient")) { return patchClientNetHandlerClass(basicClass); } else if (transformedName.equals("net.minecraft.potion.PotionEffect")) { return patchPotionEffect(basicClass); } else if (transformedName.equals("net.minecraft.potion.Potion")) { return patchPotion(basicClass); } return basicClass; } private byte[] patchPotion(byte[] basicClass) { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(basicClass); classReader.accept(classNode, 0); logger.log(Level.DEBUG, "Found Potion Class: " + classNode.name); MethodNode constructor = null; for (MethodNode mn : classNode.methods) { if (mn.name.equals("<init>") && mn.desc.equals("(ILnet/minecraft/util/ResourceLocation;ZI)V")) { constructor = mn; break; } } if (constructor != null) { for (int i=0;i<constructor.instructions.size();i++) { AbstractInsnNode ain = constructor.instructions.get(i); if (ain instanceof MethodInsnNode) { MethodInsnNode min = (MethodInsnNode) ain; if (min.getOpcode()==INVOKESPECIAL && min.owner.equals("java/lang/Object") && min.name.equals("<init>") && min.desc.equals("()V")) { logger.log(Level.DEBUG, " - Found Constructor"); InsnList toInsert = new InsnList(); toInsert.add(new VarInsnNode(ALOAD, 0)); toInsert.add(new VarInsnNode(ILOAD, 1)); toInsert.add(new MethodInsnNode(INVOKESTATIC, "lumien/extendedpotions/ErrorHandler", "checkID", "(Lnet/minecraft/potion/Potion;I)V", false)); constructor.instructions.insert(min,toInsert); } } } } ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); } private byte[] patchPotionEffect(byte[] basicClass) { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(basicClass); classReader.accept(classNode, 0); logger.log(Level.DEBUG, "Found PotionEffect Class: " + classNode.name); MethodNode readCustomPotionEffectFromNBT = null; MethodNode writeCustomPotionEffectToNBT = null; for (MethodNode mn : classNode.methods) { if (mn.name.equals(MCPNames.method("func_82722_b"))) { readCustomPotionEffectFromNBT = mn; } else if (mn.name.equals(MCPNames.method("func_82719_a"))) { writeCustomPotionEffectToNBT = mn; } } if (readCustomPotionEffectFromNBT != null) { logger.log(Level.DEBUG, " - Found readCustomPotionEffectFromNBT"); InsnList toInsert = new InsnList(); toInsert.add(new VarInsnNode(ALOAD, 0)); toInsert.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "lumien/extendedpotions/SavingHandler", "readCustomPotionEffectFromNBT", "(Lnet/minecraft/nbt/NBTTagCompound;)Lnet/minecraft/potion/PotionEffect;", false)); toInsert.add(new InsnNode(Opcodes.ARETURN)); readCustomPotionEffectFromNBT.instructions.insert(toInsert); } if (writeCustomPotionEffectToNBT != null) { logger.log(Level.DEBUG, " - Found writeCustomPotionEffectToNBT"); InsnList toInsert = new InsnList(); toInsert.add(new VarInsnNode(ALOAD, 0)); toInsert.add(new VarInsnNode(ALOAD, 1)); toInsert.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "lumien/extendedpotions/SavingHandler", "writeCustomPotionEffectToNBT", "(Lnet/minecraft/potion/PotionEffect;Lnet/minecraft/nbt/NBTTagCompound;)Lnet/minecraft/nbt/NBTTagCompound;", false)); toInsert.add(new InsnNode(Opcodes.ARETURN)); writeCustomPotionEffectToNBT.instructions.insert(toInsert); } ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); } private byte[] patchClientNetHandlerClass(byte[] basicClass) { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(basicClass); classReader.accept(classNode, 0); logger.log(Level.DEBUG, "Found NetHandlerPlayClient Class: " + classNode.name); MethodNode handleEffect = null; for (MethodNode mn : classNode.methods) { if (mn.name.equals(MCPNames.method("func_147260_a"))) { handleEffect = mn; } } if (handleEffect != null) { logger.log(Level.DEBUG, " - Found handleEffect"); for (int i = 0; i < handleEffect.instructions.size(); i++) { AbstractInsnNode ain = handleEffect.instructions.get(i); if (ain instanceof MethodInsnNode) { MethodInsnNode min = (MethodInsnNode) ain; if (min.owner.equals("net/minecraft/network/play/server/S1DPacketEntityEffect") && min.name.equals("func_149427_e")) { logger.log(Level.DEBUG, " - Changed handleEffect to refer to proper method"); min.desc = "()I"; } } } } ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); } private byte[] patchEffectPacket(byte[] basicClass) { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(basicClass); classReader.accept(classNode, 0); logger.log(Level.DEBUG, "Found S1DPacketEntityEffect Class: " + classNode.name); for (FieldNode fn : classNode.fields) { if (fn.name.equals("field_149432_b")) { logger.log(Level.DEBUG, " - Changed description of field_149432_b from B to I"); fn.desc = "I"; } } MethodNode constructor = null; MethodNode getId = null; MethodNode readPacketData = null; MethodNode writePacketData = null; for (MethodNode mn : classNode.methods) { if (mn.name.equals("<init>") && mn.desc.equals("(ILnet/minecraft/potion/PotionEffect;)V")) { constructor = mn; } else if (mn.name.equals("func_149427_e")) { getId = mn; } else if (mn.name.equals(MCPNames.method("func_148837_a"))) { readPacketData = mn; } else if (mn.name.equals(MCPNames.method("func_148840_b"))) { writePacketData = mn; } } if (getId != null) { logger.log(Level.DEBUG, " - Changed getId to Integer"); getId.desc = "()I"; for (AbstractInsnNode ain : getId.instructions.toArray()) { if (ain instanceof FieldInsnNode) { FieldInsnNode fin = (FieldInsnNode) ain; fin.desc = "I"; } } } if (constructor != null) { for (int i = 0; i < constructor.instructions.size(); i++) { AbstractInsnNode ain = constructor.instructions.get(i); if (ain instanceof MethodInsnNode) { MethodInsnNode min = (MethodInsnNode) ain; if (min.name.equals(MCPNames.method("func_76456_a"))) { logger.log(Level.DEBUG, " - Changed Constructor to properly save in field"); for (int c = 0; c < 3; c++) { AbstractInsnNode n = constructor.instructions.get(i + 1); constructor.instructions.remove(n); } } } else if (ain instanceof FieldInsnNode) { FieldInsnNode fin = (FieldInsnNode) ain; if (fin.name.equals("field_149432_b")) { fin.desc = "I"; } } } } if (readPacketData != null) { logger.log(Level.DEBUG, " - Patching readPacketData"); for (int i = 0; i < readPacketData.instructions.size(); i++) { AbstractInsnNode ain = readPacketData.instructions.get(i); if (ain instanceof FieldInsnNode) { FieldInsnNode fin = (FieldInsnNode) ain; if (fin.name.equals("field_149432_b")) { fin.desc = "I"; } } if (ain instanceof MethodInsnNode) { MethodInsnNode min = (MethodInsnNode) ain; if (min.owner.equals("net/minecraft/network/PacketBuffer")) { if (min.name.equals("readByte") && readPacketData.instructions.get(i + 1) instanceof FieldInsnNode) { FieldInsnNode fin = (FieldInsnNode) readPacketData.instructions.get(i + 1); if (fin.name.equals("field_149432_b")) { logger.log(Level.DEBUG, " - Changed reading to readInt"); MethodInsnNode newMethod = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "net/minecraft/network/PacketBuffer", "readInt", "()I", false); readPacketData.instructions.insert(min, newMethod); readPacketData.instructions.remove(min); } } } } } } if (writePacketData != null) { logger.log(Level.DEBUG, " - Patching writePacketData"); for (int i = 0; i < writePacketData.instructions.size(); i++) { AbstractInsnNode ain = writePacketData.instructions.get(i); if (ain instanceof FieldInsnNode) { FieldInsnNode fin = (FieldInsnNode) ain; if (fin.name.equals("field_149432_b")) { fin.desc = "I"; } } if (ain instanceof MethodInsnNode) { MethodInsnNode min = (MethodInsnNode) ain; if (min.owner.equals("net/minecraft/network/PacketBuffer")) { if (min.name.equals("writeByte") && writePacketData.instructions.get(i - 1) instanceof FieldInsnNode) { FieldInsnNode fin = (FieldInsnNode) writePacketData.instructions.get(i - 1); if (fin.name.equals("field_149432_b")) { logger.log(Level.DEBUG, " - Changed writing to writeInt"); MethodInsnNode newMethod = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "net/minecraft/network/PacketBuffer", "writeInt", "(I)Lio/netty/buffer/ByteBuf;", false); writePacketData.instructions.insert(min, newMethod); writePacketData.instructions.remove(min); } } } } } } ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); } private byte[] patchRemoveEffectPacket(byte[] basicClass) { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(basicClass); classReader.accept(classNode, 0); logger.log(Level.DEBUG, "Found S1EPacketRemoveEntityEffect Class: " + classNode.name); MethodNode constructor = null; MethodNode getId = null; MethodNode readPacketData = null; MethodNode writePacketData = null; for (MethodNode mn : classNode.methods) { if (mn.name.equals("<init>") && mn.desc.equals("(ILnet/minecraft/potion/PotionEffect;)V")) { constructor = mn; } else if (mn.name.equals("func_149075_d")) { getId = mn; } else if (mn.name.equals(MCPNames.method("func_148837_a"))) { readPacketData = mn; } else if (mn.name.equals(MCPNames.method("func_148840_b"))) { writePacketData = mn; } } if (getId != null) { logger.log(Level.DEBUG, " - Changed getId to Integer"); getId.desc = "()I"; for (AbstractInsnNode ain : getId.instructions.toArray()) { if (ain instanceof FieldInsnNode) { FieldInsnNode fin = (FieldInsnNode) ain; fin.desc = "I"; } } } if (constructor != null) { for (int i = 0; i < constructor.instructions.size(); i++) { AbstractInsnNode ain = constructor.instructions.get(i); if (ain instanceof FieldInsnNode) { FieldInsnNode fin = (FieldInsnNode) ain; if (fin.name.equals("field_149078_b")) { fin.desc = "I"; } } } } if (readPacketData != null) { logger.log(Level.DEBUG, " - Patching readPacketData"); for (int i = 0; i < readPacketData.instructions.size(); i++) { AbstractInsnNode ain = readPacketData.instructions.get(i); if (ain instanceof MethodInsnNode) { MethodInsnNode min = (MethodInsnNode) ain; if (min.owner.equals("net/minecraft/network/PacketBuffer")) { if (min.name.equals("readUnsignedByte") && readPacketData.instructions.get(i + 1) instanceof FieldInsnNode) { FieldInsnNode fin = (FieldInsnNode) readPacketData.instructions.get(i + 1); if (fin.name.equals("field_149078_b")) { logger.log(Level.DEBUG, " - Changed reading to readInt"); MethodInsnNode newMethod = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "net/minecraft/network/PacketBuffer", "readInt", "()I", false); readPacketData.instructions.insert(min, newMethod); readPacketData.instructions.remove(min); } } } } } } if (writePacketData != null) { logger.log(Level.DEBUG, " - Patching writePacketData"); for (int i = 0; i < writePacketData.instructions.size(); i++) { AbstractInsnNode ain = writePacketData.instructions.get(i); if (ain instanceof MethodInsnNode) { MethodInsnNode min = (MethodInsnNode) ain; if (min.owner.equals("net/minecraft/network/PacketBuffer")) { if (min.name.equals("writeByte") && writePacketData.instructions.get(i - 1) instanceof FieldInsnNode) { FieldInsnNode fin = (FieldInsnNode) writePacketData.instructions.get(i - 1); if (fin.name.equals("field_149078_b")) { logger.log(Level.DEBUG, " - Changed writing to writeInt"); MethodInsnNode newMethod = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "net/minecraft/network/PacketBuffer", "writeInt", "(I)Lio/netty/buffer/ByteBuf;", false); writePacketData.instructions.insert(min, newMethod); writePacketData.instructions.remove(min); } } } } } } ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); } private byte[] patchDummyClass(byte[] basicClass) { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(basicClass); classReader.accept(classNode, 0); logger.log(Level.DEBUG, "Found Dummy Class: " + classNode.name); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); } }