blusunrize.immersiveengineering.common.asm.IEClassTransformer.java Source code

Java tutorial

Introduction

Here is the source code for blusunrize.immersiveengineering.common.asm.IEClassTransformer.java

Source

/*
 * BluSunrize
 * Copyright (c) 2017
 *
 * This code is licensed under "Blu's License of Common Sense"
 * Details can be found in the license file in the root folder of this project
 */

package blusunrize.immersiveengineering.common.asm;

import com.google.common.collect.Maps;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;

import java.util.Iterator;
import java.util.Map;
import java.util.function.Consumer;

/**
 * @author BluSunrize - 20.07.2017
 */
public class IEClassTransformer implements IClassTransformer {
    private static Map<String, MethodTransformer[]> transformerMap = Maps.newHashMap();

    static {
        transformerMap.put("net.minecraft.client.model.ModelBiped",
                new MethodTransformer[] { new MethodTransformer("setRotationAngles", "func_78087_a",
                        "(FFFFFFLnet/minecraft/entity/Entity;)V", methodNode -> {
                            Iterator<AbstractInsnNode> iterator = methodNode.instructions.iterator();
                            while (iterator.hasNext()) {
                                AbstractInsnNode anode = iterator.next();
                                if (anode.getOpcode() == Opcodes.RETURN) {
                                    InsnList newInstructions = new InsnList();
                                    newInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
                                    newInstructions.add(new VarInsnNode(Opcodes.ALOAD, 7));
                                    newInstructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
                                            "blusunrize/immersiveengineering/client/ClientUtils",
                                            "handleBipedRotations",
                                            "(Lnet/minecraft/client/model/ModelBiped;Lnet/minecraft/entity/Entity;)V",
                                            false));
                                    methodNode.instructions.insertBefore(anode, newInstructions);
                                }
                            }
                        }) });
        transformerMap.put("net.minecraft.entity.Entity", new MethodTransformer[] {
                new MethodTransformer("doBlockCollisions", "func_145775_I", "()V", (m) -> {
                    //INVOKEVIRTUAL net/minecraft/block/Block.onEntityCollision (Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/entity/Entity;)V
                    Iterator<AbstractInsnNode> iterator = m.instructions.iterator();
                    while (iterator.hasNext()) {
                        AbstractInsnNode anode = iterator.next();
                        if (anode.getOpcode() == Opcodes.INVOKEVIRTUAL) {
                            MethodInsnNode n = (MethodInsnNode) anode;
                            if (n.name.equals("onEntityCollision") || n.name.equals("func_180634_a")) {
                                InsnList newInstructions = new InsnList();
                                newInstructions.add(new VarInsnNode(Opcodes.ALOAD, 4));
                                newInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
                                newInstructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
                                        "blusunrize/immersiveengineering/api/energy/wires/ImmersiveNetHandler",
                                        "handleEntityCollision",
                                        "(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/entity/Entity;)V",
                                        false));
                                m.instructions.insert(n, newInstructions);
                            }
                        }
                    }
                }) });
    }

    @Override
    public byte[] transform(String name, String transformedName, byte[] basicClass) {
        if (basicClass != null && transformerMap.containsKey(transformedName)) {
            MethodTransformer[] transformers = transformerMap.get(transformedName);
            ClassReader reader = new ClassReader(basicClass);
            ClassNode node = new ClassNode();
            reader.accept(node, 0);

            for (MethodNode method : node.methods)
                for (MethodTransformer methodTransformer : transformers)
                    if ((methodTransformer.functionName.equals(method.name)
                            || methodTransformer.srgName.equals(method.name))
                            && methodTransformer.functionDesc.equals(method.desc))
                        methodTransformer.function.accept(method);

            ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
            node.accept(writer);
            return writer.toByteArray();
        }
        return basicClass;
    }

    private static class MethodTransformer {
        final String functionName;
        final String srgName;
        final String functionDesc;
        final Consumer<MethodNode> function;

        private MethodTransformer(String funcName, String srgName, String funcDesc, Consumer<MethodNode> function) {
            this.functionName = funcName;
            this.srgName = srgName;
            this.functionDesc = funcDesc;
            this.function = function;
        }
    }
}