package coloredlightscore.src.asm.transformer;
import coloredlightscore.src.asm.transformer.core.ASMUtils;
import coloredlightscore.src.asm.transformer.core.MethodTransformer;
import coloredlightscore.src.asm.transformer.core.NameMapper;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import static coloredlightscore.src.asm.ColoredLightsCoreDummyContainer.CLLog;
/**
* Fields added to net.minecraft.world.chunk.storage.ExtendedBlockStorage:
* NibbleArray rColorArray
* NibbleArray gColorArray
* NibbleArray bColorArray
* <p/>
* Methods added to net.minecraft.world.chunk.storage.ExtendedBlockStorage:
* setRedColorArray
* setGreenColorArray
* setBlueColorArray
* getRedColorArray
* getGreenColorArray
* getBlueColorArray
* <p/>
* Methods modified on net.minecraft.world.chunk.storage.ExtendedBlockStorage:
* setExtBlocklightValue
* getExtBlocklightValue
*
* @author Josh
*/
public class TransformExtendedBlockStorage extends MethodTransformer {
private boolean addedFields = false;
private FieldNode rColorArray;
private FieldNode gColorArray;
private FieldNode bColorArray;
private FieldNode blockLSBArray;
private final String NIBBLE_ARRAY = "Lnet.minecraft.world.chunk.NibbleArray;";
private final String EBS_CLASSNAME = "net.minecraft.world.chunk.storage.ExtendedBlockStorage";
private final String SET_BLOCK_LIGHT_VALUE = "setExtBlocklightValue (IIII)V";
private final String GET_BLOCK_LIGHT_VALUE = "getExtBlocklightValue (III)I";
private boolean addedMethods = false;
@Override
protected boolean transforms(ClassNode clazz, MethodNode method) {
return NameMapper.getInstance().isMethod(method, EBS_CLASSNAME, SET_BLOCK_LIGHT_VALUE)
| NameMapper.getInstance().isMethod(method, EBS_CLASSNAME, GET_BLOCK_LIGHT_VALUE)
| method.name.equals("<init>");
}
@Override
protected boolean transform(ClassNode clazz, MethodNode method) {
boolean changed = false;
if (!addedFields) {
addRGBNibbleArrays(clazz);
changed = true;
}
if (NameMapper.getInstance().isMethod(method, EBS_CLASSNAME, SET_BLOCK_LIGHT_VALUE)) {
transformSetExtBlocklightValue(clazz, method);
changed = true;
}
if (NameMapper.getInstance().isMethod(method, EBS_CLASSNAME, GET_BLOCK_LIGHT_VALUE)) {
transformGetExtBlocklightValue(clazz, method);
changed = true;
}
if (method.name.equals("<init>")) {
transformConstructor(clazz, method);
changed = true;
}
return changed;
}
@Override
protected boolean postTransformClass(ClassNode clazz) {
if (!addedMethods) {
addRGBNibbleArrayMethods(clazz);
return true;
} else
return false;
}
@Override
protected boolean transforms(String className) {
//return className.equals(NameMapper.getInstance().getClassName(EBS_CLASSNAME).replace('/', '.'));
return className.equals(EBS_CLASSNAME);
}
private void addRGBNibbleArrays(ClassNode clazz) {
Type typeNibbleArray = NameMapper.getInstance().getType(NIBBLE_ARRAY);
//Type typeNibbleArray = Type.getType(net.minecraft.world.chunk.NibbleArray.class);
for (FieldNode f : clazz.fields)
if (f.name.equals("blockLSBArray") || f.name.equals("field_76680_d") || (f.name.equals("d") && f.desc.equals("[B")))
blockLSBArray = f;
if (blockLSBArray == null)
CLLog.error("TransformExtendedBlockStorage: Failed to find blockLSBArray!");
rColorArray = new FieldNode(Opcodes.ACC_PUBLIC, "rColorArray", typeNibbleArray.getDescriptor(), null, null);
gColorArray = new FieldNode(Opcodes.ACC_PUBLIC, "gColorArray", typeNibbleArray.getDescriptor(), null, null);
bColorArray = new FieldNode(Opcodes.ACC_PUBLIC, "bColorArray", typeNibbleArray.getDescriptor(), null, null);
clazz.fields.add(rColorArray);
clazz.fields.add(gColorArray);
clazz.fields.add(bColorArray);
CLLog.info("Added RGB color arrays to ExtendedBlockStorage, type " + typeNibbleArray.getDescriptor());
addedFields = true;
}
private boolean addRGBNibbleArrayMethods(ClassNode clazz) {
if (addedFields && !addedMethods) {
// These new methods are required for storing/loading the new nibble arrays to disk
clazz.methods.add(ASMUtils.generateSetterMethod(clazz.name, "setRedColorArray", rColorArray.name, rColorArray.desc));
clazz.methods.add(ASMUtils.generateSetterMethod(clazz.name, "setGreenColorArray", gColorArray.name, gColorArray.desc));
clazz.methods.add(ASMUtils.generateSetterMethod(clazz.name, "setBlueColorArray", bColorArray.name, bColorArray.desc));
clazz.methods.add(ASMUtils.generateGetterMethod(clazz.name, "getRedColorArray", rColorArray.name, rColorArray.desc));
clazz.methods.add(ASMUtils.generateGetterMethod(clazz.name, "getGreenColorArray", gColorArray.name, gColorArray.desc));
clazz.methods.add(ASMUtils.generateGetterMethod(clazz.name, "getBlueColorArray", bColorArray.name, bColorArray.desc));
addedMethods = true;
}
return addedMethods;
}
private void transformConstructor(ClassNode clazz, MethodNode m) {
String ebsInternalName = clazz.name;
Type typeNibbleArray = NameMapper.getInstance().getType(NIBBLE_ARRAY);
//Type typeNibbleArray = Type.getType(net.minecraft.world.chunk.NibbleArray.class);
// Initializes array the same length as blockLSBArray:
// 18 aload_0 [this]
// 19 new net.minecraft.world.chunk.NibbleArray [4]
// 22 dup
// 23 aload_0 [this]
// 24 getfield net.minecraft.world.chunk.storage.ExtendedBlockStorage.blockLSBArray : byte[] [3]
// 27 arraylength
// 28 iconst_4
// 29 invokespecial net.minecraft.world.chunk.NibbleArray(int, int) [5]
// 32 putfield net.minecraft.world.chunk.storage.ExtendedBlockStorage.blockMetadataArray : net.minecraft.world.chunk.NibbleArray [6]
// Remove the return, to be re-inserted later
AbstractInsnNode returnNode = ASMUtils.findLastReturn(m);
if (returnNode == null) {
CLLog.error(String.format("Failed to find RETURN statement on %s/%s %s", clazz.name, m.name, m.desc));
} else
m.instructions.remove(returnNode);
// Initialize rColorArray
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
m.instructions.add(new TypeInsnNode(Opcodes.NEW, typeNibbleArray.getInternalName()));
m.instructions.add(new InsnNode(Opcodes.DUP));
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
m.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, ebsInternalName, blockLSBArray.name, blockLSBArray.desc));
m.instructions.add(new InsnNode(Opcodes.ARRAYLENGTH));
m.instructions.add(new InsnNode(Opcodes.ICONST_4));
m.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, typeNibbleArray.getInternalName(), "<init>", "(II)V"));
m.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, ebsInternalName, rColorArray.name, rColorArray.desc));
// Initialize gColorArray
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
m.instructions.add(new TypeInsnNode(Opcodes.NEW, typeNibbleArray.getInternalName()));
m.instructions.add(new InsnNode(Opcodes.DUP));
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
m.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, ebsInternalName, blockLSBArray.name, blockLSBArray.desc));
m.instructions.add(new InsnNode(Opcodes.ARRAYLENGTH));
m.instructions.add(new InsnNode(Opcodes.ICONST_4));
m.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, typeNibbleArray.getInternalName(), "<init>", "(II)V"));
m.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, ebsInternalName, gColorArray.name, gColorArray.desc));
// Initialize bColorArray
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
m.instructions.add(new TypeInsnNode(Opcodes.NEW, typeNibbleArray.getInternalName()));
m.instructions.add(new InsnNode(Opcodes.DUP));
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
m.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, ebsInternalName, blockLSBArray.name, blockLSBArray.desc));
m.instructions.add(new InsnNode(Opcodes.ARRAYLENGTH));
m.instructions.add(new InsnNode(Opcodes.ICONST_4));
m.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, typeNibbleArray.getInternalName(), "<init>", "(II)V"));
m.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, ebsInternalName, bColorArray.name, bColorArray.desc));
m.instructions.add(returnNode);
}
private void transformSetExtBlocklightValue(ClassNode clazz, MethodNode m) {
String ebsInternalName = clazz.name;
Type typeNibbleArray = NameMapper.getInstance().getType(NIBBLE_ARRAY);
String nibbleArraySet = NameMapper.getInstance().getMethodName("net/minecraft/world/chunk/NibbleArray", "set (IIII)V");
// Already in stock method:
// 0 aload_0 [this]
// 1 getfield net.minecraft.world.chunk.storage.ExtendedBlockStorage.blocklightArray : net.minecraft.world.chunk.NibbleArray [91]
// 4 iload_1 [x]
// 5 iload_2 [y]
// 6 iload_3 [z]
// 7 iload 4 [lightValue]
// 9 invokevirtual net.minecraft.world.chunk.NibbleArray.set(int, int, int, int) : void [93]
// return is there by default - remove for now
AbstractInsnNode oldReturn = ASMUtils.findLastReturn(m);
if (oldReturn != null)
m.instructions.remove(oldReturn);
// Colored light mod: Store red value
// 12 aload_0 [this]
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
// 13 getfield net.minecraft.world.chunk.storage.ExtendedBlockStorage.rColorArray : net.minecraft.world.chunk.NibbleArray [98]
m.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, ebsInternalName, rColorArray.name, rColorArray.desc));
// 16 iload_1 [x]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 1));
// 17 iload_2 [y]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 2));
// 18 iload_3 [z]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 3));
// 19 iload 4 [lightValue]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 4));
// 21 iconst_5
m.instructions.add(new InsnNode(Opcodes.ICONST_5));
// 22 ishr
m.instructions.add(new InsnNode(Opcodes.ISHR));
// 23 bipush 15
m.instructions.add(new IntInsnNode(Opcodes.BIPUSH, 15));
// 25 iand
m.instructions.add(new InsnNode(Opcodes.IAND));
// 26 invokevirtual net.minecraft.world.chunk.NibbleArray.set(int, int, int, int) : void [93]
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, typeNibbleArray.getInternalName(), nibbleArraySet, "(IIII)V"));
// Colored light mod: Store green value
// 29 aload_0 [this]
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
// 30 getfield net.minecraft.world.chunk.storage.ExtendedBlockStorage.gColorArray : net.minecraft.world.chunk.NibbleArray [100]
m.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, ebsInternalName, gColorArray.name, gColorArray.desc));
// 33 iload_1 [x]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 1));
// 34 iload_2 [y]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 2));
// 35 iload_3 [z]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 3));
// 36 iload 4 [lightValue]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 4));
// 38 bipush 10
m.instructions.add(new IntInsnNode(Opcodes.BIPUSH, 10));
// 40 ishr
m.instructions.add(new InsnNode(Opcodes.ISHR));
// 41 bipush 15
m.instructions.add(new IntInsnNode(Opcodes.BIPUSH, 15));
// 43 iand
m.instructions.add(new InsnNode(Opcodes.IAND));
// 44 invokevirtual net.minecraft.world.chunk.NibbleArray.set(int, int, int, int) : void [93]
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, typeNibbleArray.getInternalName(), nibbleArraySet, "(IIII)V"));
// Colored light mod: Store blue value
// 47 aload_0 [this]
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
// 48 getfield net.minecraft.world.chunk.storage.ExtendedBlockStorage.bColorArray : net.minecraft.world.chunk.NibbleArray [102]
m.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, ebsInternalName, bColorArray.name, bColorArray.desc));
// 51 iload_1 [x]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 1));
// 52 iload_2 [y]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 2));
// 53 iload_3 [z]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 3));
// 54 iload 4 [lightValue]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 4));
// 56 bipush 15
m.instructions.add(new IntInsnNode(Opcodes.BIPUSH, 15));
// 58 ishr
m.instructions.add(new InsnNode(Opcodes.ISHR));
// 59 bipush 15
m.instructions.add(new IntInsnNode(Opcodes.BIPUSH, 15));
// 61 iand
m.instructions.add(new InsnNode(Opcodes.IAND));
// 62 invokevirtual net.minecraft.world.chunk.NibbleArray.set(int, int, int, int) : void [93]
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, typeNibbleArray.getInternalName(), nibbleArraySet, "(IIII)V"));
// 65 return
m.instructions.add(new InsnNode(Opcodes.RETURN));
}
private void transformGetExtBlocklightValue(ClassNode clazz, MethodNode m) {
String ebsInternalName = clazz.name;
Type typeNibbleArray = NameMapper.getInstance().getType(NIBBLE_ARRAY);
String nibbleArrayGet = NameMapper.getInstance().getMethodName("net/minecraft/world/chunk/NibbleArray", "get (III)I");
// Already in stock method:
// 0 aload_0 [this]
// 1 getfield net.minecraft.world.chunk.storage.ExtendedBlockStorage.blocklightArray : net.minecraft.world.chunk.NibbleArray [91]
// 4 iload_1 [par1]
// 5 iload_2 [par2]
// 6 iload_3 [par3]
// 7 invokevirtual net.minecraft.world.chunk.NibbleArray.get(int, int, int) : int [110]
// ireturn is there by default - remove for now
AbstractInsnNode returnNode = ASMUtils.findLastReturn(m);
if (returnNode != null)
m.instructions.remove(returnNode);
// 10 aload_0 [this]
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
// 11 getfield net.minecraft.world.chunk.storage.ExtendedBlockStorage.rColorArray : net.minecraft.world.chunk.NibbleArray [98]
m.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, ebsInternalName, rColorArray.name, rColorArray.desc));
// 14 iload_1 [par1]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 1));
// 15 iload_2 [par2]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 2));
// 16 iload_3 [par3]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 3));
// 17 invokevirtual net.minecraft.world.chunk.NibbleArray.get(int, int, int) : int [110]
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, typeNibbleArray.getInternalName(), nibbleArrayGet, "(III)I"));
// 20 iconst_5
m.instructions.add(new InsnNode(Opcodes.ICONST_5));
// 21 ishl
m.instructions.add(new InsnNode(Opcodes.ISHL));
// 22 ior
m.instructions.add(new InsnNode(Opcodes.IOR));
// 23 aload_0 [this]
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
// 24 getfield net.minecraft.world.chunk.storage.ExtendedBlockStorage.gColorArray : net.minecraft.world.chunk.NibbleArray [100]
m.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, ebsInternalName, gColorArray.name, gColorArray.desc));
// 27 iload_1 [par1]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 1));
// 28 iload_2 [par2]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 2));
// 29 iload_3 [par3]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 3));
// 30 invokevirtual net.minecraft.world.chunk.NibbleArray.get(int, int, int) : int [110]
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, typeNibbleArray.getInternalName(), nibbleArrayGet, "(III)I"));
// 33 bipush 10
m.instructions.add(new IntInsnNode(Opcodes.BIPUSH, 10));
// 35 ishl
m.instructions.add(new InsnNode(Opcodes.ISHL));
// 36 ior
m.instructions.add(new InsnNode(Opcodes.IOR));
// 37 aload_0 [this]
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
// 38 getfield net.minecraft.world.chunk.storage.ExtendedBlockStorage.bColorArray : net.minecraft.world.chunk.NibbleArray [102]
m.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, ebsInternalName, bColorArray.name, bColorArray.desc));
// 41 iload_1 [par1]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 1));
// 42 iload_2 [par2]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 2));
// 43 iload_3 [par3]
m.instructions.add(new VarInsnNode(Opcodes.ILOAD, 3));
// 44 invokevirtual net.minecraft.world.chunk.NibbleArray.get(int, int, int) : int [110]
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, typeNibbleArray.getInternalName(), nibbleArrayGet, "(III)I"));
// 47 bipush 15
m.instructions.add(new IntInsnNode(Opcodes.BIPUSH, 15));
// 49 ishl
m.instructions.add(new InsnNode(Opcodes.ISHL));
// 50 ior
m.instructions.add(new InsnNode(Opcodes.IOR));
if (returnNode != null)
// 51 ireturn
m.instructions.add(returnNode);
}
}