Package cofh.asm

Source Code of cofh.asm.ASMCore$AnnotationInfo

package cofh.asm;

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

import cofh.asm.relauncher.Implementable;
import cofh.asm.relauncher.Strippable;
import com.google.common.base.Throwables;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModAPIManager;
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
import cpw.mods.fml.common.asm.transformers.deobf.FMLRemappingAdapter;
import cpw.mods.fml.common.discovery.ASMDataTable;
import cpw.mods.fml.common.discovery.ASMDataTable.ASMData;

import gnu.trove.map.hash.TObjectByteHashMap;
import gnu.trove.set.hash.THashSet;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

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.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;

class ASMCore {

  static Logger log = LogManager.getLogger("CoFH ASM");

  static TObjectByteHashMap<String> hashes = new TObjectByteHashMap<String>(20, 1, (byte) 0);
  static THashSet<String> parsables, implementables, strippables;
  static final String implementableDesc, strippableDesc;
  static String side;

  static void init() {

  }

  static {

    implementableDesc = Type.getDescriptor(Implementable.class);
    strippableDesc = Type.getDescriptor(Strippable.class);

    parsables = new THashSet<String>(10);
    implementables = new THashSet<String>(10);
    strippables = new THashSet<String>(10);

    hashes.put("net.minecraft.world.WorldServer", (byte) 1);
    hashes.put("net.minecraft.world.World", (byte) 2);
    hashes.put("skyboy.core.world.WorldProxy", (byte) 3);
    hashes.put("skyboy.core.world.WorldServerProxy", (byte) 4);
    hashes.put("net.minecraft.block.BlockPane", (byte) 5);
    hashes.put("net.minecraft.inventory.Container", (byte) 6);
    hashes.put("net.minecraft.client.multiplayer.PlayerControllerMP", (byte) 7);
    hashes.put("net.minecraft.util.LongHashMap", (byte) 8);
    if (Boolean.parseBoolean(System.getProperty("cofh.lightedit", "true"))) {
      hashes.put("net.minecraft.world.chunk.Chunk", (byte) 9);
    }
    hashes.put("net.minecraft.client.Minecraft", (byte) 10);
    hashes.put("net.minecraft.client.renderer.RenderBlocks", (byte) 11);
    hashes.put("net.minecraft.tileentity.TileEntity", (byte) 12);
  }

  static final ArrayList<String> workingPath = new ArrayList<String>();
  private static final String[] emptyList = {};

  static class AnnotationInfo {

    public String side;
    public String[] values = emptyList;
  }

  static byte[] parse(String name, String transformedName, byte[] bytes) {

    workingPath.add(transformedName);

    if (implementables.contains(name)) {
      log.info("Adding runtime interfaces to " + transformedName);
      ClassReader cr = new ClassReader(bytes);
      ClassNode cn = new ClassNode();
      cr.accept(cn, 0);
      if (implement(cn)) {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cn.accept(cw);
        bytes = cw.toByteArray();
      } else {
        log.debug("Nothing implemented on " + transformedName);
      }
    }

    if (strippables.contains(name)) {
      log.info("Stripping methods and fields from " + transformedName);
      ClassReader cr = new ClassReader(bytes);
      ClassNode cn = new ClassNode();
      cr.accept(cn, 0);
      if (strip(cn)) {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        cn.accept(cw);
        bytes = cw.toByteArray();
      } else {
        log.debug("Nothing stripped from " + transformedName);
      }
    }

    workingPath.remove(workingPath.size() - 1);
    return bytes;
  }

  static synchronized void HACK(String name, byte[] bytes) {

    synchronized (workingPath) {
      workingPath.add(name);
      ClassReader cr = new ClassReader(bytes);
      ClassNode cn = new ClassNode();
      cr.accept(cn, 0);
      if (cn.innerClasses != null) {
        for (InnerClassNode node : cn.innerClasses) {
          log.debug("\tInner class: " + node.name);
          if (!workingPath.contains(node.name)) {
            try {
              Class.forName(node.name, false, ASMCore.class.getClassLoader());
            } catch (Throwable _) {
            }
          }
        }
      }
      workingPath.remove(workingPath.size() - 1);
    }
  }

  static byte[] transform(int index, String name, String transformedName, byte[] bytes) {

    ClassReader cr = new ClassReader(bytes);

    switch (index) {
    case 1:
      return writeWorldServer(name, transformedName, bytes, cr);
    case 2:
      return writeWorld(name, transformedName, bytes, cr);
    case 3:
      return writeWorldProxy(name, bytes, cr);
    case 4:
      return writeWorldServerProxy(name, bytes, cr);
    case 5:
      return alterBlockPane(name, transformedName, bytes, cr);
    case 6:
      return alterContainer(name, transformedName, bytes, cr);
    case 7:
      return alterController(name, transformedName, bytes, cr);
    case 8:
      return alterLongHashMap(name, transformedName, bytes, cr);
    case 9:
      return alterChunk(name, transformedName, bytes, cr);
    case 10:
      return alterMinecraft(name, transformedName, bytes, cr);
    case 11:
      return alterRenderBlocks(name, transformedName, bytes, cr);
    case 12:
      return alterTileEntity(name, transformedName, bytes, cr);

    default:
      return bytes;
    }
  }

  // { Improve Vanilla
  private static byte[] alterRenderBlocks(String name, String transformedName, byte[] bytes, ClassReader cr) {
    //.renderBlockStainedGlassPane(Block, int, int, int)
    String[] names;
    if (LoadingPlugin.runtimeDeobfEnabled) {
      names = new String[] { "func_147733_k", "func_150098_a", "func_147439_a" };
    } else {
      names = new String[] { "renderBlockStainedGlassPane", "canPaneConnectToBlock", "getBlock" };
    }

    name = name.replace('.', '/');
    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, ClassReader.EXPAND_FRAMES);

