net.dries007.tfctweaks.asm.FluidRegistryCT.java Source code

Java tutorial

Introduction

Here is the source code for net.dries007.tfctweaks.asm.FluidRegistryCT.java

Source

/*
 * Copyright (c) 2015 Dries007
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted (subject to the limitations in the
 * disclaimer below) provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 *  * Neither the name of Dries007 nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
 * GRANTED BY THIS LICENSE.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
 * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package net.dries007.tfctweaks.asm;

import com.bioxx.tfc.api.TFCFluids;
import com.google.common.collect.BiMap;
import cpw.mods.fml.common.FMLLog;
import net.dries007.tfctweaks.util.FluidHacks;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.*;

import java.util.ListIterator;

import static org.objectweb.asm.Opcodes.*;

/**
 * @author Dries007
 */
public class FluidRegistryCT implements IClassTransformer {
    public static final int DONE = 6; // water register + lava register + getFluid + isFluidRegistered(fluid) + isFluidRegistered(string) + getFluidStack
    public static int done = 0;

    @Override
    public byte[] transform(String originalName, String transformedName, byte[] bytes) {
        if (originalName.equals("net.minecraftforge.fluids.FluidRegistry"))
            return magic(bytes);
        return bytes;
    }

    private byte[] magic(byte[] bytes) {
        FMLLog.info("Found the FluidRegistry class...");
        ClassNode classNode = new ClassNode();
        ClassReader classReader = new ClassReader(bytes);
        classReader.accept(classNode, 0);
        for (MethodNode m : classNode.methods) {
            if (m.name.equals("<clinit>") && m.desc.equals("()V")) {
                FMLLog.info("Found the <clinit> method...");

                ListIterator<AbstractInsnNode> i = m.instructions.iterator();
                while (i.hasNext()) {
                    AbstractInsnNode node = i.next();
                    if (!(node instanceof FieldInsnNode) || node.getOpcode() != GETSTATIC)
                        continue;
                    FieldInsnNode fieldInsnNode = ((FieldInsnNode) node);
                    if (!fieldInsnNode.owner.equals("net/minecraftforge/fluids/FluidRegistry"))
                        continue;
                    if (!fieldInsnNode.name.equals("WATER") && !fieldInsnNode.name.equals("LAVA"))
                        continue;
                    if (!fieldInsnNode.desc.equals("Lnet/minecraftforge/fluids/Fluid;"))
                        continue;
                    node = i.next();
                    if (!(node instanceof MethodInsnNode) || node.getOpcode() != INVOKESTATIC)
                        continue;
                    MethodInsnNode methodInsnNode = ((MethodInsnNode) node);
                    if (!methodInsnNode.owner.equals("net/minecraftforge/fluids/FluidRegistry"))
                        continue;
                    if (!methodInsnNode.name.equals("registerFluid"))
                        continue;
                    if (!methodInsnNode.desc.equals("(Lnet/minecraftforge/fluids/Fluid;)Z"))
                        continue;
                    node = i.next();
                    if (!(node instanceof InsnNode) || node.getOpcode() != POP)
                        continue;
                    InsnNode insnNode = ((InsnNode) node);
                    m.instructions.remove(fieldInsnNode);
                    m.instructions.remove(methodInsnNode);
                    m.instructions.remove(insnNode);
                    FMLLog.info("[FluidRegistryCT] Removed the " + fieldInsnNode.name + " registration.");
                    done++;
                }
            } else if (m.name.equals("getFluid")
                    && m.desc.equals("(Ljava/lang/String;)Lnet/minecraftforge/fluids/Fluid;")) {
                FMLLog.info("Found the getFluid method...");
                InsnList insnList = new InsnList();
                {
                    LabelNode labelFirstIf = new LabelNode();
                    insnList.add(new FieldInsnNode(GETSTATIC, "net/dries007/tfctweaks/util/FluidHacks",
                            "makeAllWaterFTCWater", "Z"));
                    insnList.add(new JumpInsnNode(IFEQ, labelFirstIf));
                    insnList.add(new VarInsnNode(ALOAD, 0));
                    insnList.add(new LdcInsnNode("water"));
                    insnList.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "equals",
                            "(Ljava/lang/Object;)Z", false));
                    insnList.add(new JumpInsnNode(IFEQ, labelFirstIf));
                    insnList.add(new FieldInsnNode(GETSTATIC, "com/bioxx/tfc/api/TFCFluids", "FRESHWATER",
                            "Lnet/minecraftforge/fluids/Fluid;"));
                    insnList.add(new InsnNode(ARETURN));
                    insnList.add(labelFirstIf);
                }
                {
                    LabelNode lableSecondIf = new LabelNode();
                    insnList.add(new FieldInsnNode(GETSTATIC, "net/dries007/tfctweaks/util/FluidHacks",
                            "makeAllLavaFTCLava", "Z"));
                    insnList.add(new JumpInsnNode(IFEQ, lableSecondIf));
                    insnList.add(new VarInsnNode(ALOAD, 0));
                    insnList.add(new LdcInsnNode("lava"));
                    insnList.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "equals",
                            "(Ljava/lang/Object;)Z", false));
                    insnList.add(new JumpInsnNode(IFEQ, lableSecondIf));
                    insnList.add(new FieldInsnNode(GETSTATIC, "com/bioxx/tfc/api/TFCFluids", "LAVA",
                            "Lnet/minecraftforge/fluids/Fluid;"));
                    insnList.add(new InsnNode(ARETURN));
                    insnList.add(lableSecondIf);
                }
                m.instructions.insertBefore(m.instructions.getFirst(), insnList);

                FMLLog.info("[FluidRegistryCT] Edited getFluid(String) : Fluid.");
                done++;
            } else if (m.name.equals("isFluidRegistered")) {
                if (m.desc.equals("(Lnet/minecraftforge/fluids/Fluid;)Z")) {
                    InsnList insnList = new InsnList();
                    LabelNode falseLabel = new LabelNode();
                    LabelNode trueLabel = new LabelNode();
                    insnList.add(new VarInsnNode(ALOAD, 0));
                    insnList.add(new JumpInsnNode(IFNULL, falseLabel));
                    insnList.add(new VarInsnNode(ALOAD, 0));
                    insnList.add(new MethodInsnNode(INVOKEVIRTUAL, "net/minecraftforge/fluids/Fluid", "getName",
                            "()Ljava/lang/String;", false));
                    insnList.add(new MethodInsnNode(INVOKESTATIC, "net/minecraftforge/fluids/FluidRegistry",
                            "isFluidRegistered", "(Ljava/lang/String;)Z", false));
                    insnList.add(new JumpInsnNode(IFEQ, falseLabel));
                    insnList.add(new InsnNode(ICONST_1));
                    insnList.add(new JumpInsnNode(GOTO, trueLabel));
                    insnList.add(falseLabel);
                    insnList.add(new InsnNode(ICONST_0));
                    insnList.add(trueLabel);
                    insnList.add(new InsnNode(IRETURN));
                    // replace entire method
                    m.instructions.clear();
                    m.instructions.add(insnList);

                    FMLLog.info("[FluidRegistryCT] Edited isFluidRegistered(Fluid) : bool.");
                    done++;
                } else if (m.desc.equals("(Ljava/lang/String;)Z")) {
                    InsnList insnList = new InsnList();
                    LabelNode trueLabel = new LabelNode();
                    insnList.add(new LdcInsnNode("water"));
                    insnList.add(new VarInsnNode(ALOAD, 0));
                    insnList.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "equals",
                            "(Ljava/lang/Object;)Z", false));
                    insnList.add(new JumpInsnNode(IFNE, trueLabel));
                    insnList.add(new LdcInsnNode("lava"));
                    insnList.add(new VarInsnNode(ALOAD, 0));
                    insnList.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "equals",
                            "(Ljava/lang/Object;)Z", false));
                    insnList.add(new JumpInsnNode(IFNE, trueLabel));
                    insnList.add(new FieldInsnNode(GETSTATIC, "net/minecraftforge/fluids/FluidRegistry", "fluids",
                            "Lcom/google/common/collect/BiMap;"));
                    insnList.add(new VarInsnNode(ALOAD, 0));
                    insnList.add(new MethodInsnNode(INVOKEINTERFACE, "com/google/common/collect/BiMap",
                            "containsKey", "(Ljava/lang/Object;)Z", true));
                    LabelNode falseLabel = new LabelNode();
                    insnList.add(new JumpInsnNode(IFEQ, falseLabel));
                    insnList.add(trueLabel);
                    insnList.add(new InsnNode(ICONST_1));
                    LabelNode returnLabel = new LabelNode();
                    insnList.add(new JumpInsnNode(GOTO, returnLabel));
                    insnList.add(falseLabel);
                    insnList.add(new InsnNode(ICONST_0));
                    insnList.add(returnLabel);
                    insnList.add(new InsnNode(IRETURN));

                    // replace entire method
                    m.instructions.clear();
                    m.instructions.add(insnList);

                    FMLLog.info("[FluidRegistryCT] Edited isFluidRegistered(String) : bool.");
                    done++;
                }
            } else if (m.name.equals("getFluidStack")) {
                LabelNode notNullNode = null;
                { // Grab first jump node label
                    ListIterator<AbstractInsnNode> i = m.instructions.iterator();
                    while (i.hasNext()) {
                        AbstractInsnNode node = i.next();
                        if (node.getOpcode() == IFNE) {
                            notNullNode = ((JumpInsnNode) node).label;
                            break;
                        }
                    }
                }

                InsnList insnList = new InsnList();
                insnList.add(new LdcInsnNode("water"));
                insnList.add(new VarInsnNode(ALOAD, 0));
                insnList.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "equals",
                        "(Ljava/lang/Object;)Z", false));
                insnList.add(new JumpInsnNode(IFNE, notNullNode));
                insnList.add(new LdcInsnNode("lava"));
                insnList.add(new VarInsnNode(ALOAD, 0));
                insnList.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "equals",
                        "(Ljava/lang/Object;)Z", false));
                insnList.add(new JumpInsnNode(IFNE, notNullNode));

                // add to the beginning of the list, leave the rest of the method in place.
                m.instructions.insert(insnList);

                FMLLog.info("[FluidRegistryCT] Edited getFluidStack(String, int) : FluidStack.");
                done++;
            }
        }

        if (done != DONE) {
            FMLLog.severe(
                    "\n######################################################################################\n"
                            + "######################################################################################\n"
                            + "######################################################################################\n"
                            + "OUR ASM FLUID HACK FAILED! PLEASE MAKE AN ISSUE REPORT ON GITHUB WITH A COMPLETE MODLIST! https://github.com/dries007/TFC-Tweaks\n"
                            + "Done %d out of %d ASM tweaks on class FluidRegistry\n"
                            + "########################################################################################\n"
                            + "########################################################################################\n"
                            + "########################################################################################\n\n",
                    done, DONE);
        }

        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        classNode.accept(writer);
        return writer.toByteArray();
    }

    /*
     * Dummy methods
     */
    static BiMap<String, Fluid> fluids = null;

    /*
    ORIGINAL:
    GETSTATIC net/minecraftforge/fluids/FluidRegistry.fluids : Lcom/google/common/collect/BiMap;
    ALOAD 0
    INVOKEINTERFACE com/google/common/collect/BiMap.get (Ljava/lang/Object;)Ljava/lang/Object;
    CHECKCAST net/minecraftforge/fluids/Fluid
    ARETURN
     */
    @SuppressWarnings("ALL")
    private static Fluid getFluid(String fluidName) {
        if (FluidHacks.makeAllWaterFTCWater && fluidName.equals("water"))
            return TFCFluids.FRESHWATER;
        else if (FluidHacks.makeAllLavaFTCLava && fluidName.equals("lava"))
            return TFCFluids.LAVA;

        return fluids.get(fluidName);
    }

    /*
    ORIGINAL:
    ALOAD 0
    IFNULL L0
    GETSTATIC net/minecraftforge/fluids/FluidRegistry.fluids : Lcom/google/common/collect/BiMap;
    ALOAD 0
    INVOKEVIRTUAL net/minecraftforge/fluids/Fluid.getName ()Ljava/lang/String;
    INVOKEINTERFACE com/google/common/collect/BiMap.containsKey (Ljava/lang/Object;)Z
    IFEQ L0
    ICONST_1
    GOTO L1
    L0
    ICONST_0
    L1
    IRETURN
     */
    @SuppressWarnings("ALL")
    public static boolean isFluidRegistered(Fluid fluid) {
        return fluid != null && FluidRegistry.isFluidRegistered(fluid.getName());
    }

    /*
    ORIGINAL:
    GETSTATIC net/minecraftforge/fluids/FluidRegistry.fluids : Lcom/google/common/collect/BiMap;
    ALOAD 0
    INVOKEINTERFACE com/google/common/collect/BiMap.containsKey (Ljava/lang/Object;)Z
    IRETURN
     */
    @SuppressWarnings("ALL")
    public static boolean isFluidRegistered(String fluidName) {
        return "water".equals(fluidName) || "lava".equals(fluidName) || fluids.containsKey(fluidName);
    }

    /*
    ORIGINAL:
       L0
    LINENUMBER 270 L0
    GETSTATIC net/dries007/tfctweaks/asm/FluidRegistryCT.fluids : Lcom/google/common/collect/BiMap;
    ALOAD 0
    INVOKEINTERFACE com/google/common/collect/BiMap.containsKey (Ljava/lang/Object;)Z
    IFNE L1
       L2
    LINENUMBER 272 L2
    ACONST_NULL
    ARETURN
       L1
    LINENUMBER 274 L1
       FRAME SAME
    NEW net/minecraftforge/fluids/FluidStack
    DUP
    ALOAD 0
    INVOKESTATIC net/dries007/tfctweaks/asm/FluidRegistryCT.getFluid (Ljava/lang/String;)Lnet/minecraftforge/fluids/Fluid;
    ILOAD 1
    INVOKESPECIAL net/minecraftforge/fluids/FluidStack.<init> (Lnet/minecraftforge/fluids/Fluid;I)V
    ARETURN
       L3
     */
    @SuppressWarnings("ALL")
    public static FluidStack getFluidStack(String fluidName, int amount) {
        if (!("water".equals(fluidName) || "lava".equals(fluidName) || fluids.containsKey(fluidName))) {
            return null;
        }
        return new FluidStack(getFluid(fluidName), amount);
    }
}