mirror of
https://github.com/GTMEGA/EndlessIDs.git
synced 2026-11-12 18:57:37 +00:00
modernize ASM
This commit is contained in:
15 files changed
+475
-394
No files matched your search
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String, List<ClassEdit>> 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<ClassEdit> 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;
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -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<String>) 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<String, Object> data) {
|
||||
IETransformer.isObfuscated = (Boolean) data.get("runtimeDeobfuscationEnabled");
|
||||
}
|
||||
|
||||
public String getAccessTransformerClass() {
|
||||
@@ -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<TurboClassTransformer> transformers() {
|
||||
val result = new ArrayList<TurboClassTransformer>();
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -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<Block, WeakReference<ItemBlock>> 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<Item> fakeIterable() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -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<String> 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>(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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+50
-31
@@ -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!");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-7
@@ -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;
|
||||
}
|
||||
}
|
||||
+224
-50
@@ -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
|
||||
* </pre>
|
||||
*/
|
||||
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<AbstractInsnNode> 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<Object> locals = new ArrayList<>();
|
||||
int currentInsn;
|
||||
int startingInsn;
|
||||
int getBiomeArrayInsn;
|
||||
int castInsn;
|
||||
int arrayStoreInsn;
|
||||
List<Object> localsAtPatchStart;
|
||||
List<FrameNode> framesLeaveUnpatched = new ArrayList<>();
|
||||
List<FrameNode> 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<Object> parseDesc(String desc) {
|
||||
val result = new ArrayList<Object>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user