    final String sig = "(Lnet/minecraft/block/Block;III)Z";
    final String Rsig = "(Lnet/minecraft/world/IBlockAccess;IIILnet/minecraftforge/common/util/ForgeDirection;)Z";
    final String Ssig = "(Lnet/minecraft/block/Block;)Z";
    final String Csig = "(III)Lnet/minecraft/block/Block;";
    final String cc = "net/minecraft/block/BlockPane";
    final String fd = "net/minecraftforge/common/util/ForgeDirection";
    FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;

    l: {
      MethodNode m = null;
      for (MethodNode n : cn.methods) {
        if (names[0].equals(remapper.mapMethodName(name, n.name, n.desc)) && sig.equals(remapper.mapMethodDesc(n.desc))) {
          m = n;
          break;
        }
      }

      if (m == null)
        break l;

      m.localVariables = null;

      final String[] dirs = {"NORTH", "NORTH", "SOUTH", "SOUTH", "WEST", "WEST", "EAST", "EAST"};
      int di = 0;

      for (int i = 0, e = m.instructions.size(); i < e; ++i) {
        AbstractInsnNode n = m.instructions.get(i);
        if (n.getType() == AbstractInsnNode.METHOD_INSN) {
          MethodInsnNode mn = (MethodInsnNode)n;
          if (n.getOpcode() == INVOKEINTERFACE && n.getNext().getOpcode() == INVOKEVIRTUAL) {
            if (names[2].equals(remapper.mapMethodName(mn.owner, mn.name, mn.desc))) {
              if (Csig.equals(remapper.mapMethodDesc(mn.desc)) &&
                  Ssig.equals(remapper.mapMethodDesc(((MethodInsnNode)mn.getNext()).desc))) {
                m.instructions.insertBefore(n, new FieldInsnNode(GETSTATIC, fd, dirs[di++], 'L'+fd+';'));
                m.instructions.insertBefore(n, new MethodInsnNode(INVOKEVIRTUAL, cc, "canPaneConnectTo", Rsig, false));
                m.instructions.remove(n.getNext());
                m.instructions.remove(n);
              }
            }
          }
        }
      }

      if (di == 0)
         break l;

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

    return bytes;
  }

  private static byte[] alterBlockPane(String name, String transformedName, byte[] bytes, ClassReader cr) {

    String names = "canPaneConnectTo"; // forge added

    name = name.replace('.', '/');
    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, ClassReader.EXPAND_FRAMES);

    final String sig = "(Lnet/minecraft/world/IBlockAccess;IIILnet/minecraftforge/common/util/ForgeDirection;)Z";
    FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;

