reworked class transformer
This commit is contained in:
@@ -10,7 +10,7 @@ public class BlockRenderTypeOverride {
|
|||||||
public static IRenderTypeProvider provider = null;
|
public static IRenderTypeProvider provider = null;
|
||||||
|
|
||||||
public static interface IRenderTypeProvider {
|
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,
|
/** Entry point from transformed RenderBlocks class. If no provider is given,
|
||||||
@@ -18,7 +18,7 @@ public class BlockRenderTypeOverride {
|
|||||||
* @param block block instance
|
* @param block block instance
|
||||||
* @return block render type
|
* @return block render type
|
||||||
*/
|
*/
|
||||||
public static int getRenderType(Block block) {
|
public static int getRenderTypeOverride(int orig, Block block) {
|
||||||
return provider == null ? block.getRenderType() : provider.getRenderType(block);
|
return provider == null ? orig : provider.getRenderType(orig, block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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.grassEnabled && block instanceof BlockGrass) return grassRenderId;
|
||||||
|
|
||||||
if (Config.leavesEnabled)
|
if (Config.leavesEnabled)
|
||||||
for (Class<?> clazz : blockLeavesClasses)
|
for (Class<?> clazz : blockLeavesClasses)
|
||||||
if (clazz.isAssignableFrom(block.getClass()))
|
if (clazz.isAssignableFrom(block.getClass()) && (original == 0 || original >= 42))
|
||||||
return leavesRenderId;
|
return leavesRenderId;
|
||||||
|
|
||||||
return block.getRenderType();
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLeafTexture(TextureAtlasSprite icon) {
|
public boolean isLeafTexture(TextureAtlasSprite icon) {
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ public class DeobfNames {
|
|||||||
/** Obfuscated signature of BlockRenderTypeOverride.getRenderType(Block) */
|
/** Obfuscated signature of BlockRenderTypeOverride.getRenderType(Block) */
|
||||||
public static final String BRTO_GRT_SIG_OBF = "(Lahu;)I";
|
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 */
|
/** MCP name of SimpleReloadableResourceManager.domainResourceManagers */
|
||||||
public static final String SRRM_DRM_MCP = "domainResourceManagers";
|
public static final String SRRM_DRM_MCP = "domainResourceManagers";
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import java.util.Map;
|
|||||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||||
|
|
||||||
@IFMLLoadingPlugin.MCVersion("1.7.2")
|
@IFMLLoadingPlugin.MCVersion("1.7.2")
|
||||||
|
@IFMLLoadingPlugin.TransformerExclusions({"mods.betterfoliage.loader"})
|
||||||
public class BetterFoliageLoader implements IFMLLoadingPlugin {
|
public class BetterFoliageLoader implements IFMLLoadingPlugin {
|
||||||
|
|
||||||
public String[] getASMTransformerClass() {
|
public String[] getASMTransformerClass() {
|
||||||
|
|||||||
@@ -1,63 +1,27 @@
|
|||||||
package mods.betterfoliage.loader;
|
package mods.betterfoliage.loader;
|
||||||
|
|
||||||
import mods.betterfoliage.common.util.DeobfNames;
|
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.Opcodes;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
import org.objectweb.asm.tree.MethodInsnNode;
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
import org.objectweb.asm.tree.MethodNode;
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
import org.objectweb.asm.tree.VarInsnNode;
|
||||||
|
|
||||||
/** Transformer overriding the first line of RenderBlocks.renderBlockByRenderType()
|
public class BetterFoliageTransformer extends EZTransformerBase {
|
||||||
* with the following instruction:<br/><br/>
|
|
||||||
* int l = mods.betterfoliage.BlockRenderTypeOverride.getRenderType(block);<br/><br/>
|
|
||||||
*
|
|
||||||
* @author octarine-noise
|
|
||||||
*/
|
|
||||||
public class BetterFoliageTransformer implements IClassTransformer {
|
|
||||||
|
|
||||||
Logger log = LogManager.getLogger("BetterFoliageCore");
|
@MethodTransform(className="net.minecraft.client.renderer.RenderBlocks",
|
||||||
|
obf=@MethodMatch(name=DeobfNames.RB_RBBRT_NAME_OBF, signature=DeobfNames.RB_RBBRT_SIG_OBF),
|
||||||
public byte[] transform(String name, String transformedName, byte[] basicClass) {
|
deobf=@MethodMatch(name=DeobfNames.RB_RBBRT_NAME_MCP, signature=DeobfNames.RB_RBBRT_SIG_MCP),
|
||||||
if (basicClass == null) return null;
|
log="Adding RenderBlocks.renderBlockByRenderType() render type ovverride")
|
||||||
|
public void handleRenderBlockOverride(MethodNode method, boolean obf) {
|
||||||
if (transformedName.equals("net.minecraft.client.renderer.RenderBlocks")) {
|
AbstractInsnNode invokeGetRenderType = findNext(method.instructions.getFirst(), matchInvokeAny());
|
||||||
log.info(String.format("Found class %s", transformedName));
|
AbstractInsnNode storeRenderType = findNext(invokeGetRenderType, matchOpcode(Opcodes.ISTORE));
|
||||||
ClassNode classNode = new ClassNode();
|
insertAfter(method.instructions, storeRenderType,
|
||||||
ClassReader classReader = new ClassReader(basicClass);
|
new VarInsnNode(Opcodes.ILOAD, 5),
|
||||||
classReader.accept(classNode, 0);
|
new VarInsnNode(Opcodes.ALOAD, 1),
|
||||||
|
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/BlockRenderTypeOverride", "getRenderTypeOverride", obf ? DeobfNames.BRTO_GRTO_SIG_OBF : DeobfNames.BRTO_GRTO_SIG_MCP),
|
||||||
for (MethodNode mn : classNode.methods) {
|
new VarInsnNode(Opcodes.ISTORE, 5)
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
128
src/main/java/mods/betterfoliage/loader/EZTransformerBase.java
Normal file
128
src/main/java/mods/betterfoliage/loader/EZTransformerBase.java
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user