diff --git a/build.gradle b/build.gradle index 66ff38c..39517da 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ minecraft { jar.baseName = 'BetterFoliage' group = 'com.github.octarine-noise' -version='0.9.7b' +version='0.9.7b-hotfix1' processResources { inputs.property "version", project.version diff --git a/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java b/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java index 5163c40..7c91a57 100644 --- a/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java +++ b/src/main/java/mods/betterfoliage/loader/BetterFoliageTransformer.java @@ -1,55 +1,78 @@ package mods.betterfoliage.loader; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.MethodInsnNode; +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.ClassNode; import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.VarInsnNode; import com.google.common.collect.ImmutableList; import cpw.mods.fml.relauncher.FMLInjectionData; -public class BetterFoliageTransformer extends EZTransformerBase { +public class BetterFoliageTransformer implements IClassTransformer { + protected Iterable transformers = ImmutableList.of( + new TransformRenderBlockOverride(), + new TransformShaderModBlockOverride() + ); + + protected Logger logger = LogManager.getLogger(getClass().getSimpleName()); + public BetterFoliageTransformer() { String mcVersion = FMLInjectionData.data()[4].toString(); if (!ImmutableList.of("1.7.2", "1.7.10").contains(mcVersion)) - logger.warn(String.format("Unsupported Minecraft version %s", mcVersion)); + logger.warn(String.format("Unsupported Minecraft version %s", mcVersion)); DeobfHelper.init(); } - @MethodTransform(className="net.minecraft.client.renderer.RenderBlocks", - methodName="renderBlockByRenderType", - signature="(Lnet/minecraft/block/Block;III)Z", - log="Applying RenderBlocks.renderBlockByRenderType() render type ovverride") - public void handleRenderBlockOverride(MethodNode method) { - AbstractInsnNode invokeGetRenderType = findNext(method.instructions.getFirst(), matchInvokeAny()); - AbstractInsnNode storeRenderType = findNext(invokeGetRenderType, matchOpcode(Opcodes.ISTORE)); - insertAfter(method.instructions, storeRenderType, - new VarInsnNode(Opcodes.ALOAD, 0), - new FieldInsnNode(Opcodes.GETFIELD, className("net/minecraft/client/renderer/RenderBlocks"), element("blockAccess"), signature("Lnet/minecraft/world/IBlockAccess;")), - new VarInsnNode(Opcodes.ILOAD, 2), - new VarInsnNode(Opcodes.ILOAD, 3), - new VarInsnNode(Opcodes.ILOAD, 4), - new VarInsnNode(Opcodes.ALOAD, 1), - new VarInsnNode(Opcodes.ILOAD, 5), - new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getRenderTypeOverride", signature("(Lnet/minecraft/world/IBlockAccess;IIILnet/minecraft/block/Block;I)I")), - new VarInsnNode(Opcodes.ISTORE, 5) - ); - } - - @MethodTransform(className="shadersmodcore.client.Shaders", - methodName="pushEntity", - signature="(Lnet/minecraft/client/renderer/RenderBlocks;Lnet/minecraft/block/Block;III)V", - log="Applying Shaders.pushEntity() block id ovverride") - public void handleGLSLBlockIDOverride(MethodNode method) { - AbstractInsnNode arrayStore = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IASTORE)); - insertAfter(method.instructions, arrayStore.getPrevious(), - new VarInsnNode(Opcodes.ALOAD, 1), - new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/ShadersModIntegration", "getBlockIdOverride", signature("(ILnet/minecraft/block/Block;)I")) - ); + @Override + public byte[] transform(String name, String transformedName, byte[] basicClass) { + // ??? + if (basicClass == null) return null; + + // read class + ClassNode classNode = new ClassNode(); + ClassReader classReader = new ClassReader(basicClass); + classReader.accept(classNode, 0); + boolean hasTransformed = false; + + for (MethodTransformerBase transformer : transformers) { + // try to find specified method in class + if (!transformedName.equals(transformer.getClassName())) continue; + + logger.debug(String.format("Found class: %s -> %s", name, transformedName)); + for (MethodNode methodNode : classNode.methods) { + logger.trace(String.format("Checking method: %s, sig: %s", methodNode.name, methodNode.desc)); + Boolean isObfuscated = null; + if (methodNode.name.equals(DeobfHelper.transformElementName(transformer.getMethodName())) && methodNode.desc.equals(DeobfHelper.transformSignature(transformer.getSignature()))) { + isObfuscated = true; + } else if (methodNode.name.equals(transformer.getMethodName()) && methodNode.desc.equals(transformer.getSignature())) { + isObfuscated = false; + } + + if (isObfuscated != null) { + // transform + hasTransformed = true; + try { + transformer.transform(methodNode, isObfuscated); + logger.info(String.format("%s: SUCCESS", transformer.getLogMessage())); + } catch (Exception e) { + logger.info(String.format("%s: FAILURE", transformer.getLogMessage())); + } + break; + } + } + } + + // return result + ClassWriter writer = new ClassWriter(0); + if (hasTransformed) classNode.accept(writer); + return !hasTransformed ? basicClass : writer.toByteArray(); } + } diff --git a/src/main/java/mods/betterfoliage/loader/EZTransformerBase.java b/src/main/java/mods/betterfoliage/loader/EZTransformerBase.java deleted file mode 100644 index fee25d6..0000000 --- a/src/main/java/mods/betterfoliage/loader/EZTransformerBase.java +++ /dev/null @@ -1,153 +0,0 @@ -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); - } - - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - public static @interface MethodTransform { - public String className(); - public String methodName(); - public String signature(); - public String log(); - } - - protected Logger logger = LogManager.getLogger(getClass().getSimpleName()); - - protected Boolean isObfuscated; - - public byte[] transform(String name, String transformedName, byte[] basicClass) { - // ??? - if (basicClass == null) return null; - - // 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 - String aClassName = null; - String aMethodName = null; - String aSignature = null; - String aLog = null; - synchronized (this) { - MethodTransform annotation = classMethod.getAnnotation(MethodTransform.class); - if (annotation != null) { - aClassName = annotation.className(); - aMethodName = annotation.methodName(); - aSignature = annotation.signature(); - aLog = annotation.log(); - } - } - - if (aClassName == null) continue; - if (classMethod.getParameterTypes().length != 1) continue; - if (!classMethod.getParameterTypes()[0].equals(MethodNode.class)) continue; - - // try to find specified method in class - if (!transformedName.equals(aClassName)) continue; - logger.debug(String.format("Found class: %s -> %s", name, transformedName)); - for (MethodNode methodNode : classNode.methods) { - logger.trace(String.format("Checking method: %s, sig: %s", methodNode.name, methodNode.desc)); - isObfuscated = null; - if (methodNode.name.equals(DeobfHelper.transformElementName(aMethodName)) && methodNode.desc.equals(DeobfHelper.transformSignature(aSignature))) { - isObfuscated = true; - } else if (methodNode.name.equals(aMethodName) && methodNode.desc.equals(aSignature)) { - isObfuscated = false; - } - - if (isObfuscated != null) { - // transform - hasTransformed = true; - try { - classMethod.invoke(this, new Object[] {methodNode}); - logger.info(String.format("%s: SUCCESS", aLog)); - } catch (Exception e) { - logger.info(String.format("%s: FAILURE", aLog)); - } - break; - } - } - } - - // return result - ClassWriter writer = new ClassWriter(0); - if (hasTransformed) classNode.accept(writer); - return !hasTransformed ? basicClass : writer.toByteArray(); - } - - protected String className(String className) { - return isObfuscated ? DeobfHelper.transformClassName(className) : className; - } - - protected String element(String fieldName) { - return isObfuscated ? DeobfHelper.transformElementName(fieldName) : fieldName; - } - - protected String signature(String signature) { - return isObfuscated ? DeobfHelper.transformSignature(signature) : signature; - } - - 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); - } -} diff --git a/src/main/java/mods/betterfoliage/loader/MethodTransformerBase.java b/src/main/java/mods/betterfoliage/loader/MethodTransformerBase.java new file mode 100644 index 0000000..61fa8d3 --- /dev/null +++ b/src/main/java/mods/betterfoliage/loader/MethodTransformerBase.java @@ -0,0 +1,72 @@ +package mods.betterfoliage.loader; + +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +public abstract class MethodTransformerBase { + + public static interface IInstructionMatch { + public boolean matches(AbstractInsnNode node); + } + + public abstract String getClassName(); + public abstract String getMethodName(); + public abstract String getSignature(); + public abstract String getLogMessage(); + + public abstract void transform(MethodNode method, boolean obf); + + protected static String className(String className, boolean isObfuscated) { + return isObfuscated ? DeobfHelper.transformClassName(className) : className; + } + + protected static String element(String fieldName, boolean isObfuscated) { + return isObfuscated ? DeobfHelper.transformElementName(fieldName) : fieldName; + } + + protected static String signature(String signature, boolean isObfuscated) { + return isObfuscated ? DeobfHelper.transformSignature(signature) : signature; + } + + 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 IInstructionMatch matchInvokeAny() { + return new IInstructionMatch() { + public boolean matches(AbstractInsnNode node) { + return node instanceof MethodInsnNode; + } + }; + } + + protected IInstructionMatch matchOpcode(final int opcode) { + return new IInstructionMatch() { + public boolean matches(AbstractInsnNode node) { + return node.getOpcode() == opcode; + } + }; + } + + protected void insertAfter(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) { + InsnList listAdd = new InsnList(); + for (AbstractInsnNode inst : added) listAdd.add(inst); + insnList.insert(node, listAdd); + } +} diff --git a/src/main/java/mods/betterfoliage/loader/TransformRenderBlockOverride.java b/src/main/java/mods/betterfoliage/loader/TransformRenderBlockOverride.java new file mode 100644 index 0000000..bee086f --- /dev/null +++ b/src/main/java/mods/betterfoliage/loader/TransformRenderBlockOverride.java @@ -0,0 +1,49 @@ +package mods.betterfoliage.loader; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; + +public class TransformRenderBlockOverride extends MethodTransformerBase { + + @Override + public String getClassName() { + return "net.minecraft.client.renderer.RenderBlocks"; + } + + @Override + public String getMethodName() { + return "renderBlockByRenderType"; + } + + @Override + public String getSignature() { + return "(Lnet/minecraft/block/Block;III)Z"; + } + + @Override + public String getLogMessage() { + return "Applying RenderBlocks.renderBlockByRenderType() render type ovverride"; + } + + @Override + public void transform(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.ALOAD, 0), + new FieldInsnNode(Opcodes.GETFIELD, className("net/minecraft/client/renderer/RenderBlocks", obf), element("blockAccess", obf), signature("Lnet/minecraft/world/IBlockAccess;", obf)), + new VarInsnNode(Opcodes.ILOAD, 2), + new VarInsnNode(Opcodes.ILOAD, 3), + new VarInsnNode(Opcodes.ILOAD, 4), + new VarInsnNode(Opcodes.ALOAD, 1), + new VarInsnNode(Opcodes.ILOAD, 5), + new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getRenderTypeOverride", signature("(Lnet/minecraft/world/IBlockAccess;IIILnet/minecraft/block/Block;I)I", obf)), + new VarInsnNode(Opcodes.ISTORE, 5) + ); + } + +} diff --git a/src/main/java/mods/betterfoliage/loader/TransformShaderModBlockOverride.java b/src/main/java/mods/betterfoliage/loader/TransformShaderModBlockOverride.java new file mode 100644 index 0000000..4631bc7 --- /dev/null +++ b/src/main/java/mods/betterfoliage/loader/TransformShaderModBlockOverride.java @@ -0,0 +1,40 @@ +package mods.betterfoliage.loader; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; + +public class TransformShaderModBlockOverride extends MethodTransformerBase { + + @Override + public String getClassName() { + return "shadersmodcore.client.Shaders"; + } + + @Override + public String getMethodName() { + return "pushEntity"; + } + + @Override + public String getSignature() { + return "(Lnet/minecraft/client/renderer/RenderBlocks;Lnet/minecraft/block/Block;III)V"; + } + + @Override + public String getLogMessage() { + return "Applying Shaders.pushEntity() block id ovverride"; + } + + @Override + public void transform(MethodNode method, boolean obf) { + AbstractInsnNode arrayStore = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IASTORE)); + insertAfter(method.instructions, arrayStore.getPrevious(), + new VarInsnNode(Opcodes.ALOAD, 1), + new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/ShadersModIntegration", "getBlockIdOverride", signature("(ILnet/minecraft/block/Block;)I", obf)) + ); + } + +}