    l: {
      MethodNode m = null;
      for (MethodNode n : cn.methods) {
        if (names.equals(remapper.mapMethodName(name, n.name, n.desc)) && sig.equals(remapper.mapMethodDesc(n.desc))) {
          m = n;
          break;
        }
      }

      if (m == null) {
        break l;
      }

      m.instructions.clear();
      m.instructions.add(new VarInsnNode(ALOAD, 1));
      m.instructions.add(new VarInsnNode(ILOAD, 2));
      m.instructions.add(new VarInsnNode(ILOAD, 3));
      m.instructions.add(new VarInsnNode(ILOAD, 4));
      m.instructions.add(new VarInsnNode(ALOAD, 5));
      m.instructions.add(new MethodInsnNode(INVOKESTATIC, "cofh/asm/HooksCore", "paneConnectsTo", sig, false));
      m.instructions.add(new InsnNode(IRETURN));

      m.localVariables = null;

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

  private static byte[] alterMinecraft(String name, String transformedName, byte[] bytes, ClassReader cr) {

    String[] names;
    if (LoadingPlugin.runtimeDeobfEnabled) {
      names = new String[] { "func_71407_l", "func_110550_d" };
    } else {
      names = new String[] { "runTick", "tick" };
    }

    name = name.replace('.', '/');
    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, ClassReader.EXPAND_FRAMES);

    FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;
    String mOwner = remapper.unmap("net/minecraft/client/renderer/texture/TextureManager");

    l: {
      boolean updated = false;
      mc: for (MethodNode m : cn.methods) {
        String mName = remapper.mapMethodName(name, m.name, m.desc);
        if (names[0].equals(mName) && "()V".equals(m.desc)) {
          updated = true;
          for (int i = 0, e = m.instructions.size(); i < e; ++i) {
            AbstractInsnNode n = m.instructions.get(i);
            if (n.getOpcode() == INVOKEVIRTUAL) {
              MethodInsnNode mn = (MethodInsnNode) n;
              if (mOwner.equals(mn.owner) && names[1].equals(remapper.mapMethodName(mn.owner, mn.name, mn.desc)) && "()V".equals(mn.desc)) {
                m.instructions.set(mn, new MethodInsnNode(INVOKESTATIC, "cofh/asm/HooksCore", "tickTextures",
                    "(Lnet/minecraft/client/renderer/texture/ITickable;)V", false));
                break mc;
              }
            }
          }
        }
      }

      if (!updated) {
        break l;
      }

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

  private static byte[] alterChunk(String name, String transformedName, byte[] bytes, ClassReader cr) {

    String[] names;
    if (LoadingPlugin.runtimeDeobfEnabled) {
      names = new String[] { "func_150803_c", "field_76650_s" };
    } else {
      names = new String[] { "recheckGaps", "isGapLightingUpdated" };
    }

    name = name.replace('.', '/');
    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, ClassReader.EXPAND_FRAMES);

    FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;

    l: {
      boolean updated = false;
      for (MethodNode m : cn.methods) {
        String mName = remapper.mapMethodName(name, m.name, m.desc);
        if (names[0].equals(mName) && "(Z)V".equals(m.desc)) {
          updated = true;
          for (int i = 0, e = m.instructions.size(); i < e; ++i) {
            AbstractInsnNode n = m.instructions.get(i);
            if (n.getOpcode() == RETURN) {
              m.instructions.insertBefore(n, new VarInsnNode(ALOAD, 0));
              m.instructions.insertBefore(n, new InsnNode(ICONST_0));
              m.instructions.insertBefore(n, new FieldInsnNode(PUTFIELD, name, names[1], "Z"));
              break;
            }
          }
        }
      }

      if (!updated) {
        break l;
      }

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

  private static byte[] alterLongHashMap(String name, String transformedName, byte[] bytes, ClassReader cr) {

    String[] names;
    if (LoadingPlugin.runtimeDeobfEnabled) {
      names = new String[] { "func_76155_g", "func_76160_c", "func_76161_b" };
    } else {
      names = new String[] { "getHashedKey", "getEntry", "containsItem" };
    }

    name = name.replace('.', '/');
    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, ClassReader.EXPAND_FRAMES);

    FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;

    l: {
      boolean updated = false;
      MethodNode getEntry = null, containsItem = null;
      for (MethodNode m : cn.methods) {
        String mName = remapper.mapMethodName(name, m.name, m.desc);
        if (names[0].equals(mName) && "(J)I".equals(m.desc)) {
          updated = true;
          for (int i = 0, e = m.instructions.size(); i < e; ++i) {
            AbstractInsnNode n = m.instructions.get(i);
            if (n.getOpcode() == LXOR) {
              m.instructions.insertBefore(n, new LdcInsnNode(new Long(13L)));
              m.instructions.insertBefore(n, new InsnNode(LMUL));
              break;
            }
          }
          if (containsItem != null) {
            break;
          }
        } else if (names[2].equals(mName) && "(J)Z".equals(m.desc)) {
          containsItem = m;
          if (updated) {
            break;
          }
        }
      }

      mc: if (containsItem != null) {
        // { cloning methods to get a different set of instructions to avoid erasing getEntry
        ClassNode clone = new ClassNode(ASM4);
        cr.accept(clone, ClassReader.EXPAND_FRAMES);
        String sig = "(J)Lnet/minecraft/util/LongHashMap$Entry;";
        for (MethodNode m : clone.methods) {
          String mName = remapper.mapMethodName(name, m.name, m.desc);
          if (names[1].equals(mName) && sig.equals(remapper.mapMethodDesc(m.desc))) {
            getEntry = m;
            break;
          }
        }
        // }
        if (getEntry == null) {
          break mc;
        }
        updated = true;
        containsItem.instructions.clear();
        containsItem.instructions.add(getEntry.instructions);
        /**
         * this looks counter intuitive (replacing getEntry != null check with the full method) but due to how the JVM handles inlining, this needs to
         * be done manually
         */
        for (AbstractInsnNode n = containsItem.instructions.get(0); n != null; n = n.getNext()) {
          if (n.getOpcode() == ARETURN) {
            AbstractInsnNode n2 = n.getPrevious();
            if (n2.getOpcode() == ACONST_NULL) {
              containsItem.instructions.set(n2, new InsnNode(ICONST_0));
            } else {
              containsItem.instructions.set(n2, new InsnNode(ICONST_1));
            }
            containsItem.instructions.set(n, n = new InsnNode(IRETURN));
          }
        }
      }

      if (!updated) {
        break l;
      }

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

  private static byte[] alterController(String name, String transformedName, byte[] bytes, ClassReader cr) {

    String[] names;
    if (LoadingPlugin.runtimeDeobfEnabled) {
      names = new String[] { "func_85182_a", "field_85183_f" };
    } else {
      names = new String[] { "sameToolAndBlock", "currentItemHittingBlock" };
    }

    name = name.replace('.', '/');
    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, ClassReader.EXPAND_FRAMES);

    final String sig = "(III)Z";
    FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;
    final String itemstack = remapper.unmap("net/minecraft/item/ItemStack");

    l: {
      MethodNode m = null;
      for (MethodNode n : cn.methods) {
        if (names[0].equals(remapper.mapMethodName(name, n.name, n.desc)) && sig.equals(n.desc)) {
          m = n;
          break;
        }
      }

      if (m == null) {
        break l;
      }

      for (int i = 0, e = m.instructions.size(); i < e; i++) {
        AbstractInsnNode n = m.instructions.get(i);
        if (n.getOpcode() == INVOKEVIRTUAL) {
          MethodInsnNode mn = (MethodInsnNode) n;
          if (itemstack.equals(mn.owner)) {
            LabelNode jmp = null, jmp2 = null;
            s: for (int j = i; j < e; ++j) {
              n = m.instructions.get(j);
              if (n.getOpcode() == ICONST_1) {
                for (int k = j; k > i; --k) {
                  n = m.instructions.get(k);
                  if (n.getType() == AbstractInsnNode.LABEL) {
                    jmp = (LabelNode) n;
                    break;
                  }
                }
                for (int k = j; k < e; ++k) {
                  n = m.instructions.get(k);
                  if (n.getType() == AbstractInsnNode.LABEL) {
                    jmp2 = (LabelNode) n;
                    break s;
                  }
                }
              }
            }
            if (jmp == null || jmp2 == null) {
              break l;
            }

            // presently on stack: player.getHeldItem()
            m.instructions.insertBefore(mn, new VarInsnNode(ALOAD, 0));
            m.instructions.insertBefore(mn, new FieldInsnNode(GETFIELD, name, names[1], 'L' + itemstack + ';'));
            final String clazz = "cofh/asm/HooksCore";
            final String method = "areItemsEqualHook";
            final String sign = "(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemStack;)Z";
            m.instructions.insertBefore(mn, new MethodInsnNode(INVOKESTATIC, clazz, method, sign, false));
            m.instructions.insertBefore(mn, new JumpInsnNode(IFEQ, jmp2));
            m.instructions.insertBefore(mn, new JumpInsnNode(GOTO, jmp));
            break;
          }
        }
      }

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

  private static byte[] alterContainer(String name, String transformedName, byte[] bytes, ClassReader cr) {

    String[] names;
    if (LoadingPlugin.runtimeDeobfEnabled) {
      names = new String[] { "func_75135_a", "field_75151_b" };
    } else {
      names = new String[] { "mergeItemStack", "inventorySlots" };
    }

    name = name.replace('.', '/');
    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, ClassReader.EXPAND_FRAMES);

    final String sig = "(Lnet/minecraft/item/ItemStack;IIZ)Z";
    FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;

    l: {
      MethodNode m = null;
      for (MethodNode n : cn.methods) {
        if (names[0].equals(remapper.mapMethodName(name, n.name, n.desc)) && sig.equals(remapper.mapMethodDesc(n.desc))) {
          m = n;
          break;
        }
      }

      if (m == null) {
        break l;
      }

      m.instructions.clear();
      m.instructions.add(new VarInsnNode(ALOAD, 0));
      m.instructions.add(new FieldInsnNode(GETFIELD, name, names[1], "Ljava/util/List;"));
      m.instructions.add(new VarInsnNode(ALOAD, 1));
      m.instructions.add(new VarInsnNode(ILOAD, 2));
      m.instructions.add(new VarInsnNode(ILOAD, 3));
      m.instructions.add(new VarInsnNode(ILOAD, 4));
      m.instructions.add(new InsnNode(ICONST_0));
      m.instructions.add(new MethodInsnNode(INVOKESTATIC, "cofh/lib/util/helpers/InventoryHelper", "mergeItemStack",
          "(Ljava/util/List;Lnet/minecraft/item/ItemStack;IIZZ)Z", false));
      m.instructions.add(new InsnNode(IRETURN));

      // this fixes a crash in dev and with cauldron
      m.localVariables = null;

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

  private static byte[] alterTileEntity(String name, String transformedName, byte[] bytes, ClassReader cr) {

    name = name.replace('.', '/');
    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, 0);
    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
    cn.accept(cw);
    cw.newMethod(name, "cofh_validate", "()V", true);
    MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "cofh_validate", "()V", null, null);
    mv.visitCode();
    mv.visitInsn(RETURN);
    mv.visitMaxs(0, 1);
    mv.visitEnd();
    cw.visitEnd();
    return cw.toByteArray();
  }

  private static byte[] writeWorld(String name, String transformedName, byte[] bytes, ClassReader cr) {

    String[] names;
    if (LoadingPlugin.runtimeDeobfEnabled) {
      names = new String[] { "field_73019_z", "field_72986_A", "field_73011_w", "field_72984_F", "func_147448_a", "func_147455_a", "func_72939_s" };
    } else {
      names = new String[] { "saveHandler", "worldInfo", "provider", "theProfiler", "func_147448_a", "setTileEntity", "updateEntities" };
    }
    name = name.replace('.', '/');
    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, ClassReader.EXPAND_FRAMES);

    final String sig = "(Lnet/minecraft/world/storage/ISaveHandler;Ljava/lang/String;Lnet/minecraft/world/WorldProvider;Lnet/minecraft/world/WorldSettings;Lnet/minecraft/profiler/Profiler;)V";
    FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;

    MethodNode addTileEntity = null, addTileEntities = null, setTileEntity = null, updateEntities = null;
    boolean found = false;
    for (MethodNode m : cn.methods) {
      if ("<init>".equals(m.name)) {
        if (sig.equals(remapper.mapMethodDesc(m.desc)))
            found = true;
        LabelNode a = new LabelNode(new Label());
        AbstractInsnNode n = m.instructions.getFirst();
        while (n.getOpcode() != INVOKESPECIAL ||
            !((MethodInsnNode)n).name.equals("<init>")) n = n.getNext();
        m.instructions.insert(n, n = a);
        m.instructions.insert(n, n = new LineNumberNode(-15000, a));
        m.instructions.insert(n, n = new VarInsnNode(ALOAD, 0));
        m.instructions.insert(n, n = new TypeInsnNode(NEW, "cofh/lib/util/LinkedHashList"));
        m.instructions.insert(n, n = new InsnNode(DUP));
        m.instructions.insert(n, n = new MethodInsnNode(INVOKESPECIAL, "cofh/lib/util/LinkedHashList", "<init>", "()V", false));
        m.instructions.insert(n, n = new FieldInsnNode(PUTFIELD, "net/minecraft/world/World", "cofh_recentTiles", "Lcofh/lib/util/LinkedHashList;"));
      } else if ("addTileEntity".equals(m.name) && "(Lnet/minecraft/tileentity/TileEntity;)V".equals(remapper.mapMethodDesc(m.desc))) {
        addTileEntity = m;
      } else if (names[4].equals(remapper.mapMethodName(name, m.name, m.desc)) && "(Ljava/util/Collection;)V".equals(m.desc)) {
        addTileEntities = m;
      } else if (names[5].equals(remapper.mapMethodName(name, m.name, m.desc)) && "(IIILnet/minecraft/tileentity/TileEntity;)V".equals(remapper.mapMethodDesc(m.desc))) {
        setTileEntity = m;
      } else if (names[6].equals(remapper.mapMethodName(name, m.name, m.desc)) && "()V".equals(remapper.mapMethodDesc(m.desc))) {
        updateEntities = m;
      }
    }
    cn.fields.add(new FieldNode(ACC_PRIVATE, "cofh_recentTiles", "Lcofh/lib/util/LinkedHashList;", null, null));
    if (addTileEntity != null) {

      LabelNode a = new LabelNode(new Label());
      AbstractInsnNode n;
      addTileEntity.instructions.insert(n = a);
      addTileEntity.instructions.insert(n, n = new LineNumberNode(-15001, a));
      addTileEntity.instructions.insert(n, n = new VarInsnNode(ALOAD, 0));
      addTileEntity.instructions.insert(n, n = new FieldInsnNode(GETFIELD, "net/minecraft/world/World", "cofh_recentTiles", "Lcofh/lib/util/LinkedHashList;"));
      addTileEntity.instructions.insert(n, n = new VarInsnNode(ALOAD, 1));
      addTileEntity.instructions.insert(n, n = new MethodInsnNode(INVOKEVIRTUAL, "cofh/lib/util/LinkedHashList", "push", "(Ljava/lang/Object;)Z", false));
      addTileEntity.instructions.insert(n, n = new InsnNode(POP));
    }
    if (setTileEntity != null) {

      LabelNode a = new LabelNode(new Label());
      AbstractInsnNode n = setTileEntity.instructions.getLast();
      while (n.getOpcode() != RETURN) n = n.getPrevious();
      n = n.getPrevious();
      setTileEntity.instructions.insert(n = a);
      setTileEntity.instructions.insert(n, n = new LineNumberNode(-15002, a));
      setTileEntity.instructions.insert(n, n = new VarInsnNode(ALOAD, 0));
      setTileEntity.instructions.insert(n, n = new FieldInsnNode(GETFIELD, "net/minecraft/world/World", "cofh_recentTiles", "Lcofh/lib/util/LinkedHashList;"));
      setTileEntity.instructions.insert(n, n = new VarInsnNode(ALOAD, 4));
      setTileEntity.instructions.insert(n, n = new MethodInsnNode(INVOKEVIRTUAL, "cofh/lib/util/LinkedHashList", "push", "(Ljava/lang/Object;)Z", false));
      setTileEntity.instructions.insert(n, n = new InsnNode(POP));
    }
    if (addTileEntities != null) {
      LabelNode a = new LabelNode(new Label());
      AbstractInsnNode n = addTileEntities.instructions.getFirst();
      for (;;) {
        while (n.getOpcode() != CHECKCAST) n = n.getNext();
        if (remapper.mapType(((TypeInsnNode)n).desc).equals("net/minecraft/tileentity/TileEntity"))
          break;
      }
      addTileEntities.instructions.insert(n, n = a);
      addTileEntities.instructions.insert(n, n = new LineNumberNode(-15003, a));
      addTileEntities.instructions.insert(n, n = new InsnNode(DUP));
      addTileEntities.instructions.insert(n, n = new VarInsnNode(ALOAD, 0));
      addTileEntities.instructions.insert(n, n = new FieldInsnNode(GETFIELD, "net/minecraft/world/World", "cofh_recentTiles", "Lcofh/lib/util/LinkedHashList;"));
      addTileEntities.instructions.insert(n, n = new InsnNode(SWAP));
      addTileEntities.instructions.insert(n, n = new MethodInsnNode(INVOKEVIRTUAL, "cofh/lib/util/LinkedHashList", "push", "(Ljava/lang/Object;)Z", false));
      addTileEntities.instructions.insert(n, n = new InsnNode(POP));
    }
    if (updateEntities != null) {
      AbstractInsnNode n = updateEntities.instructions.getFirst();
      while (n.getOpcode() != INVOKEVIRTUAL ||
          !"onChunkUnload".equals(((MethodInsnNode)n).name) ||
          !"()V".equals(((MethodInsnNode)n).desc)) n = n.getNext();
      while (n.getOpcode() != PUTFIELD) n = n.getNext();
      n = n.getPrevious().getPrevious();
      LabelNode lStart = new LabelNode(new Label());
      LabelNode lCond = new LabelNode(new Label());
      LabelNode lGuard = new LabelNode(new Label());
      LabelNode a = new LabelNode(new Label());
      updateEntities.instructions.insert(n, n = a);
      updateEntities.instructions.insert(n, n = new LineNumberNode(-15004, a));
      updateEntities.instructions.insert(n, n = new JumpInsnNode(GOTO, lCond));
      updateEntities.instructions.insert(n, n = lStart);
      updateEntities.instructions.insert(n, n = new FrameNode(F_SAME, 0, null, 0, null));
      updateEntities.instructions.insert(n, n = new VarInsnNode(ALOAD, 0));
      updateEntities.instructions.insert(n, n = new FieldInsnNode(GETFIELD, "net/minecraft/world/World", "cofh_recentTiles", "Lcofh/lib/util/LinkedHashList;"));
      updateEntities.instructions.insert(n, n = new MethodInsnNode(INVOKEVIRTUAL, "cofh/lib/util/LinkedHashList", "shift", "()Ljava/lang/Object;", false));
      updateEntities.instructions.insert(n, n = new TypeInsnNode(CHECKCAST, "net/minecraft/tileentity/TileEntity"));
      updateEntities.instructions.insert(n, n = new InsnNode(DUP));
      updateEntities.instructions.insert(n, n = new JumpInsnNode(IFNULL, lGuard));
      updateEntities.instructions.insert(n, n = new MethodInsnNode(INVOKEVIRTUAL, "net/minecraft/tileentity/TileEntity", "cofh_validate", "()V", false));
      updateEntities.instructions.insert(n, n = new InsnNode(ACONST_NULL));
      updateEntities.instructions.insert(n, n = lGuard);
      updateEntities.instructions.insert(n, n = new InsnNode(POP));
      updateEntities.instructions.insert(n, n = lCond);
      updateEntities.instructions.insert(n, n = new FrameNode(F_SAME, 0, null, 0, null));
      updateEntities.instructions.insert(n, n = new VarInsnNode(ALOAD, 0));
      updateEntities.instructions.insert(n, n = new FieldInsnNode(GETFIELD, "net/minecraft/world/World", "cofh_recentTiles", "Lcofh/lib/util/LinkedHashList;"));
      updateEntities.instructions.insert(n, n = new MethodInsnNode(INVOKEVIRTUAL, "cofh/lib/util/LinkedHashList", "size", "()I", false));
      updateEntities.instructions.insert(n, n = new JumpInsnNode(IFNE, lStart));
    }

    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    cn.accept(cw);
    if (!found) {
      /*
       * new World constructor World(ISaveHandler saveHandler, String worldName, WorldProvider provider, WorldSettings worldSettings, Profiler
       * theProfiler)
       */
      cw.newMethod(name, "<init>", sig, true);
      MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", sig, null, null);
      mv.visitCode();
      mv.visitVarInsn(ALOAD, 0);
      mv.visitInsn(DUP);
      mv.visitInsn(DUP);
      mv.visitInsn(DUP);
      mv.visitInsn(DUP);
      mv.visitInsn(DUP);
      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
      mv.visitVarInsn(ALOAD, 1);
      mv.visitFieldInsn(PUTFIELD, name, names[0], "Lnet/minecraft/world/storage/ISaveHandler;");
      mv.visitTypeInsn(NEW, "net/minecraft/world/storage/WorldInfo");
      mv.visitInsn(DUP);
      mv.visitVarInsn(ALOAD, 4);
      mv.visitVarInsn(ALOAD, 2);
      mv.visitMethodInsn(INVOKESPECIAL, "net/minecraft/world/storage/WorldInfo", "<init>", "(Lnet/minecraft/world/WorldSettings;Ljava/lang/String;)V", false);
      mv.visitFieldInsn(PUTFIELD, name, names[1], "Lnet/minecraft/world/storage/WorldInfo;");
      mv.visitVarInsn(ALOAD, 3);
      mv.visitFieldInsn(PUTFIELD, name, names[2], "Lnet/minecraft/world/WorldProvider;");
      mv.visitVarInsn(ALOAD, 5);
      mv.visitFieldInsn(PUTFIELD, name, names[3], "Lnet/minecraft/profiler/Profiler;");
      mv.visitInsn(RETURN);
      mv.visitMaxs(11, 10);
      mv.visitEnd();
      cw.visitEnd();
    }
    bytes = cw.toByteArray();
    return bytes;
  }

  private static byte[] writeWorldServer(String name, String transformedName, byte[] bytes, ClassReader cr) {

    String[] names;
    if (LoadingPlugin.runtimeDeobfEnabled) {
      names = new String[] { "field_73061_a", "field_73062_L", "field_73063_M", "field_85177_Q" };
    } else {
      names = new String[] { "mcServer", "theEntityTracker", "thePlayerManager", "worldTeleporter" };
    }
    name = name.replace('.', '/');
    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, ClassReader.EXPAND_FRAMES);
    final String sig = "(Lnet/minecraft/server/MinecraftServer;Lnet/minecraft/world/storage/ISaveHandler;Ljava/lang/String;Lnet/minecraft/world/WorldProvider;Lnet/minecraft/world/WorldSettings;Lnet/minecraft/profiler/Profiler;)V";
    FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;

    l: {
      for (MethodNode m : cn.methods) {
        if ("<init>".equals(m.name) && sig.equals(remapper.mapMethodDesc(m.desc))) {
          break l;
        }
      }

      ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
      cn.accept(cw);
      /*
       * new WorldServer constructor WorldServer(MinecraftServer minecraftServer, ISaveHandler saveHandler, String worldName, WorldProvider provider,
       * WorldSettings worldSettings, Profiler theProfiler)
       */
      cw.newMethod(name, "<init>", sig, true);
      MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", sig, null, null);
      mv.visitCode();
      mv.visitVarInsn(ALOAD, 0);
      mv.visitInsn(DUP);
      mv.visitInsn(DUP);
      mv.visitInsn(DUP);
      mv.visitInsn(DUP);
      mv.visitVarInsn(ALOAD, 2);
      mv.visitVarInsn(ALOAD, 3);
      mv.visitVarInsn(ALOAD, 4);
      mv.visitVarInsn(ALOAD, 5);
      mv.visitVarInsn(ALOAD, 6);
      // [World] super(saveHandler, worldName, provider, worldSettings, theProfiler);
      mv.visitMethodInsn(
          INVOKESPECIAL,
          "net/minecraft/world/World",
          "<init>",
          "(Lnet/minecraft/world/storage/ISaveHandler;Ljava/lang/String;Lnet/minecraft/world/WorldProvider;Lnet/minecraft/world/WorldSettings;Lnet/minecraft/profiler/Profiler;)V", false);
      mv.visitVarInsn(ALOAD, 1);
      mv.visitFieldInsn(PUTFIELD, name, names[0], "Lnet/minecraft/server/MinecraftServer;");
      mv.visitInsn(ACONST_NULL);
      mv.visitFieldInsn(PUTFIELD, name, names[1], "Lnet/minecraft/entity/EntityTracker;");
      mv.visitInsn(ACONST_NULL);
      mv.visitFieldInsn(PUTFIELD, name, names[2], "Lnet/minecraft/server/management/PlayerManager;");
      mv.visitInsn(ACONST_NULL);
      mv.visitFieldInsn(PUTFIELD, name, names[3], "Lnet/minecraft/world/Teleporter;");
      mv.visitInsn(RETURN);
      mv.visitMaxs(11, 10);
      mv.visitEnd();
      cw.visitEnd();
      bytes = cw.toByteArray();
    }
    return bytes;
  }

  private static byte[] writeWorldProxy(String name, byte[] bytes, ClassReader cr) {

    ClassNode world = new ClassNode(ASM4);
    {
      FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;
      try {
        ClassReader reader = new ClassReader(LoadingPlugin.loader.getClassBytes(
            remapper.unmap("net/minecraft/world/World").replace('/', '.')));
        reader.accept(new FMLRemappingAdapter(world), ClassReader.SKIP_CODE);
      } catch (Throwable e) {
        Throwables.propagate(e);
      }
    }

    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, ClassReader.SKIP_FRAMES);

    for (MethodNode m : world.methods) {
      if (m.name.indexOf('<') != 0 && (m.access & ACC_STATIC) == 0) {
        {
          Iterator<MethodNode> i = cn.methods.iterator();
          while (i.hasNext()) {
            MethodNode m2 = i.next();
            if (m2.name.equals(m.name) && m2.desc.equals(m.desc)) {
              i.remove();
            }
          }
        }
        MethodVisitor mv = cn.visitMethod(getAccess(m), m.name, m.desc, m.signature, m.exceptions.toArray(new String[0]));
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, "skyboy/core/world/WorldProxy", "proxiedWorld", "Lnet/minecraft/world/World;");
        Type[] types = Type.getArgumentTypes(m.desc);
        for (int i = 0, w = 1, e = types.length; i < e; i++) {
          mv.visitVarInsn(types[i].getOpcode(ILOAD), w);
          w += types[i].getSize();
        }
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/minecraft/world/World", m.name, m.desc, false);
        mv.visitInsn(Type.getReturnType(m.desc).getOpcode(IRETURN));
        mv.visitMaxs(1, 1);
        mv.visitEnd();
      }
    }

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

  private static byte[] writeWorldServerProxy(String name, byte[] bytes, ClassReader cr) {

    ClassNode worldServer = new ClassNode(ASM4);
    {
      FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;
      try {
        ClassReader reader = new ClassReader(LoadingPlugin.loader.getClassBytes(
            remapper.unmap("net/minecraft/world/WorldServer").replace('/', '.')));
        reader.accept(new FMLRemappingAdapter(worldServer), ClassReader.SKIP_CODE);
      } catch (Throwable e) {
        Throwables.propagate(e);
      }
    }
    ClassNode world = new ClassNode(ASM4);
    {
      FMLDeobfuscatingRemapper remapper = FMLDeobfuscatingRemapper.INSTANCE;
      try {
        ClassReader reader = new ClassReader(LoadingPlugin.loader.getClassBytes(
            remapper.unmap("net/minecraft/world/World").replace('/', '.')));
        reader.accept(new FMLRemappingAdapter(world), ClassReader.SKIP_CODE);
      } catch (Throwable e) {
        Throwables.propagate(e);
      }
    }

    ClassNode cn = new ClassNode(ASM4);
    cr.accept(cn, ClassReader.SKIP_FRAMES);

    cn.superName = "net/minecraft/world/WorldServer";
    for (MethodNode m : cn.methods) {
      if ("<init>".equals(m.name)) {
        InsnList l = m.instructions;
        for (int i = 0, e = l.size(); i < e; i++) {
          AbstractInsnNode n = l.get(i);
          if (n instanceof MethodInsnNode) {
            MethodInsnNode mn = (MethodInsnNode) n;
            if (mn.getOpcode() == INVOKESPECIAL) {
              mn.owner = cn.superName;
              break;
            }
          }
        }
      }
    }

    for (MethodNode m : world.methods) {
      if (m.name.indexOf('<') != 0 && (m.access & ACC_STATIC) == 0) {
        {
          Iterator<MethodNode> i = cn.methods.iterator();
          while (i.hasNext()) {
            MethodNode m2 = i.next();
            if (m2.name.equals(m.name) && m2.desc.equals(m.desc)) {
              i.remove();
            }
          }
        }
        MethodVisitor mv = cn.visitMethod(getAccess(m), m.name, m.desc, m.signature, m.exceptions.toArray(new String[0]));
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, "skyboy/core/world/WorldServerProxy", "proxiedWorld", "Lnet/minecraft/world/WorldServer;");
        Type[] types = Type.getArgumentTypes(m.desc);
        for (int i = 0, w = 1, e = types.length; i < e; i++) {
          mv.visitVarInsn(types[i].getOpcode(ILOAD), w);
          w += types[i].getSize();
        }
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/minecraft/world/World", m.name, m.desc, false);
        mv.visitInsn(Type.getReturnType(m.desc).getOpcode(IRETURN));
        mv.visitMaxs(1, 1);
        mv.visitEnd();
      }
    }

    for (MethodNode m : worldServer.methods) {
      if (m.name.indexOf('<') != 0 && (m.access & ACC_STATIC) == 0) {
        {
          Iterator<MethodNode> i = cn.methods.iterator();
          while (i.hasNext()) {
            MethodNode m2 = i.next();
            if (m2.name.equals(m.name) && m2.desc.equals(m.desc)) {
              i.remove();
            }
          }
        }
        MethodVisitor mv = cn.visitMethod(getAccess(m), m.name, m.desc, m.signature, m.exceptions.toArray(new String[0]));
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, "skyboy/core/world/WorldServerProxy", "proxiedWorld", "Lnet/minecraft/world/WorldServer;");
        Type[] types = Type.getArgumentTypes(m.desc);
        for (int i = 0, w = 1, e = types.length; i < e; i++) {
          mv.visitVarInsn(types[i].getOpcode(ILOAD), w);
          w += types[i].getSize();
        }
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/minecraft/world/WorldServer", m.name, m.desc, false);
        mv.visitInsn(Type.getReturnType(m.desc).getOpcode(IRETURN));
        mv.visitMaxs(1, 1);
        mv.visitEnd();
      }
    }

    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    cn.accept(cw);
    bytes = cw.toByteArray();
    return bytes;
  }

  private static int getAccess(MethodNode m) {

    int r = m.access;
    r &= ~(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_FINAL | ACC_BRIDGE | ACC_ABSTRACT);
    r |= ACC_PUBLIC | ACC_SYNTHETIC;
    return r;
  }

  // }

  // { Implement & Strip
  static boolean implement(ClassNode cn) {

    if (cn.visibleAnnotations == null) {
      return false;
    }
    boolean interfaces = false;
    for (AnnotationNode n : cn.visibleAnnotations) {
      AnnotationInfo node = parseAnnotation(n, implementableDesc);
      if (node != null && side == node.side) {
        String[] value = node.values;
        for (int j = 0, l = value.length; j < l; ++j) {
          String clazz = value[j].trim();
          String cz = clazz.replace('.', '/');
          if (!cn.interfaces.contains(cz)) {
            try {
              if (!workingPath.contains(clazz)) {
                Class.forName(clazz, false, ASMCore.class.getClassLoader());
              }
              cn.interfaces.add(cz);
              interfaces = true;
            } catch (Throwable _) {
            }
          }
        }
      }
    }
    return interfaces;
  }

  static boolean strip(ClassNode cn) {

    boolean altered = false;
    if (cn.visibleAnnotations != null) {
      for (AnnotationNode n : cn.visibleAnnotations) {
        AnnotationInfo node = parseAnnotation(n, strippableDesc);
        if (node != null) {
          String[] value = node.values;
          boolean wrongSide = side == node.side;
          for (int j = 0, l = value.length; j < l; ++j) {
            String clazz = value[j];
            String cz = clazz.replace('.', '/');
            if (cn.interfaces.contains(cz)) {
              boolean remove = true;
              try {
                if (!wrongSide && !workingPath.contains(clazz)) {
                  Class.forName(clazz, false, ASMCore.class.getClassLoader());
                  remove = false;
                }
              } catch (Throwable _) {
              }
              if (remove) {
                cn.interfaces.remove(cz);
                altered = true;
              }
            }
          }
        }
      }
    }
    if (cn.methods != null) {
      Iterator<MethodNode> iter = cn.methods.iterator();
      while (iter.hasNext()) {
        MethodNode mn = iter.next();
        if (mn.visibleAnnotations != null) {
          for (AnnotationNode node : mn.visibleAnnotations) {
            if (checkRemove(parseAnnotation(node, strippableDesc), iter)) {
              altered = true;
              break;
            }
          }
        }
      }
    }
    if (cn.fields != null) {
      Iterator<FieldNode> iter = cn.fields.iterator();
      while (iter.hasNext()) {
        FieldNode fn = iter.next();
        if (fn.visibleAnnotations != null) {
          for (AnnotationNode node : fn.visibleAnnotations) {
            if (checkRemove(parseAnnotation(node, strippableDesc), iter)) {
              altered = true;
              break;
            }
          }
        }
      }
    }
    return altered;
  }

  static boolean checkRemove(AnnotationInfo node, Iterator<? extends Object> iter) {

    if (node != null) {
      boolean needsRemoved = node.side == side;
      if (!needsRemoved) {
        String[] value = node.values;
        for (int j = 0, l = value.length; j < l; ++j) {
          String clazz = value[j];
          if (clazz.startsWith("mod:")) {
            needsRemoved = !Loader.isModLoaded(clazz.substring(4));
          } else if (clazz.startsWith("api:")) {
            needsRemoved = !ModAPIManager.INSTANCE.hasAPI(clazz.substring(4));
          } else {
            try {
              if (!workingPath.contains(clazz)) {
                Class.forName(clazz, false, ASMCore.class.getClassLoader());
              }
            } catch (Throwable _) {
              needsRemoved = true;
            }
          }
          if (needsRemoved) {
            break;
          }
        }
      }
      if (needsRemoved) {
        iter.remove();
        return true;
      }
    }
    return false;
  }

  // }

  static AnnotationInfo parseAnnotation(AnnotationNode node, String desc) {

    AnnotationInfo info = null;
    if (node.desc.equals(desc)) {
      info = new AnnotationInfo();
      if (node.values != null) {
        List<Object> values = node.values;
        for (int i = 0, e = values.size(); i < e;) {
          Object k = values.get(i++);
          Object v = values.get(i++);
          if ("value".equals(k)) {
            if (!(v instanceof List && ((List<?>) v).size() > 0 && ((List<?>) v).get(0) instanceof String)) {
              continue;
            }
            info.values = ((List<?>) v).toArray(emptyList);
          } else if ("side".equals(k) && v instanceof String[]) {
            String t = ((String[]) v)[1];
            if (t != null) {
              info.side = t.toUpperCase().intern();
            }
          }
        }
      }
    }
    return info;
  }

  static void scrapeData(ASMDataTable table) {

    log.debug("Scraping data");

    side = FMLCommonHandler.instance().getSide().toString().toUpperCase().intern();

    for (ASMData data : table.getAll(Implementable.class.getName())) {
      String name = data.getClassName();
      parsables.add(name);
      parsables.add(name + "$class");
      implementables.add(name);
      implementables.add(name + "$class");
    }

    for (ASMData data : table.getAll(Strippable.class.getName())) {
      String name = data.getClassName();
      parsables.add(name);
      parsables.add(name + "$class");
      strippables.add(name);
      strippables.add(name + "$class");
    }

    log.debug("Found " + implementables.size() + " @Implementable and " + strippables.size() + " @Strippable");
  }
}
TOP

Related Classes of cofh.asm.ASMCore$AnnotationInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.