diff --git a/dependencies.gradle b/dependencies.gradle index 8684d23..cfb673a 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -21,7 +21,8 @@ dependencies { } annotationProcessor("org.projectlombok:lombok:1.18.32") - implementation("com.falsepattern:falsepatternlib-mc1.7.10:1.2.4:dev") + compileOnly("com.falsepattern:falsepatternlib-mc1.7.10:1.2.4:api") + runtimeOnly("com.falsepattern:falsepatternlib-mc1.7.10:1.2.4:dev") implementation("it.unimi.dsi:fastutil:8.5.13") compileOnly("com.falsepattern:chunkapi-mc1.7.10:0.5.1:api") @@ -118,7 +119,7 @@ dependencies { compileOnly(deobfCurse("industrialrevolutionbyredstonerebooted-241812:2538035")) //LOTR Mod 36.15 - compileOnly(deobfCurse("lotrmod-423748:4091561")) + implementation(deobfCurse("lotrmod-423748:4091561")) //More Fun Quicksands Mod 1.0.8.8-fixed compileOnly(deobfCurse("more-fun-quicksand-mod-321960:2717318")) diff --git a/gradle.properties b/gradle.properties index 4dc3ce6..baf7cf1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -172,7 +172,7 @@ accessTransformersFile = # Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin! # This parameter is for legacy compatibility only # Example value: coreModClass = asm.FMLPlugin + modGroup = com.myname.mymodid -> com.myname.mymodid.asm.FMLPlugin -coreModClass = IEPlugin +coreModClass = asm.EndlessIDsCore #endregion core diff --git a/src/main/java/com/falsepattern/endlessids/Unsafer.java b/src/main/java/com/falsepattern/endlessids/Unsafer.java deleted file mode 100644 index e0258ba..0000000 --- a/src/main/java/com/falsepattern/endlessids/Unsafer.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.falsepattern.endlessids; - -import lombok.val; -import sun.misc.Unsafe; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.nio.ByteBuffer; -import java.nio.ShortBuffer; - -public class Unsafer { - public static final Unsafe UNSAFE; - - static { - Field unsafeField = null; - Unsafe unsafe = null; - for (val field : Unsafe.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && - field.getType().equals(Unsafe.class)) { - unsafeField = field; - break; - } - } - if (unsafeField != null) { - unsafeField.setAccessible(true); - try { - unsafe = (Unsafe) unsafeField.get(null); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - if (unsafe == null) { - UNSAFE = null; - new IllegalStateException("Could not retrieve Unsafe! Falling back to bytebuffers!").printStackTrace(); - } else { - UNSAFE = unsafe; - } - } - - public static void arraycopy(short[] source, int sourceOffset, byte[] target, int targetOffset, int shorts) { - if (UNSAFE != null) { - val sourceOffsetBytes = sourceOffset * 2; - val arrayOffsetSource = UNSAFE.arrayBaseOffset(source.getClass()); - val arrayOffsetTarget = UNSAFE.arrayBaseOffset(target.getClass()); - UNSAFE.copyMemory(source, arrayOffsetSource + sourceOffsetBytes, target, arrayOffsetTarget + targetOffset, - (long) shorts << 1); - } else { - ByteBuffer.wrap(target, targetOffset, target.length - targetOffset) - .asShortBuffer() - .put(source, sourceOffset, shorts); - } - } - - public static void arraycopy(byte[] source, int sourceOffset, short[] target, int targetOffset, int shorts) { - if (UNSAFE != null) { - val targetOffsetBytes = targetOffset * 2; - val arrayOffsetSource = UNSAFE.arrayBaseOffset(source.getClass()); - val arrayOffsetTarget = UNSAFE.arrayBaseOffset(target.getClass()); - UNSAFE.copyMemory(source, arrayOffsetSource + sourceOffset, target, arrayOffsetTarget + targetOffsetBytes, - (long) shorts << 1); - } else { - ShortBuffer.wrap(target, targetOffset, target.length - targetOffset) - .put(ByteBuffer.wrap(source, sourceOffset, shorts * 2).asShortBuffer()); - } - } -} diff --git a/src/main/java/com/falsepattern/endlessids/asm/ClassEdit.java b/src/main/java/com/falsepattern/endlessids/asm/ClassEdit.java deleted file mode 100644 index 5188ae2..0000000 --- a/src/main/java/com/falsepattern/endlessids/asm/ClassEdit.java +++ /dev/null @@ -1,71 +0,0 @@ -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.LOTRFieldExposer; -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), - FmlRegistry(new FmlRegistry(), - () -> GeneralConfig.extendBlockItem, - "cpw.mods.fml.common.registry.GameData", - "cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry"), - LOTRFieldExposer(new LOTRFieldExposer(), - () -> GeneralConfig.extendBiome, - "lotr.common.network.LOTRPacketBiomeVariantsWatch"), - SpaceCoreModInfoGenerator(new SpaceCoreModInfoGenerator(), () -> true, - "com.spacechase0.minecraft.spacecore.mcp.ModInfoGenerator"), - GameDataAccelerator(new GameDataAccelerator(), () -> GeneralConfig.enableRegistryPerformanceTweak, "cpw.mods.fml.common.registry.GameData"), - ; - - private static final Map> editMap; - - static { - editMap = new HashMap<>(); - for (final ClassEdit edit : values()) { - for (final String name : edit.classNames) { - ClassEdit.editMap.computeIfAbsent(name, (ignored) -> new ArrayList<>()).add(edit); - } - } - } - - private final IClassNodeTransformer transformer; - private final BooleanSupplier cond; - private final String[] classNames; - - ClassEdit(final IClassNodeTransformer transformer, BooleanSupplier cond, final String... classNames) { - this.transformer = transformer; - this.cond = cond; - this.classNames = classNames; - } - - 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() { - return this.name(); - } - - public IClassNodeTransformer getTransformer() { - return this.transformer; - } -} diff --git a/src/main/java/com/falsepattern/endlessids/IEPlugin.java b/src/main/java/com/falsepattern/endlessids/asm/EndlessIDsCore.java similarity index 80% rename from src/main/java/com/falsepattern/endlessids/IEPlugin.java rename to src/main/java/com/falsepattern/endlessids/asm/EndlessIDsCore.java index 78de901..bf4b26a 100644 --- a/src/main/java/com/falsepattern/endlessids/IEPlugin.java +++ b/src/main/java/com/falsepattern/endlessids/asm/EndlessIDsCore.java @@ -1,6 +1,6 @@ -package com.falsepattern.endlessids; +package com.falsepattern.endlessids.asm; -import com.falsepattern.endlessids.asm.IETransformer; +import com.falsepattern.endlessids.Tags; import com.falsepattern.endlessids.config.GeneralConfig; import lombok.SneakyThrows; import lombok.val; @@ -19,29 +19,30 @@ import static cpw.mods.fml.relauncher.IFMLLoadingPlugin.Name; import static cpw.mods.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions; @MCVersion("1.7.10") -@Name(Tags.MODID) +@Name(Tags.MODID + "_core") @TransformerExclusions({"com.falsepattern.endlessids.asm", "com.falsepattern.endlessids.config.GeneralConfig"}) @DependsOn("falsepatternlib") -public class IEPlugin implements IFMLLoadingPlugin { - - public IEPlugin() { +public class EndlessIDsCore implements IFMLLoadingPlugin { + public static boolean deobfuscated; + public EndlessIDsCore() { super(); } @SneakyThrows public String[] getASMTransformerClass() { + deobfuscated = (boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment"); val cl = Launch.classLoader; val field = LaunchClassLoader.class.getDeclaredField("transformerExceptions"); field.setAccessible(true); val exceptions = (Set) field.get(cl); if (exceptions.contains("code.elix_x.coremods")) { - IETransformer.logger.info("AntiIDConflict detected!"); - IETransformer.logger.info("Removing ASM protections so that we can fix its code."); + EndlessIDsTransformer.logger.info("AntiIDConflict detected!"); + EndlessIDsTransformer.logger.info("Removing ASM protections so that we can fix its code."); exceptions.remove("code.elix_x.coremods"); exceptions.add("code.elix_x.coremods.antiidconflict.core"); exceptions.add("code.elix_x.coremods.antiidconflict.ByteCodeTester"); } - return new String[]{IETransformer.class.getName()}; + return new String[]{EndlessIDsTransformer.class.getName()}; } public String getModContainerClass() { @@ -68,7 +69,6 @@ public class IEPlugin implements IFMLLoadingPlugin { } public void injectData(final Map data) { - IETransformer.isObfuscated = (Boolean) data.get("runtimeDeobfuscationEnabled"); } public String getAccessTransformerClass() { diff --git a/src/main/java/com/falsepattern/endlessids/asm/EndlessIDsTransformer.java b/src/main/java/com/falsepattern/endlessids/asm/EndlessIDsTransformer.java new file mode 100644 index 0000000..12ab880 --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/asm/EndlessIDsTransformer.java @@ -0,0 +1,45 @@ +package com.falsepattern.endlessids.asm; + +import com.falsepattern.endlessids.Tags; +import com.falsepattern.endlessids.asm.transformer.DevFixer; +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.asm.transformer.chunk.ChunkProviderSuperPatcher; +import com.falsepattern.endlessids.asm.transformer.chunk.LOTRFieldExposer; +import com.falsepattern.endlessids.config.GeneralConfig; +import com.falsepattern.lib.turboasm.MergeableTurboTransformer; +import com.falsepattern.lib.turboasm.TurboClassTransformer; +import lombok.val; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +public class EndlessIDsTransformer extends MergeableTurboTransformer { + public static final Logger logger = LogManager.getLogger(Tags.MODNAME + " ASM"); + + private static List transformers() { + val result = new ArrayList(); + if (GeneralConfig.extendBiome) { + result.add(new ChunkProviderSuperPatcher()); + result.add(new LOTRFieldExposer()); + } + if (GeneralConfig.extendBlockItem) { + result.add(new FmlRegistry()); + } + if (GeneralConfig.enableRegistryPerformanceTweak) { + new GameDataAccelerator(); + } + if (EndlessIDsCore.deobfuscated) { + result.add(new SpaceCoreModInfoGenerator()); + result.add(new DevFixer()); + } + return result; + } + + public EndlessIDsTransformer() { + super(transformers()); + } +} diff --git a/src/main/java/com/falsepattern/endlessids/asm/GameDataHooks.java b/src/main/java/com/falsepattern/endlessids/asm/GameDataHooks.java index 3457650..6f97fb4 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/GameDataHooks.java +++ b/src/main/java/com/falsepattern/endlessids/asm/GameDataHooks.java @@ -12,6 +12,8 @@ import java.lang.ref.WeakReference; import java.util.Collections; import java.util.WeakHashMap; +//Used by ASM +@SuppressWarnings("unused") public class GameDataHooks { private static WeakIdentityHashMap> forGameData(GameData gameData) { if (gameData.endlessIDsItemBlockCache == null) { @@ -20,14 +22,10 @@ public class GameDataHooks { return gameData.endlessIDsItemBlockCache; } - //Called by ASM - @SuppressWarnings("unused") public static void cacheItemBlock(ItemBlock itemBlock, GameData gameData) { forGameData(gameData).put(itemBlock.field_150939_a, new WeakReference<>(itemBlock)); } - //Called by ASM - @SuppressWarnings("unused") public static ItemBlock fetchCachedItemBlock(Block block, GameData gameData) { var weakItem = forGameData(gameData).get(block); if (weakItem != null) { @@ -36,8 +34,6 @@ public class GameDataHooks { return null; } - //Called by ASM - @SuppressWarnings("unused") 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 deleted file mode 100644 index 23eb932..0000000 --- a/src/main/java/com/falsepattern/endlessids/asm/IETransformer.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.falsepattern.endlessids.asm; - -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; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; - -import net.minecraft.launchwrapper.IClassTransformer; - -import java.util.ArrayList; -import java.util.List; - -public class IETransformer implements IClassTransformer { - public static final Logger logger; - private static final List blacklist = new ArrayList<>(); - public static boolean isObfuscated; - - static { - logger = LogManager.getLogger(Tags.MODNAME + " ASM"); - blacklist.add("net.minecraft.world.chunk.storage.AnvilChunkLoader"); - } - - public IETransformer() { - super(); - } - - public byte[] transform(final String name, final String transformedName, final byte[] bytes) { - if (bytes == null) { - return null; - } - if (blacklist.contains(transformedName)) { - return bytes; - } - var edits = new ArrayList(ClassEdit.get(transformedName)); - ClassNode cn = new ClassNode(Opcodes.ASM5); - { - val reader = new ClassReader(bytes); - reader.accept(cn, 0); - } - boolean expanded = false; - if (ChunkProviderSuperPatcher.shouldPatch(cn)) { - expanded = true; - { - cn = new ClassNode(Opcodes.ASM5); - val reader = new ClassReader(bytes); - reader.accept(cn, ClassReader.EXPAND_FRAMES); - } - edits.add(ClassEdit.ChunkProviderSuperPatcher); - } - if (edits.size() == 0) { - return isObfuscated ? bytes : DevFixer.fixDev(bytes); - } - if (!isObfuscated) { - DevFixer.transform(cn); - } - for (final ClassEdit edit : edits) { - if (edit == null) { - continue; - } - IETransformer.logger.debug("Patching {} with {}...", transformedName, edit.getName()); - try { - edit.getTransformer().transform(cn, IETransformer.isObfuscated); - } catch (AsmTransformException t) { - IETransformer.logger.error("Error transforming {} with {}: {}", transformedName, edit.getName(), - t.getMessage()); - throw t; - } catch (Throwable t2) { - IETransformer.logger.error("Error transforming {} with {}: {}", transformedName, edit.getName(), - t2.getMessage()); - throw new RuntimeException(t2); - } - } - final ClassWriter writer = new ClassWriter(expanded ? ClassWriter.COMPUTE_FRAMES : 0); - try { - cn.accept(writer); - } catch (Exception e) { - for (int i = 0; i < 100; i++) { - IETransformer.logger.fatal("FAILED TO TRANSFORM " + transformedName + ", WORLD MAY GET CORRUPTED!", e); - } - return bytes; - } - IETransformer.logger.debug("Patched {} successfully.", transformedName); - return writer.toByteArray(); - } -} diff --git a/src/main/java/com/falsepattern/endlessids/asm/transformer/DevFixer.java b/src/main/java/com/falsepattern/endlessids/asm/transformer/DevFixer.java index c6b1e28..16ef3a5 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/transformer/DevFixer.java +++ b/src/main/java/com/falsepattern/endlessids/asm/transformer/DevFixer.java @@ -1,27 +1,25 @@ package com.falsepattern.endlessids.asm.transformer; -import com.falsepattern.endlessids.asm.IETransformer; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.SneakyThrows; +import com.falsepattern.endlessids.Tags; +import com.falsepattern.endlessids.asm.EndlessIDsCore; +import com.falsepattern.lib.turboasm.ClassNodeHandle; +import com.falsepattern.lib.turboasm.TurboClassTransformer; import lombok.val; +import org.jetbrains.annotations.NotNull; import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.util.CheckClassAdapter; import net.minecraft.launchwrapper.Launch; import java.io.IOException; -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class DevFixer { +public class DevFixer implements TurboClassTransformer { private static final ClassNode blocks; static { - if (IETransformer.isObfuscated) { + if (!EndlessIDsCore.deobfuscated) { blocks = null; } else { try { @@ -35,41 +33,20 @@ public final class DevFixer { } } - public static byte[] fixDev(byte[] classBytes) { - if (IETransformer.isObfuscated) { - return classBytes; - } - val cn = new ClassNode(Opcodes.ASM5); - val reader = new ClassReader(classBytes); - reader.accept(cn, 0); - transform(cn); - val writer = new ClassWriter(0); - val check = new CheckClassAdapter(writer); - cn.accept(check); - return writer.toByteArray(); - } - - @SneakyThrows - public static void transform(final ClassNode cn) { - if (IETransformer.isObfuscated) { - return; - } - remapBlocks(cn); - if (cn.name.equals("net/minecraft/world/biome/BiomeGenBase")) { - biomeTweakerCompat(cn); - } - } - - private static void biomeTweakerCompat(final ClassNode cn) { + private static boolean biomeTweakerCompat(final ClassNode cn) { + boolean modified = false; for (val field : cn.fields) { if (field.name.startsWith("spawnable")) { field.access &= ~Opcodes.ACC_PROTECTED; field.access |= Opcodes.ACC_PUBLIC; + modified = true; } } + return modified; } - private static void remapBlocks(final ClassNode cn) { + private static boolean remapBlocks(final ClassNode cn) { + boolean modified = false; for (val method : cn.methods) { val instructions = method.instructions; int insnCount = instructions.size(); @@ -81,11 +58,41 @@ public final class DevFixer { for (val blockField : blocks.fields) { if (blockField.name.equals(field.name) && !blockField.desc.equals(field.desc)) { field.desc = blockField.desc; + modified = true; } } } } } } + return modified; + } + + @Override + public String owner() { + return Tags.MODNAME; + } + + @Override + public String name() { + return "DevFixer"; + } + + @Override + public boolean shouldTransformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + return EndlessIDsCore.deobfuscated && !"net.minecraft.world.chunk.storage.AnvilChunkLoader".equals(className); + } + + @Override + public boolean transformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + val cn = classNode.getNode(); + if (cn == null) + return false; + boolean modified = false; + modified |= remapBlocks(cn); + if (className.equals("net.minecraft.world.biome.BiomeGenBase")) { + modified |= biomeTweakerCompat(cn); + } + return modified; } } diff --git a/src/main/java/com/falsepattern/endlessids/asm/transformer/FmlRegistry.java b/src/main/java/com/falsepattern/endlessids/asm/transformer/FmlRegistry.java index f92ea21..6e577fb 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/transformer/FmlRegistry.java +++ b/src/main/java/com/falsepattern/endlessids/asm/transformer/FmlRegistry.java @@ -1,21 +1,46 @@ package com.falsepattern.endlessids.asm.transformer; +import com.falsepattern.endlessids.Tags; import com.falsepattern.endlessids.asm.AsmTransformException; import com.falsepattern.endlessids.asm.AsmUtil; import com.falsepattern.endlessids.asm.IClassNodeTransformer; +import com.falsepattern.endlessids.config.GeneralConfig; import com.falsepattern.endlessids.constants.ExtendedConstants; import com.falsepattern.endlessids.constants.VanillaConstants; +import com.falsepattern.lib.turboasm.ClassNodeHandle; +import com.falsepattern.lib.turboasm.TurboClassTransformer; +import lombok.val; import lombok.var; +import org.jetbrains.annotations.NotNull; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; -public class FmlRegistry implements IClassNodeTransformer { +public class FmlRegistry implements TurboClassTransformer { public FmlRegistry() { super(); } @Override - public void transform(final ClassNode cn, final boolean obfuscated) { + public String owner() { + return Tags.MODNAME; + } + + @Override + public String name() { + return "FMLRegistry"; + } + + @Override + public boolean shouldTransformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + return GeneralConfig.extendBlockItem && ("cpw.mods.fml.common.registry.GameData".equals(className) || + "cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry".equals(className)); + } + + @Override + public boolean transformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + val cn = classNode.getNode(); + if (cn == null) + return false; var field = AsmUtil.findField(cn, "MAX_BLOCK_ID", true); if (field != null) { field.value = ExtendedConstants.maxBlockID; @@ -35,5 +60,6 @@ public class FmlRegistry implements IClassNodeTransformer { if (!found) { throw new AsmTransformException("can't find 4095 constant in any method"); } + return true; } } diff --git a/src/main/java/com/falsepattern/endlessids/asm/transformer/GameDataAccelerator.java b/src/main/java/com/falsepattern/endlessids/asm/transformer/GameDataAccelerator.java index 40ca3c0..efd8c85 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/transformer/GameDataAccelerator.java +++ b/src/main/java/com/falsepattern/endlessids/asm/transformer/GameDataAccelerator.java @@ -1,12 +1,15 @@ package com.falsepattern.endlessids.asm.transformer; +import com.falsepattern.endlessids.Tags; import com.falsepattern.endlessids.asm.AsmTransformException; -import com.falsepattern.endlessids.asm.IClassNodeTransformer; -import com.falsepattern.endlessids.asm.IETransformer; +import com.falsepattern.endlessids.asm.EndlessIDsTransformer; +import com.falsepattern.endlessids.config.GeneralConfig; +import com.falsepattern.lib.turboasm.ClassNodeHandle; +import com.falsepattern.lib.turboasm.TurboClassTransformer; import lombok.val; import lombok.var; +import org.jetbrains.annotations.NotNull; import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.InsnNode; @@ -14,35 +17,9 @@ import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.VarInsnNode; -public class GameDataAccelerator implements IClassNodeTransformer { +public class GameDataAccelerator implements TurboClassTransformer { private static final int MASK_REGISTER_BLOCK = 0b1; private static final int MASK_REGISTER_ITEM = 0b10; - @Override - public void transform(ClassNode classNode, boolean obfuscated) { - var cacheField = new FieldNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "endlessIDsItemBlockCache", "Lcom/falsepattern/endlessids/util/WeakIdentityHashMap;", null, null); - classNode.fields.add(cacheField); - 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(); @@ -65,7 +42,7 @@ public class GameDataAccelerator implements IClassNodeTransformer { } private static void transformError(MethodNode method) { - IETransformer.logger.error("Could not add caching code to GameData." + method.name + method.desc); + EndlessIDsTransformer.logger.error("Could not add caching code to GameData." + method.name + method.desc); } private void transformRegisterBlock(MethodNode method) { @@ -106,4 +83,46 @@ public class GameDataAccelerator implements IClassNodeTransformer { } transformError(method); } + + @Override + public String owner() { + return Tags.MODNAME; + } + + @Override + public String name() { + return "GameDataAccelerator"; + } + + @Override + public boolean shouldTransformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + return GeneralConfig.enableRegistryPerformanceTweak && "cpw.mods.fml.common.registry.GameData".equals(className); + } + + @Override + public boolean transformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + val cn = classNode.getNode(); + if (cn == null) + return false; + var cacheField = new FieldNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "endlessIDsItemBlockCache", "Lcom/falsepattern/endlessids/util/WeakIdentityHashMap;", null, null); + cn.fields.add(cacheField); + int tweaksLeft = MASK_REGISTER_BLOCK | MASK_REGISTER_ITEM; + for (val method: cn.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; + } else 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) { + return true; + } + } + throw new AsmTransformException("Failed to accelerate block registration! Disable enableRegistryPerformanceTweak in the EndlessIDs config!"); + } } diff --git a/src/main/java/com/falsepattern/endlessids/asm/transformer/LOTRFieldExposer.java b/src/main/java/com/falsepattern/endlessids/asm/transformer/LOTRFieldExposer.java deleted file mode 100644 index 60f87df..0000000 --- a/src/main/java/com/falsepattern/endlessids/asm/transformer/LOTRFieldExposer.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.falsepattern.endlessids.asm.transformer; - -import com.falsepattern.endlessids.asm.IClassNodeTransformer; -import lombok.val; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; - -public class LOTRFieldExposer implements IClassNodeTransformer { - @Override - public void transform(ClassNode classNode, boolean obfuscated) { - for (val field: classNode.fields) { - if ("chunkX".equals(field.name) || - "chunkZ".equals(field.name) || - "variants".equals(field.name)) { - field.access &= ~Opcodes.ACC_PRIVATE; - field.access |= Opcodes.ACC_PUBLIC; - } - } - } -} diff --git a/src/main/java/com/falsepattern/endlessids/asm/transformer/SpaceCoreModInfoGenerator.java b/src/main/java/com/falsepattern/endlessids/asm/transformer/SpaceCoreModInfoGenerator.java index 3f44fb4..1f6969d 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/transformer/SpaceCoreModInfoGenerator.java +++ b/src/main/java/com/falsepattern/endlessids/asm/transformer/SpaceCoreModInfoGenerator.java @@ -1,22 +1,43 @@ package com.falsepattern.endlessids.asm.transformer; -import com.falsepattern.endlessids.asm.IClassNodeTransformer; +import com.falsepattern.endlessids.Tags; +import com.falsepattern.endlessids.asm.EndlessIDsCore; +import com.falsepattern.endlessids.asm.EndlessIDsTransformer; +import com.falsepattern.lib.turboasm.ClassNodeHandle; +import com.falsepattern.lib.turboasm.TurboClassTransformer; import lombok.val; +import org.jetbrains.annotations.NotNull; import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.InsnNode; -public class SpaceCoreModInfoGenerator implements IClassNodeTransformer { +public class SpaceCoreModInfoGenerator implements TurboClassTransformer { @Override - public void transform(final ClassNode cn, final boolean obfuscated) { - if (obfuscated) { - return; - } + public String owner() { + return Tags.MODNAME; + } + + @Override + public String name() { + return "SpaceCoreModInfoGenerator"; + } + + @Override + public boolean shouldTransformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + return EndlessIDsCore.deobfuscated && "com.spacechase0.minecraft.spacecore.mcp.ModInfoGenerator".equals(className); + } + + @Override + public boolean transformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + val cn = classNode.getNode(); + if (cn == null) + return false; for (val method : cn.methods) { if (method.name.equals("generate")) { val iterator = method.instructions.iterator(); iterator.add(new InsnNode(Opcodes.RETURN)); + return true; } } + return false; } } diff --git a/src/main/java/com/falsepattern/endlessids/asm/transformer/ChunkProviderSuperPatcher.java b/src/main/java/com/falsepattern/endlessids/asm/transformer/chunk/ChunkProviderSuperPatcher.java similarity index 65% rename from src/main/java/com/falsepattern/endlessids/asm/transformer/ChunkProviderSuperPatcher.java rename to src/main/java/com/falsepattern/endlessids/asm/transformer/chunk/ChunkProviderSuperPatcher.java index cebef61..3ed43b9 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/transformer/ChunkProviderSuperPatcher.java +++ b/src/main/java/com/falsepattern/endlessids/asm/transformer/chunk/ChunkProviderSuperPatcher.java @@ -1,11 +1,14 @@ -package com.falsepattern.endlessids.asm.transformer; +package com.falsepattern.endlessids.asm.transformer.chunk; -import com.falsepattern.endlessids.asm.IClassNodeTransformer; -import com.falsepattern.endlessids.asm.IETransformer; -import com.falsepattern.endlessids.config.GeneralConfig; +import com.falsepattern.endlessids.Tags; +import com.falsepattern.endlessids.asm.EndlessIDsCore; +import com.falsepattern.endlessids.asm.EndlessIDsTransformer; +import com.falsepattern.lib.turboasm.ClassNodeHandle; +import com.falsepattern.lib.turboasm.TurboClassTransformer; import lombok.Data; import lombok.val; import lombok.var; +import org.jetbrains.annotations.NotNull; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; @@ -21,6 +24,7 @@ import org.objectweb.asm.tree.VarInsnNode; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; @@ -116,7 +120,7 @@ import java.util.function.Supplier; * L6 * */ -public class ChunkProviderSuperPatcher implements IClassNodeTransformer { +public class ChunkProviderSuperPatcher implements TurboClassTransformer { public static final String[] CLASS_BiomeGenBase; public static final String[] CLASS_IChunkProvider; public static final String[] CLASS_ChunkProviderGenerate; @@ -149,20 +153,20 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { private static State STATE_FINAL = null; static { - if (IETransformer.isObfuscated) { - CLASS_BiomeGenBase = new String[]{"ahu", "net/minecraft/world/biome/BiomeGenBase"}; - CLASS_IChunkProvider = new String[]{"apu", "net/minecraft/world/chunk/IChunkProvider"}; - CLASS_ChunkProviderGenerate = new String[]{"aqz", "net/minecraft/world/gen/ChunkProviderGenerate"}; - CLASS_Chunk = new String[]{"apx", "net/minecraft/world/chunk/Chunk"}; - FIELD_biomeID = new String[]{"ay", "field_76756_M"}; - METHOD_getBiomeArray = new String[]{"m", "func_76605_m"}; - } else { + if (EndlessIDsCore.deobfuscated) { CLASS_BiomeGenBase = new String[]{"net/minecraft/world/biome/BiomeGenBase"}; CLASS_IChunkProvider = new String[]{"net/minecraft/world/chunk/IChunkProvider"}; CLASS_ChunkProviderGenerate = new String[]{"net/minecraft/world/gen/ChunkProviderGenerate"}; CLASS_Chunk = new String[]{"net/minecraft/world/chunk/Chunk"}; FIELD_biomeID = new String[]{"biomeID"}; METHOD_getBiomeArray = new String[]{"getBiomeArray"}; + } else { + CLASS_BiomeGenBase = new String[]{"ahu", "net/minecraft/world/biome/BiomeGenBase"}; + CLASS_IChunkProvider = new String[]{"apu", "net/minecraft/world/chunk/IChunkProvider"}; + CLASS_ChunkProviderGenerate = new String[]{"aqz", "net/minecraft/world/gen/ChunkProviderGenerate"}; + CLASS_Chunk = new String[]{"apx", "net/minecraft/world/chunk/Chunk"}; + FIELD_biomeID = new String[]{"ay", "field_76756_M"}; + METHOD_getBiomeArray = new String[]{"m", "func_76605_m"}; } } @@ -170,9 +174,10 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { STATE_FINAL = (insn, memory) -> STATE_FINAL; STATE0 = casting(VarInsnNode.class, (insn, memory) -> { + memory.reset(); if (insn.getOpcode() == Opcodes.ALOAD) { memory.chunkIndex = insn.var; - memory.startingInsn = memory.currentInsn; + memory.startingInsn = insn; return STATE1; } return STATE0; @@ -183,7 +188,7 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { anyMatch(insn.owner, CLASS_Chunk) && anyMatch(insn.name, METHOD_getBiomeArray) && insn.desc.equals("()[B")) { - memory.getBiomeArrayInsn = memory.currentInsn; + memory.getBiomeArrayInsn = insn; return STATE2; } return STATE0; @@ -192,6 +197,7 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { STATE2 = casting(VarInsnNode.class, (insn, memory) -> { if (insn.getOpcode() == Opcodes.ASTORE) { memory.biomeArrayIndex = insn.var; + memory.startPatchingFrames(); return STATE3; } return STATE0; @@ -294,7 +300,7 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { STATE16 = casting(InsnNode.class, (insn, memory) -> { if (insn.getOpcode() == Opcodes.I2B) { - memory.castInsn = memory.currentInsn; + memory.castInsn = insn; return STATE17; } return STATE0; @@ -302,25 +308,13 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { STATE17 = casting(InsnNode.class, (insn, memory) -> { if (insn.getOpcode() == Opcodes.BASTORE) { - memory.arrayStoreInsn = memory.currentInsn; + memory.arrayStoreInsn = insn; return STATE_FINAL; } return STATE0; }); } - public static boolean shouldPatch(final ClassNode cn) { - if (!GeneralConfig.extendBiome) { - return false; - } - for (MethodNode method : cn.methods) { - if (scanForBrokenCall(method)) { - return true; - } - } - return false; - } - private static boolean scanForBrokenCall(MethodNode method) { val instructions = method.instructions; val insnCount = instructions.size(); @@ -388,23 +382,28 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { }; } - private static Memory findTheTarget(MethodNode method) { + private static Memory findTheTarget(MethodNode method, ClassNode cn) { val insnList = method.instructions; var currentState = STATE0; val memory = new Memory(); - for (int insnIndex = 0; insnIndex < insnList.size(); insnIndex++) { - memory.currentInsn = insnIndex; - val insn = insnList.get(insnIndex); + memory.initLocals(method, cn); + val iter = insnList.iterator(); + while (iter.hasNext()) { + val insn = iter.next(); if (insn instanceof FrameNode) { val frame = (FrameNode) insn; - if (frame.type == Opcodes.F_NEW) { - memory.locals.clear(); - memory.locals.addAll(frame.local); - } + memory.recordFrame(frame); + memory.updateLocalsState(frame); } val newState = currentState.apply(insn, memory); if (newState != null) { if (newState == STATE_FINAL) { + while (iter.hasNext()) { + val insn2 = iter.next(); + if (insn2 instanceof FrameNode) { + memory.recordFrame((FrameNode) insn2); + } + } return memory; } currentState = newState; @@ -414,30 +413,93 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { } @Override - public void transform(final ClassNode cn, final boolean obfuscated) { + public String owner() { + return Tags.MODNAME; + } + + @Override + public String name() { + return "ChunkProviderSuperPatcher"; + } + + @Override + public boolean shouldTransformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + if ("net.minecraft.world.chunk.storage.AnvilChunkLoader".equals(className)) + return false; + val cn = classNode.getNode(); + if (cn == null) + return false; + for (MethodNode method : cn.methods) { + if (scanForBrokenCall(method)) { + return true; + } + } + return false; + } + + @Override + public boolean transformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + val cn = classNode.getNode(); + if (cn == null) + return false; boolean patched = false; for (val method : cn.methods) { - val memory = findTheTarget(method); + val memory = findTheTarget(method, cn); if (memory != null) { patched = true; - IETransformer.logger.debug("[ChunkProvider patcher]: Match found in method " + method.name); + EndlessIDsTransformer.logger.debug("[ChunkProvider patcher]: Match found in method " + method.name); val insnList = method.instructions; for (val node : method.localVariables) { if (node.desc.equals("[B") && node.index == memory.biomeArrayIndex) { node.desc = "[S"; } } - val getBiomeArray = (MethodInsnNode) insnList.get(memory.getBiomeArrayInsn); + val getBiomeArray = memory.getBiomeArrayInsn; insnList.set(getBiomeArray, new MethodInsnNode(Opcodes.INVOKEVIRTUAL, getBiomeArray.owner, "getBiomeShortArray", "()[S", false)); - insnList.set(insnList.get(memory.castInsn), new InsnNode(Opcodes.I2S)); - insnList.set(insnList.get(memory.arrayStoreInsn), new InsnNode(Opcodes.SASTORE)); + insnList.set(memory.castInsn, new InsnNode(Opcodes.I2S)); + insnList.set(memory.arrayStoreInsn, new InsnNode(Opcodes.SASTORE)); + patchStackFrames(memory, method, cn); } } if (!patched) { - IETransformer.logger.debug("[ChunkProvider patcher]: No matches found in class! This is not explicitly " + - "an error, because the patch might be applied by a mixin instead!"); + EndlessIDsTransformer.logger.debug("[ChunkProvider patcher]: No matches found in class! This is not explicitly " + + "an error, because the patch might be applied by a mixin instead!"); + } + return patched; + } + + + private static void patchStackFrames(Memory memory, MethodNode method, ClassNode owner) { + memory.locals.clear(); + memory.initLocals(method, owner); + for (val frame: memory.framesLeaveUnpatched) + memory.updateLocalsState(frame); + + int localToPatch = memory.biomeArrayIndex; + for (val frame: memory.framesToPatch) { + int currentLocalIndex = memory.locals.size() - 1; + if (frame.local != null) { + switch (frame.type) { + case Opcodes.F_NEW: + case Opcodes.F_FULL: + if (frame.local.size() > localToPatch && + "[B".equals(frame.local.get(localToPatch))) { + frame.local.set(localToPatch, "[S"); + } + break; + case Opcodes.F_APPEND: + if (currentLocalIndex < localToPatch && + (currentLocalIndex + frame.local.size()) >= localToPatch) { + int indexInFrame = localToPatch - currentLocalIndex - 1; + if ("[B".equals(frame.local.get(indexInFrame))) { + frame.local.set(indexInFrame, "[S"); + } + } + } + } + memory.updateLocalsState(frame); } } @@ -449,13 +511,125 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { private static class Memory { final List locals = new ArrayList<>(); - int currentInsn; - int startingInsn; - int getBiomeArrayInsn; - int castInsn; - int arrayStoreInsn; + List localsAtPatchStart; + List framesLeaveUnpatched = new ArrayList<>(); + List framesToPatch = new ArrayList<>(); + boolean patchingFrames; + VarInsnNode startingInsn; + MethodInsnNode getBiomeArrayInsn; + InsnNode castInsn; + InsnNode arrayStoreInsn; int chunkIndex; int biomeArrayIndex; int iteratorIndex; + + public void startPatchingFrames() { + localsAtPatchStart = new ArrayList<>(locals); + patchingFrames = true; + } + + public void recordFrame(FrameNode frame) { + if (patchingFrames) + framesToPatch.add(frame); + else + framesLeaveUnpatched.add(frame); + } + + public void reset() { + localsAtPatchStart = null; + framesLeaveUnpatched.addAll(framesToPatch); + framesToPatch.clear(); + patchingFrames = false; + startingInsn = null; + getBiomeArrayInsn = null; + castInsn = null; + arrayStoreInsn = null; + chunkIndex = 0; + biomeArrayIndex = 0; + iteratorIndex = 0; + } + + private static int getDescriptorNameEnd(String desc, int index) { + val c = desc.charAt(index); + switch (c) { + case '[': + return getDescriptorNameEnd(desc, index + 1); + case 'L': + do { + index++; + } while (desc.charAt(index) != ';'); + default: + return index + 1; + } + } + + private static List parseDesc(String desc) { + val result = new ArrayList(); + desc = desc.substring(1, desc.indexOf(')')); + for (int i = 0; i < desc.length(); i++) { + val end = getDescriptorNameEnd(desc, i); + val name = desc.substring(i, end); + switch (name) { + case "I": + case "S": + case "B": + case "Z": + result.add(Opcodes.INTEGER); + break; + case "F": + result.add(Opcodes.FLOAT); + break; + case "D": + result.add(Opcodes.DOUBLE); + result.add(Opcodes.DOUBLE); + break; + case "J": + result.add(Opcodes.LONG); + result.add(Opcodes.LONG); + break; + default: + result.add(name); + } + i = end - 1; + } + return result; + } + + public void initLocals(MethodNode method, ClassNode owner) { + locals.clear(); + if ((method.access & Opcodes.ACC_STATIC) == 0) { + locals.add("L" + owner.name + ";"); + } + locals.addAll(parseDesc(method.desc)); + } + + public void updateLocalsState(FrameNode frame) { + switch (frame.type) { + case Opcodes.F_NEW: + case Opcodes.F_FULL: + locals.clear(); + case Opcodes.F_APPEND: + if (frame.local != null) { + for (val local : frame.local) { + if (Objects.equals(local, Opcodes.DOUBLE) || Objects.equals(local, Opcodes.LONG)) + locals.add(local); + locals.add(local); + } + } + break; + case Opcodes.F_CHOP: { + int removeCount = frame.local.size(); + for (int i = removeCount - 1; i >= 0 && !locals.isEmpty(); i--) { + val removed = locals.remove(locals.size() - 1); + if (Objects.equals(removed, Opcodes.DOUBLE) || Objects.equals(removed, Opcodes.LONG)) + locals.remove(locals.size() - 1); + } + break; + } + case Opcodes.F_SAME: + case Opcodes.F_SAME1: + break; + } + } } } \ No newline at end of file diff --git a/src/main/java/com/falsepattern/endlessids/asm/transformer/chunk/LOTRFieldExposer.java b/src/main/java/com/falsepattern/endlessids/asm/transformer/chunk/LOTRFieldExposer.java new file mode 100644 index 0000000..57e0648 --- /dev/null +++ b/src/main/java/com/falsepattern/endlessids/asm/transformer/chunk/LOTRFieldExposer.java @@ -0,0 +1,41 @@ +package com.falsepattern.endlessids.asm.transformer.chunk; + +import com.falsepattern.endlessids.Tags; +import com.falsepattern.lib.turboasm.ClassNodeHandle; +import com.falsepattern.lib.turboasm.TurboClassTransformer; +import lombok.val; +import org.jetbrains.annotations.NotNull; +import org.objectweb.asm.Opcodes; + +public class LOTRFieldExposer implements TurboClassTransformer { + @Override + public String owner() { + return Tags.MODNAME; + } + + @Override + public String name() { + return "LOTRFieldExposer"; + } + + @Override + public boolean shouldTransformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + return "lotr.common.network.LOTRPacketBiomeVariantsWatch".equals(className); + } + + @Override + public boolean transformClass(@NotNull String className, @NotNull ClassNodeHandle classNode) { + val cn = classNode.getNode(); + if (cn == null) + return false; + for (val field: cn.fields) { + if ("chunkX".equals(field.name) || + "chunkZ".equals(field.name) || + "variants".equals(field.name)) { + field.access &= ~Opcodes.ACC_PRIVATE; + field.access |= Opcodes.ACC_PUBLIC; + } + } + return true; + } +}