diff --git a/src/main/java/com/falsepattern/endlessids/asm/ClassEdit.java b/src/main/java/com/falsepattern/endlessids/asm/ClassEdit.java index ec07f85..414c59b 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/ClassEdit.java +++ b/src/main/java/com/falsepattern/endlessids/asm/ClassEdit.java @@ -2,13 +2,18 @@ package com.falsepattern.endlessids.asm; import com.falsepattern.endlessids.asm.transformer.ChunkProviderSuperPatcher; import com.falsepattern.endlessids.asm.transformer.FmlRegistry; +import com.falsepattern.endlessids.asm.transformer.GameDataAccelerator; import com.falsepattern.endlessids.asm.transformer.SpaceCoreModInfoGenerator; import com.falsepattern.endlessids.config.GeneralConfig; import lombok.val; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.BooleanSupplier; +import java.util.stream.Collectors; public enum ClassEdit { ChunkProviderSuperPatcher(new ChunkProviderSuperPatcher(), () -> GeneralConfig.extendBiome), @@ -17,15 +22,17 @@ public enum ClassEdit { "cpw.mods.fml.common.registry.GameData", "cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry"), SpaceCoreModInfoGenerator(new SpaceCoreModInfoGenerator(), () -> true, - "com.spacechase0.minecraft.spacecore.mcp.ModInfoGenerator"); + "com.spacechase0.minecraft.spacecore.mcp.ModInfoGenerator"), + GameDataAccelerator(new GameDataAccelerator(), () -> true, "cpw.mods.fml.common.registry.GameData"), + ; - private static final Map editMap; + private static final Map> editMap; static { editMap = new HashMap<>(); for (final ClassEdit edit : values()) { for (final String name : edit.classNames) { - ClassEdit.editMap.put(name, edit); + ClassEdit.editMap.computeIfAbsent(name, (ignored) -> new ArrayList<>()).add(edit); } } } @@ -40,9 +47,14 @@ public enum ClassEdit { this.classNames = classNames; } - public static ClassEdit get(final String className) { - val ret = ClassEdit.editMap.get(className); - return ret != null && ret.cond.getAsBoolean() ? ret : null; + public static List get(final String className) { + val edit = ClassEdit.editMap.get(className); + if (edit == null) { + return Collections.emptyList(); + } + return edit.stream() + .filter(r -> r.cond.getAsBoolean()) + .collect(Collectors.toList()); } public String getName() { diff --git a/src/main/java/com/falsepattern/endlessids/asm/GameDataHooks.java b/src/main/java/com/falsepattern/endlessids/asm/GameDataHooks.java new file mode 100644 index 0000000..488d4fe --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/asm/GameDataHooks.java @@ -0,0 +1,31 @@ +package com.falsepattern.endlessids.asm; + +import lombok.var; + +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; + +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.WeakHashMap; + +public class GameDataHooks { + private static final WeakHashMap> itemBlockCache = new WeakHashMap<>(); + + public static void cacheItemBlock(ItemBlock itemBlock) { + itemBlockCache.put(itemBlock.field_150939_a, new WeakReference<>(itemBlock)); + } + + public static ItemBlock fetchCachedItemBlock(Block block) { + var weakItem = itemBlockCache.get(block); + if (weakItem != null) { + return weakItem.get(); + } + return null; + } + + public static Iterable fakeIterable() { + return Collections.emptyList(); + } +} diff --git a/src/main/java/com/falsepattern/endlessids/asm/IETransformer.java b/src/main/java/com/falsepattern/endlessids/asm/IETransformer.java index 29840cf..ac7e2ac 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/IETransformer.java +++ b/src/main/java/com/falsepattern/endlessids/asm/IETransformer.java @@ -4,6 +4,7 @@ import com.falsepattern.endlessids.Tags; import com.falsepattern.endlessids.asm.transformer.ChunkProviderSuperPatcher; import com.falsepattern.endlessids.asm.transformer.DevFixer; import lombok.val; +import lombok.var; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.objectweb.asm.ClassReader; @@ -37,8 +38,7 @@ public class IETransformer implements IClassTransformer { if (blacklist.contains(transformedName)) { return bytes; } - ClassEdit[] edits = new ClassEdit[2]; - edits[0] = ClassEdit.get(transformedName); + var edits = new ArrayList(ClassEdit.get(transformedName)); ClassNode cn = new ClassNode(Opcodes.ASM5); { val reader = new ClassReader(bytes); @@ -52,20 +52,15 @@ public class IETransformer implements IClassTransformer { val reader = new ClassReader(bytes); reader.accept(cn, ClassReader.EXPAND_FRAMES); } - if (edits[0] == null) { - edits[0] = ClassEdit.ChunkProviderSuperPatcher; - } else { - edits[1] = ClassEdit.ChunkProviderSuperPatcher; - } + edits.add(ClassEdit.ChunkProviderSuperPatcher); } - if (edits[0] == null) { + if (edits.size() == 0) { return isObfuscated ? bytes : DevFixer.fixDev(bytes); } if (!isObfuscated) { DevFixer.transform(cn); } - for (int i = 0; i < edits.length; i++) { - val edit = edits[i]; + for (final ClassEdit edit : edits) { if (edit == null) { continue; } diff --git a/src/main/java/com/falsepattern/endlessids/asm/transformer/GameDataAccelerator.java b/src/main/java/com/falsepattern/endlessids/asm/transformer/GameDataAccelerator.java new file mode 100644 index 0000000..821c95b --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/asm/transformer/GameDataAccelerator.java @@ -0,0 +1,92 @@ +package com.falsepattern.endlessids.asm.transformer; + +import com.falsepattern.endlessids.asm.IClassNodeTransformer; +import com.falsepattern.endlessids.asm.IETransformer; +import lombok.val; +import lombok.var; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; + +public class GameDataAccelerator implements IClassNodeTransformer { + private static final int MASK_REGISTER_BLOCK = 0b1; + private static final int MASK_REGISTER_ITEM = 0b10; + @Override + public void transform(ClassNode classNode, boolean obfuscated) { + int tweaksLeft = MASK_REGISTER_BLOCK | MASK_REGISTER_ITEM; + for (val method: classNode.methods) { + if ((tweaksLeft & MASK_REGISTER_BLOCK) != 0 && + method.name.equals("registerBlock") && + method.desc.equals("(Lnet/minecraft/block/Block;Ljava/lang/String;I)I")) { + transformRegisterBlock(method); + tweaksLeft &= ~MASK_REGISTER_BLOCK; + } + if ((tweaksLeft & MASK_REGISTER_ITEM) != 0 && + method.name.equals("registerItem") && + method.desc.equals("(Lnet/minecraft/item/Item;Ljava/lang/String;I)I")) { + transformRegisterItem(method); + tweaksLeft &= ~MASK_REGISTER_ITEM; + } + if (tweaksLeft == 0) { + break; + } + } + if (tweaksLeft != 0) { + IETransformer.logger.error("Could not accelerate block registration!"); + } + } + + private void transformRegisterItem(MethodNode method) { + var iter = method.instructions.iterator(); + while (iter.hasNext()) { + var insn = iter.next(); + if (!(insn instanceof FieldInsnNode)) + continue; + var typeInsn = (FieldInsnNode) insn; + if (!(typeInsn.owner.equals("net/minecraft/item/ItemBlock") && + typeInsn.name.equals("field_150939_a") && + typeInsn.desc.equals("Lnet/minecraft/block/Block;"))) + continue; + iter.previous(); + iter.add(new InsnNode(Opcodes.DUP)); + iter.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/falsepattern/endlessids/asm/GameDataHooks", "cacheItemBlock", "(Lnet/minecraft/item/ItemBlock;)V", false)); + return; + } + transformError(method); + } + + private static void transformError(MethodNode method) { + IETransformer.logger.error("Could not add caching code to GameData." + method.name + method.desc); + } + + private void transformRegisterBlock(MethodNode method) { + var iter = method.instructions.iterator(); + while (iter.hasNext()) { + var insn = iter.next(); + if (!(insn instanceof FieldInsnNode)) + continue; + var fieldInsn = (FieldInsnNode) insn; + if (!(fieldInsn.owner.equals("cpw/mods/fml/common/registry/GameData") && + fieldInsn.name.equals("iItemRegistry") && + fieldInsn.desc.equals("Lcpw/mods/fml/common/registry/FMLControlledNamespacedRegistry;"))) + continue; + iter.previous(); + iter.previous(); + iter.remove(); + iter.next(); + iter.remove(); + iter.next(); + iter.remove(); + iter.add(new VarInsnNode(Opcodes.ALOAD, 1)); + iter.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/falsepattern/endlessids/asm/GameDataHooks", "fetchCachedItemBlock", "(Lnet/minecraft/block/Block;)Lnet/minecraft/item/ItemBlock;", false)); + iter.add(new VarInsnNode(Opcodes.ASTORE, 4)); + iter.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/falsepattern/endlessids/asm/GameDataHooks", "fakeIterable", "()Ljava/lang/Iterable;", false)); + return; + } + transformError(method); + } +}