From 30f199e9a26b5ef9a30e6c63298e685f8b823ca6 Mon Sep 17 00:00:00 2001 From: octarine-noise Date: Fri, 27 Jun 2014 22:14:07 +0200 Subject: [PATCH] reworked class transformer --- .../BlockRenderTypeOverride.java | 6 +- .../client/BetterFoliageClient.java | 9 +- .../betterfoliage/common/util/DeobfNames.java | 6 + .../loader/BetterFoliageLoader.java | 1 + .../loader/BetterFoliageTransformer.java | 68 +++------- .../loader/EZTransformerBase.java | 128 ++++++++++++++++++ 6 files changed, 160 insertions(+), 58 deletions(-) create mode 100644 src/main/java/mods/betterfoliage/loader/EZTransformerBase.java diff --git a/src/main/java/mods/betterfoliage/BlockRenderTypeOverride.java b/src/main/java/mods/betterfoliage/BlockRenderTypeOverride.java index cb9257b..5a4e253 100644 --- a/src/main/java/mods/betterfoliage/BlockRenderTypeOverride.java +++ b/src/main/java/mods/betterfoliage/BlockRenderTypeOverride.java @@ -10,7 +10,7 @@ public class BlockRenderTypeOverride { public static IRenderTypeProvider provider = null; public static interface IRenderTypeProvider { - public int getRenderType(Block block); + public int getRenderType(int original, Block block); } /** Entry point from transformed RenderBlocks class. If no provider is given, @@ -18,7 +18,7 @@ public class BlockRenderTypeOverride { * @param block block instance * @return block render type */ - public static int getRenderType(Block block) { - return provider == null ? block.getRenderType() : provider.getRenderType(block); + public static int getRenderTypeOverride(int orig, Block block) { + return provider == null ? orig : provider.getRenderType(orig, block); } } \ No newline at end of file diff --git a/src/main/java/mods/betterfoliage/client/BetterFoliageClient.java b/src/main/java/mods/betterfoliage/client/BetterFoliageClient.java index e481fbf..8aa8fa7 100644 --- a/src/main/java/mods/betterfoliage/client/BetterFoliageClient.java +++ b/src/main/java/mods/betterfoliage/client/BetterFoliageClient.java @@ -57,15 +57,18 @@ public class BetterFoliageClient implements IRenderTypeProvider, ILeafTextureRec } } - public int getRenderType(Block block) { + public int getRenderType(int original, Block block) { + // universal sign for DON'T RENDER ME! + if (original == -1) return original; + if (Config.grassEnabled && block instanceof BlockGrass) return grassRenderId; if (Config.leavesEnabled) for (Class clazz : blockLeavesClasses) - if (clazz.isAssignableFrom(block.getClass())) + if (clazz.isAssignableFrom(block.getClass()) && (original == 0 || original >= 42)) return leavesRenderId; - return block.getRenderType(); + return original; } public boolean isLeafTexture(TextureAtlasSprite icon) { diff --git a/src/main/java/mods/betterfoliage/common/util/DeobfNames.java b/src/main/java/mods/betterfoliage/common/util/DeobfNames.java index f3d9d0c..797ddac 100644 --- a/src/main/java/mods/betterfoliage/common/util/DeobfNames.java +++ b/src/main/java/mods/betterfoliage/common/util/DeobfNames.java @@ -22,6 +22,12 @@ public class DeobfNames { /** Obfuscated signature of BlockRenderTypeOverride.getRenderType(Block) */ public static final String BRTO_GRT_SIG_OBF = "(Lahu;)I"; + /** MCP signature of BlockRenderTypeOverride.getRenderType(Block) */ + public static final String BRTO_GRTO_SIG_MCP = "(ILnet/minecraft/block/Block;)I"; + + /** Obfuscated signature of BlockRenderTypeOverride.getRenderType(Block) */ + public static final String BRTO_GRTO_SIG_OBF = "(ILahu;)I"; + /** MCP name of SimpleReloadableResourceManager.domainResourceManagers */ public static final String SRRM_DRM_MCP = "domainResourceManagers"; diff --git a/src/main/java/mods/betterfoliage/loader/BetterFoliageLoader.java b/src/main/java/mods/betterfoliage/loader/BetterFoliageLoader.java index a5bd141..a8951af 100644 --- a/src/main/java/mods/betterfoliage/loader/BetterFoliageLoader.java +++ b/src/main/java/mods/betterfoliage/loader/BetterFoliageLoader.java @@ -5,6 +5,7 @@ import java.util.Map; import cpw.mods.fml.relauncher.IFMLLoadingPlugin; @IFMLLoadingPlugin.MCVersion("1.7.2") +@IFMLLoadingPlugin.TransformerExclusions({"mods.betterfoliage.loader"}) public class BetterFoliageLoader implements IFMLLoadingPlugin { public String[] getASMTransformerClass() { diff --git a/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java b/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java index b264f41..011f40f 100644 --- a/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java +++ b/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java @@ -1,63 +1,27 @@ package mods.betterfoliage.loader; import mods.betterfoliage.common.util.DeobfNames; -import net.minecraft.launchwrapper.IClassTransformer; -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.ClassNode; +import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; -/** Transformer overriding the first line of RenderBlocks.renderBlockByRenderType() - * with the following instruction:

- * int l = mods.betterfoliage.BlockRenderTypeOverride.getRenderType(block);

- * - * @author octarine-noise - */ -public class BetterFoliageTransformer implements IClassTransformer { +public class BetterFoliageTransformer extends EZTransformerBase { - Logger log = LogManager.getLogger("BetterFoliageCore"); - - public byte[] transform(String name, String transformedName, byte[] basicClass) { - if (basicClass == null) return null; - - if (transformedName.equals("net.minecraft.client.renderer.RenderBlocks")) { - log.info(String.format("Found class %s", transformedName)); - ClassNode classNode = new ClassNode(); - ClassReader classReader = new ClassReader(basicClass); - classReader.accept(classNode, 0); - - for (MethodNode mn : classNode.methods) { - boolean found = false; - boolean obf = false; - if (mn.desc.equals(DeobfNames.RB_RBBRT_SIG_MCP) && (mn.name.equals(DeobfNames.RB_RBBRT_NAME_MCP))) { - found = true; - } else if (mn.desc.equals(DeobfNames.RB_RBBRT_SIG_OBF) && (mn.name.equals(DeobfNames.RB_RBBRT_NAME_OBF))) { - found = true; - obf = true; - } - if (found) { - log.info("Overriding RenderBlocks.renderBlockByRenderType()"); - int invokeNodeIdx = 0; - for (int idx = 0; idx < mn.instructions.size(); idx++) if (mn.instructions.get(idx) instanceof MethodInsnNode) { - invokeNodeIdx = idx; - break; - } - mn.instructions.remove(mn.instructions.get(invokeNodeIdx)); - MethodInsnNode replacement = new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/BlockRenderTypeOverride", "getRenderType", obf ? DeobfNames.BRTO_GRT_SIG_OBF : DeobfNames.BRTO_GRT_SIG_MCP); - mn.instructions.insertBefore(mn.instructions.get(invokeNodeIdx), replacement); - break; - } - } - - ClassWriter writer = new ClassWriter(0); - classNode.accept(writer); - return writer.toByteArray(); - } - return basicClass; + @MethodTransform(className="net.minecraft.client.renderer.RenderBlocks", + obf=@MethodMatch(name=DeobfNames.RB_RBBRT_NAME_OBF, signature=DeobfNames.RB_RBBRT_SIG_OBF), + deobf=@MethodMatch(name=DeobfNames.RB_RBBRT_NAME_MCP, signature=DeobfNames.RB_RBBRT_SIG_MCP), + log="Adding RenderBlocks.renderBlockByRenderType() render type ovverride") + public void handleRenderBlockOverride(MethodNode method, boolean obf) { + AbstractInsnNode invokeGetRenderType = findNext(method.instructions.getFirst(), matchInvokeAny()); + AbstractInsnNode storeRenderType = findNext(invokeGetRenderType, matchOpcode(Opcodes.ISTORE)); + insertAfter(method.instructions, storeRenderType, + new VarInsnNode(Opcodes.ILOAD, 5), + new VarInsnNode(Opcodes.ALOAD, 1), + new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/BlockRenderTypeOverride", "getRenderTypeOverride", obf ? DeobfNames.BRTO_GRTO_SIG_OBF : DeobfNames.BRTO_GRTO_SIG_MCP), + new VarInsnNode(Opcodes.ISTORE, 5) + ); } } diff --git a/src/main/java/mods/betterfoliage/loader/EZTransformerBase.java b/src/main/java/mods/betterfoliage/loader/EZTransformerBase.java new file mode 100644 index 0000000..dba9c7b --- /dev/null +++ b/src/main/java/mods/betterfoliage/loader/EZTransformerBase.java @@ -0,0 +1,128 @@ +package mods.betterfoliage.loader; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Method; + +import net.minecraft.launchwrapper.IClassTransformer; + +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.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +public class EZTransformerBase implements IClassTransformer { + + public static interface IInstructionMatch { + public boolean matches(AbstractInsnNode node); + } + + @Retention(RetentionPolicy.RUNTIME) + public static @interface MethodMatch { + public String name(); + public String signature(); + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public static @interface MethodTransform { + public String className(); + public MethodMatch deobf(); + public MethodMatch obf(); + public String log(); + } + + protected Logger logger = LogManager.getLogger(getClass().getSimpleName()); + + public byte[] transform(String name, String transformedName, byte[] basicClass) { + // read class + ClassNode classNode = new ClassNode(); + ClassReader classReader = new ClassReader(basicClass); + classReader.accept(classNode, 0); + boolean hasTransformed = false; + + for (Method classMethod : getClass().getMethods()) { + // check for annotated method with correct signature + MethodTransform annot = classMethod.getAnnotation(MethodTransform.class); + if (annot == null) continue; + if (classMethod.getParameterTypes().length != 2) continue; + if (!classMethod.getParameterTypes()[0].equals(MethodNode.class)) continue; + if (!classMethod.getParameterTypes()[1].equals(boolean.class)) continue; + + // try to find specified method in class + if (!transformedName.equals(annot.className())) continue; + for (MethodNode methodNode : classNode.methods) { + Boolean obf = null; + if (methodNode.name.equals(annot.obf().name()) && methodNode.desc.equals(annot.obf().signature())) { + obf = true; + } else if (methodNode.name.equals(annot.deobf().name()) && methodNode.desc.equals(annot.deobf().signature())) { + obf = false; + } + + if (obf != null) { + // transform + hasTransformed = true; + try { + classMethod.invoke(this, new Object[] {methodNode, obf}); + logger.info(String.format("%s: SUCCESS", annot.log())); + } catch (Exception e) { + logger.info(String.format("%s: FAILURE", annot.log())); + } + break; + } + } + } + + // return result + ClassWriter writer = new ClassWriter(0); + if (hasTransformed) classNode.accept(writer); + return !hasTransformed ? basicClass : writer.toByteArray(); + } + + protected AbstractInsnNode findNext(AbstractInsnNode start, IInstructionMatch match) { + AbstractInsnNode current = start; + while(current != null) { + if (match.matches(current)) break; + current = current.getNext(); + } + return current; + } + + protected AbstractInsnNode findPrevious(AbstractInsnNode start, IInstructionMatch match) { + AbstractInsnNode current = start; + while(current != null) { + if (match.matches(current)) break; + current = current.getPrevious(); + } + return current; + } + + protected static IInstructionMatch matchInvokeAny() { + return new IInstructionMatch() { + public boolean matches(AbstractInsnNode node) { + return node instanceof MethodInsnNode; + } + }; + } + + protected static IInstructionMatch matchOpcode(final int opcode) { + return new IInstructionMatch() { + public boolean matches(AbstractInsnNode node) { + return node.getOpcode() == opcode; + } + }; + } + + protected static void insertAfter(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) { + InsnList listAdd = new InsnList(); + for (AbstractInsnNode inst : added) listAdd.add(inst); + insnList.insert(node, listAdd); + } +}