Compare commits

...

9 Commits

Author SHA1 Message Date
octarine-noise
e351af2369 get rid of annotations in class transformer 2014-07-27 23:38:40 +02:00
octarine-noise
32a88fa824 Update README.md 2014-07-26 10:49:51 +02:00
octarine-noise
ec723113d3 bump version 2014-07-26 10:00:58 +02:00
octarine-noise
4e42f63c36 don't alter ShaderMod special textures 2014-07-25 20:59:21 +02:00
octarine-noise
0daf61583a fixed leaves and grass block data for ShadersMod integration 2014-07-25 15:57:27 +02:00
octarine-noise
504f033c2e only rebuild block ID cache on client world load 2014-07-24 17:59:06 +02:00
octarine-noise
fa099b1b97 fix deadlock issue during class transformation 2014-07-24 17:50:06 +02:00
octarine-noise
b0c0cd0d1b avoid Class.forName() in world loading event 2014-07-24 01:03:35 +02:00
octarine-noise
df69605521 Update README.md 2014-07-23 02:19:18 +02:00
10 changed files with 246 additions and 229 deletions

View File

@@ -6,6 +6,4 @@ More info: http://www.minecraftforum.net/topic/2776217-better-foliage/
Download
========
[BetterFoliage 0.9.4-beta] (http://goo.gl/pNBE23) (MC 1.7.2)
[BetterFoliage 0.9.4-beta] (http://goo.gl/ywT6Xg) (MC 1.7.10)
[BetterFoliage 0.9.7-beta] (http://goo.gl/xNVloR) (MC 1.7.2 & 1.7.10)

View File

@@ -22,7 +22,7 @@ minecraft {
jar.baseName = 'BetterFoliage'
group = 'com.github.octarine-noise'
version='0.9.6b'
version='0.9.7b-hotfix1'
processResources {
inputs.property "version", project.version

View File

@@ -2,16 +2,14 @@ package mods.betterfoliage.client;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.common.util.Utils;
import net.minecraft.block.Block;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.world.WorldEvent;
@@ -21,30 +19,22 @@ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
public class BlockMatcher {
public Set<String> whiteList = Sets.newHashSet();
public Set<String> blackList = Sets.newHashSet();
public Set<Class<?>> whiteList = Sets.newHashSet();
public Set<Class<?>> blackList = Sets.newHashSet();
public Set<Integer> blockIDs = Sets.newHashSet();
public void addClass(String className) {
if (className.startsWith("-"))
blackList.add(className.substring(1));
else
whiteList.add(className);
try {
if (className.startsWith("-"))
blackList.add(Class.forName(className.substring(1)));
else
whiteList.add(Class.forName(className));
} catch(ClassNotFoundException e) {}
}
public boolean matchesClass(Block block) {
for (String className : blackList) {
try {
Class<?> clazz = Class.forName(className);
if (clazz.isAssignableFrom(block.getClass())) return false;
} catch(ClassNotFoundException e) {}
}
for (String className : whiteList) {
try {
Class<?> clazz = Class.forName(className);
if (clazz.isAssignableFrom(block.getClass())) return true;
} catch(ClassNotFoundException e) {}
}
for (Class<?> clazz : blackList) if (clazz.isAssignableFrom(block.getClass())) return false;
for (Class<?> clazz : whiteList) if (clazz.isAssignableFrom(block.getClass())) return true;
return false;
}
@@ -70,40 +60,19 @@ public class BlockMatcher {
line = reader.readLine();
}
reader.close();
} catch (FileNotFoundException e) {
saveDefaults(file);
} catch (IOException e) {
} catch (Exception e) {
BetterFoliage.log.warn(String.format("Error reading configuration: %s", file.getName()));
}
}
public void saveDefaults(File file) {
FileWriter writer = null;
try {
writer = new FileWriter(file);
for (String className : whiteList) {
writer.write(className);
writer.write("\n");
}
for (String className : blackList) {
writer.write("-");
writer.write(className);
writer.write("\n");
}
writer.close();
} catch (FileNotFoundException e) {
saveDefaults(file);
} catch (IOException e) {
BetterFoliage.log.warn(String.format("Error writing default configuration: %s", file.getName()));
}
}
/** Caches block IDs on world load for fast lookup
* @param event
*/
@SuppressWarnings("unchecked")
@SubscribeEvent
public void handleWorldLoad(WorldEvent.Load event) {
if (!(event.world instanceof WorldClient)) return;
blockIDs.clear();
Iterator<Block> iter = Block.blockRegistry.iterator();
while (iter.hasNext()) {

View File

@@ -48,8 +48,8 @@ public class ShadersModIntegration {
}
public static int getBlockIdOverride(int original, Block block) {
if (BetterFoliageClient.leaves.matchesID(original & 0xFFFF)) return tallGrassEntityData;
if (BetterFoliageClient.crops.matchesID(original & 0xFFFF)) return leavesEntityData;
if (BetterFoliageClient.leaves.matchesID(original & 0xFFFF)) return leavesEntityData;
if (BetterFoliageClient.crops.matchesID(original & 0xFFFF)) return tallGrassEntityData;
return original;
}
}

View File

@@ -39,8 +39,14 @@ public class LeafTextureResource implements IResource {
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
try {
// load normal leaf texture
ResourceLocation origResource = new ResourceLocation(resLeaf.getResourceDomain(), "textures/blocks/" + resLeaf.getResourcePath());
if (origResource.getResourcePath().toLowerCase().endsWith("_n.png") || origResource.getResourcePath().toLowerCase().endsWith("_s.png")) {
// Don't alter ShaderMod normal and specular maps
fallbackResource = resourceManager.getResource(origResource);
return;
}
// load normal leaf texture
BufferedImage origImage = ImageIO.read(resourceManager.getResource(origResource).getInputStream());
if (origImage.getWidth() != origImage.getHeight()) return;
int size = origImage.getWidth();

View File

@@ -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<MethodTransformerBase> transformers = ImmutableList.<MethodTransformerBase>of(
new TransformRenderBlockOverride(),
new TransformShaderModBlockOverride()
);
protected Logger logger = LogManager.getLogger(getClass().getSimpleName());
public BetterFoliageTransformer() {
String mcVersion = FMLInjectionData.data()[4].toString();
if (!ImmutableList.<String>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();
}
}

View File

@@ -1,140 +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
MethodTransform annot = classMethod.getAnnotation(MethodTransform.class);
if (annot == 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(annot.className())) 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(annot.methodName())) && methodNode.desc.equals(DeobfHelper.transformSignature(annot.signature()))) {
isObfuscated = true;
} else if (methodNode.name.equals(annot.methodName()) && methodNode.desc.equals(annot.signature())) {
isObfuscated = false;
}
if (isObfuscated != null) {
// transform
hasTransformed = true;
try {
classMethod.invoke(this, new Object[] {methodNode});
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 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);
}
}

View File

@@ -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);
}
}

View File

@@ -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)
);
}
}

View File

@@ -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))
);
}
}