org.blockartistry.DynSurround.asm.Transformer.java Source code

Java tutorial

Introduction

Here is the source code for org.blockartistry.DynSurround.asm.Transformer.java

Source

/*
 * This file is part of Dynamic Surroundings, licensed under the MIT License (MIT).
 *
 * Copyright (c) OreCruncher
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.blockartistry.DynSurround.asm;

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

import java.util.ListIterator;

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.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;

import net.minecraft.launchwrapper.IClassTransformer;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.blockartistry.DynSurround.ModOptions;

public class Transformer implements IClassTransformer {

    private static final Logger logger = LogManager.getLogger("dsurround Transform");

    private static boolean isOneOf(final String src, final String[] candidates) {
        for (final String s : candidates)
            if (src.equals(s))
                return true;
        return false;
    }

    @Override
    public byte[] transform(String name, String transformedName, byte[] basicClass) {
        if (isOneOf(transformedName, new String[] { "net.minecraft.client.renderer.EntityRenderer", "bqc" })) {
            if (ModOptions.enableWeatherASM) {
                logger.debug("Transforming " + transformedName);
                basicClass = transformEntityRenderer(basicClass);
            }
        } else if (isOneOf(transformedName, new String[] { "net.minecraft.world.WorldServer", "lw" })) {
            if (ModOptions.enableResetOnSleepASM) {
                logger.debug("Transforming " + transformedName);
                basicClass = transformWorldServer(basicClass);
            }
        } else if (isOneOf(transformedName, new String[] { "net.minecraft.client.audio.SoundHandler", "ccp" })) {
            logger.debug("Transforming " + transformedName);
            basicClass = transformSoundManager(basicClass);
        }

        if (ModOptions.enableRandomReplace) {
            basicClass = replaceRandom(transformedName, basicClass);
        }

        return basicClass;
    }

    private byte[] transformEntityRenderer(final byte[] classBytes) {
        final String names[] = { "func_78484_h", "addRainParticles" };
        final String targetName = "addRainParticles";

        final ClassReader cr = new ClassReader(classBytes);
        final ClassNode cn = new ClassNode(ASM5);
        cr.accept(cn, 0);

        for (final MethodNode m : cn.methods) {
            if (isOneOf(m.name, names)) {
                logger.debug("Hooking " + m.name);
                final InsnList list = new InsnList();
                list.add(new VarInsnNode(ALOAD, 0));
                final String sig = "(Lnet/minecraft/client/renderer/EntityRenderer;)V";
                list.add(new MethodInsnNode(INVOKESTATIC,
                        "org/blockartistry/DynSurround/client/weather/RenderWeather", targetName, sig, false));
                list.add(new InsnNode(RETURN));
                m.instructions.insertBefore(m.instructions.getFirst(), list);
            }
        }

        final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cn.accept(cw);
        return cw.toByteArray();
    }

    private byte[] transformWorldServer(final byte[] classBytes) {
        final String names[] = { "func_73051_P", "resetRainAndThunder" };
        final String targetName = "resetRainAndThunder";

        final ClassReader cr = new ClassReader(classBytes);
        final ClassNode cn = new ClassNode(ASM5);
        cr.accept(cn, 0);

        for (final MethodNode m : cn.methods) {
            if (isOneOf(m.name, names)) {
                logger.debug("Hooking " + m.name);
                InsnList list = new InsnList();
                list.add(new VarInsnNode(ALOAD, 0));
                final String sig = "(Lnet/minecraft/world/WorldServer;)V";
                list.add(new MethodInsnNode(INVOKESTATIC, "org/blockartistry/DynSurround/server/PlayerSleepHandler",
                        targetName, sig, false));
                list.add(new InsnNode(RETURN));
                m.instructions.insertBefore(m.instructions.getFirst(), list);
                break;
            }
        }

        final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cn.accept(cw);
        return cw.toByteArray();
    }

    private byte[] transformSoundManager(final byte[] classBytes) {
        final String managerToReplace = "net/minecraft/client/audio/SoundManager";
        final String newManager = "org/blockartistry/DynSurround/client/sound/SoundManagerReplacement";

        final ClassReader cr = new ClassReader(classBytes);
        final ClassNode cn = new ClassNode(ASM5);
        cr.accept(cn, 0);

        for (final MethodNode m : cn.methods) {
            final ListIterator<AbstractInsnNode> itr = m.instructions.iterator();
            boolean foundNew = false;
            while (itr.hasNext()) {
                final AbstractInsnNode node = itr.next();
                if (node.getOpcode() == NEW) {
                    final TypeInsnNode theNew = (TypeInsnNode) node;
                    if (managerToReplace.equals(theNew.desc)) {
                        m.instructions.set(node, new TypeInsnNode(NEW, newManager));
                        foundNew = true;
                    }
                } else if (node.getOpcode() == INVOKESPECIAL) {
                    final MethodInsnNode theInvoke = (MethodInsnNode) node;
                    if (managerToReplace.equals(theInvoke.owner)) {
                        if (foundNew) {
                            m.instructions.set(node, new MethodInsnNode(INVOKESPECIAL, newManager, theInvoke.name,
                                    theInvoke.desc, false));
                            foundNew = false;
                        }
                    }
                }
            }
        }

        final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cn.accept(cw);
        return cw.toByteArray();
    }

    private byte[] replaceRandom(final String name, final byte[] classBytes) {

        final String randomToReplace = "java/util/Random";
        final String newRandom = "org/blockartistry/lib/random/XorShiftRandom";

        boolean madeUpdate = false;

        final ClassReader cr = new ClassReader(classBytes);
        final ClassNode cn = new ClassNode(ASM5);
        cr.accept(cn, 0);

        for (final MethodNode m : cn.methods) {
            final ListIterator<AbstractInsnNode> itr = m.instructions.iterator();
            boolean foundNew = false;
            while (itr.hasNext()) {
                final AbstractInsnNode node = itr.next();
                if (node.getOpcode() == NEW) {
                    final TypeInsnNode theNew = (TypeInsnNode) node;
                    if (randomToReplace.equals(theNew.desc)) {
                        m.instructions.set(node, new TypeInsnNode(NEW, newRandom));
                        madeUpdate = true;
                        foundNew = true;
                    }
                } else if (node.getOpcode() == INVOKESPECIAL) {
                    final MethodInsnNode theInvoke = (MethodInsnNode) node;
                    if (randomToReplace.equals(theInvoke.owner)) {
                        if (foundNew) {
                            m.instructions.set(node, new MethodInsnNode(INVOKESPECIAL, newRandom, theInvoke.name,
                                    theInvoke.desc, false));
                            foundNew = false;
                        }
                    }
                }
            }
        }

        if (madeUpdate) {
            logger.debug("Replaced Random in " + name);
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
            cn.accept(cw);
            return cw.toByteArray();
        }
        return classBytes;
    }
}