Added ASM for injecting dimensions
parent
50975ebb0f
commit
d5759b3595
@ -1,138 +0,0 @@
|
||||
package com.zivilon.cinder_loe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lotr.common.LOTRAchievement;
|
||||
import lotr.common.fac.LOTRFaction;
|
||||
import lotr.common.world.LOTRWorldProvider;
|
||||
import lotr.common.world.LOTRWorldProviderMiddleEarth;
|
||||
import lotr.common.world.LOTRWorldProviderUtumno;
|
||||
import lotr.common.world.biome.LOTRBiome;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldProvider;
|
||||
import net.minecraftforge.common.DimensionManager;
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public enum CinderDimension {
|
||||
ISLAND("Island", 101, LOTRWorldProviderMiddleEarth.class, false, 500, EnumSet.of(DimensionRegion.REG));
|
||||
|
||||
public String dimensionName;
|
||||
private int defaultID;
|
||||
public int dimensionID;
|
||||
private Class providerClass;
|
||||
private boolean loadSpawn;
|
||||
public LOTRBiome[] biomeList = new LOTRBiome[256];
|
||||
public Map<Integer, Integer> colorsToBiomeIDs = new HashMap<Integer, Integer>();
|
||||
public List<LOTRBiome> majorBiomes = new ArrayList<LOTRBiome>();
|
||||
public List<LOTRAchievement.Category> achievementCategories = new ArrayList<LOTRAchievement.Category>();
|
||||
public List<LOTRAchievement> allAchievements = new ArrayList<LOTRAchievement>();
|
||||
public List<LOTRFaction> factionList = new ArrayList<LOTRFaction>();
|
||||
public List<CinderDimension.DimensionRegion> dimensionRegions = new ArrayList<CinderDimension.DimensionRegion>();
|
||||
public int spawnCap;
|
||||
|
||||
private CinderDimension(String s, int i, Class c, boolean flag, int spawns, EnumSet<DimensionRegion> regions) {
|
||||
this.dimensionName = s;
|
||||
this.defaultID = i;
|
||||
this.providerClass = c;
|
||||
this.loadSpawn = flag;
|
||||
this.spawnCap = spawns;
|
||||
this.dimensionRegions.addAll(regions);
|
||||
for (DimensionRegion r : this.dimensionRegions) {
|
||||
r.setDimension(this);
|
||||
}
|
||||
}
|
||||
public String getUntranslatedDimensionName() {
|
||||
return "lotr.dimension." + this.dimensionName;
|
||||
}
|
||||
|
||||
public String getDimensionName() {
|
||||
return StatCollector.translateToLocal((String)this.getUntranslatedDimensionName());
|
||||
}
|
||||
|
||||
public static void configureDimensions(Configuration config, String category) {
|
||||
for (CinderDimension dim : CinderDimension.values()) {
|
||||
dim.dimensionID = config.get(category, "Dimension ID: " + dim.dimensionName, dim.defaultID).getInt();
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerDimensions() {
|
||||
for (CinderDimension dim : CinderDimension.values()) {
|
||||
DimensionManager.registerProviderType((int)dim.dimensionID, (Class)dim.providerClass, (boolean)dim.loadSpawn);
|
||||
DimensionManager.registerDimension((int)dim.dimensionID, (int)dim.dimensionID);
|
||||
}
|
||||
}
|
||||
|
||||
public static CinderDimension getCurrentDimension(World world) {
|
||||
WorldProvider provider;
|
||||
if (world != null && (provider = world.provider) instanceof LOTRWorldProvider) {
|
||||
return ((LOTRWorldProvider)provider).getLOTRDimension();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static CinderDimension getCurrentDimensionWithFallback(World world) {
|
||||
CinderDimension dim = CinderDimension.getCurrentDimension(world);
|
||||
if (dim == null) {
|
||||
return ISLAND;
|
||||
}
|
||||
return dim;
|
||||
}
|
||||
|
||||
public static CinderDimension forName(String s) {
|
||||
for (CinderDimension dim : CinderDimension.values()) {
|
||||
if (!dim.dimensionName.equals(s)) continue;
|
||||
return dim;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static enum DimensionRegion {
|
||||
REG("island");
|
||||
|
||||
private String regionName;
|
||||
private CinderDimension dimension;
|
||||
public List<LOTRFaction> factionList = new ArrayList<LOTRFaction>();
|
||||
|
||||
private DimensionRegion(String s) {
|
||||
this.regionName = s;
|
||||
}
|
||||
|
||||
public void setDimension(CinderDimension dim) {
|
||||
this.dimension = dim;
|
||||
}
|
||||
|
||||
public CinderDimension getDimension() {
|
||||
return this.dimension;
|
||||
}
|
||||
|
||||
public String codeName() {
|
||||
return this.regionName;
|
||||
}
|
||||
|
||||
public String getRegionName() {
|
||||
return StatCollector.translateToLocal((String)("lotr.dimension." + this.dimension.dimensionName + "." + this.codeName()));
|
||||
}
|
||||
|
||||
public static DimensionRegion forName(String regionName) {
|
||||
for (DimensionRegion r : DimensionRegion.values()) {
|
||||
if (!r.codeName().equals(regionName)) continue;
|
||||
return r;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static DimensionRegion forID(int ID) {
|
||||
for (DimensionRegion r : DimensionRegion.values()) {
|
||||
if (r.ordinal() != ID) continue;
|
||||
return r;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,206 @@
|
||||
package com.zivilon.cinder_loe.coremod;
|
||||
|
||||
import com.zivilon.cinder_loe.world.CinderWorldProviderIsland;
|
||||
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.IntInsnNode;
|
||||
import org.objectweb.asm.tree.TypeInsnNode;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.LdcInsnNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class LOTRDimensionAdder implements IClassTransformer {
|
||||
public static List<DimensionInfo> custom_dimensions = new ArrayList<>();
|
||||
|
||||
// This is where you add new dimensions
|
||||
public void registerDimensions() {
|
||||
System.out.println("Registering dimensions");
|
||||
custom_dimensions = new ArrayList<>();
|
||||
// register("ISLAND", "Island", 102, CinderWorldProviderIsland.class, 100);
|
||||
}
|
||||
|
||||
// The ASM code you shouldn't touch
|
||||
@Override
|
||||
public byte[] transform(String name, String transformedName, byte[] basicClass) {
|
||||
if ("lotr.common.LOTRDimension".equals(transformedName)) {
|
||||
registerDimensions();
|
||||
if (custom_dimensions.isEmpty()) return basicClass;
|
||||
|
||||
// Get class
|
||||
ClassReader classReader = new ClassReader(basicClass);
|
||||
ClassNode classNode = new ClassNode();
|
||||
classReader.accept(classNode, 0);
|
||||
|
||||
|
||||
// Add the new enum constant
|
||||
for (DimensionInfo dimension : custom_dimensions) {
|
||||
FieldNode newEnumConstant = new FieldNode(
|
||||
Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL + Opcodes.ACC_ENUM,
|
||||
dimension.enum_name,
|
||||
"Llotr/common/LOTRDimension;",
|
||||
null,
|
||||
null
|
||||
);
|
||||
classNode.fields.add(newEnumConstant);
|
||||
}
|
||||
|
||||
|
||||
// Locate <clinit>
|
||||
MethodNode clinit = null;
|
||||
for (MethodNode method : classNode.methods) {
|
||||
if ("<clinit>".equals(method.name)) {
|
||||
clinit = method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
InsnList insns = clinit.instructions;
|
||||
AbstractInsnNode constructor_injection_point = null;
|
||||
|
||||
for (AbstractInsnNode insn : clinit.instructions.toArray()) {
|
||||
if (insn.getOpcode() == Opcodes.ICONST_2) {
|
||||
constructor_injection_point = insn;
|
||||
int list_size = 2 + custom_dimensions.size();
|
||||
IntInsnNode push_size = new IntInsnNode(Opcodes.BIPUSH, 2 + custom_dimensions.size());
|
||||
clinit.instructions.insert(insn, push_size);
|
||||
clinit.instructions.remove(insn);
|
||||
constructor_injection_point = push_size;
|
||||
System.out.println("LOTRDimension list size: " + list_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create the constructor instructions to add new dimension
|
||||
InsnList constructor_injection = new InsnList();
|
||||
int i = 2;
|
||||
for (DimensionInfo dimension : custom_dimensions) {
|
||||
System.out.println("Registering with enum " + dimension.enum_name);
|
||||
System.out.println("Registering with ordinal " + i);
|
||||
System.out.println("Registering with name " + dimension.dimension_name);
|
||||
System.out.println("Registering with ID " + dimension.dimension_id);
|
||||
|
||||
constructor_injection.add(new TypeInsnNode(Opcodes.NEW, "lotr/common/LOTRDimension"));
|
||||
constructor_injection.add(new InsnNode(Opcodes.DUP));
|
||||
constructor_injection.add(new LdcInsnNode(dimension.enum_name));
|
||||
constructor_injection.add(new IntInsnNode(Opcodes.BIPUSH, i));
|
||||
constructor_injection.add(new LdcInsnNode(dimension.dimension_name));
|
||||
constructor_injection.add(new IntInsnNode(Opcodes.BIPUSH, dimension.dimension_id));
|
||||
constructor_injection.add(new LdcInsnNode(Type.getType(dimension.world_provider))); // World provider class
|
||||
constructor_injection.add(new InsnNode(Opcodes.ICONST_0)); // Do not load spawn persistently
|
||||
constructor_injection.add(new IntInsnNode(Opcodes.BIPUSH, dimension.spawn_limit)); // Set spawn cap for dimension
|
||||
|
||||
// Add no DimensionRegions
|
||||
constructor_injection.add(new LdcInsnNode(Type.getType("Llotr/common/LOTRDimension$DimensionRegion;")));
|
||||
constructor_injection.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/EnumSet", "noneOf", "(Ljava/lang/Class;)Ljava/util/EnumSet;", false));
|
||||
|
||||
// Add PUTSTATIC
|
||||
constructor_injection.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "lotr/common/LOTRDimension", "<init>", "(Ljava/lang/String;ILjava/lang/String;ILjava/lang/Class;ZILjava/util/EnumSet;)V", false));
|
||||
constructor_injection.add(new FieldInsnNode(Opcodes.PUTSTATIC, "lotr/common/LOTRDimension", dimension.enum_name, "Llotr/common/LOTRDimension;"));
|
||||
i++;
|
||||
}
|
||||
// Insert the new instructions
|
||||
if (constructor_injection_point != null) {
|
||||
insns.insertBefore(constructor_injection_point, constructor_injection);
|
||||
}
|
||||
|
||||
// Modifying the $VALUES array
|
||||
// Create new instruction list to be injected later
|
||||
InsnList values_array_injection = new InsnList();
|
||||
|
||||
// Add instructions to the instruction list
|
||||
i = 2;
|
||||
for (DimensionInfo dimension : custom_dimensions) {
|
||||
values_array_injection.add(new InsnNode(Opcodes.DUP));
|
||||
values_array_injection.add(new IntInsnNode(Opcodes.BIPUSH, i));
|
||||
values_array_injection.add(new FieldInsnNode(Opcodes.GETSTATIC, "lotr/common/LOTRDimension", dimension.enum_name, "Llotr/common/LOTRDimension;"));
|
||||
values_array_injection.add(new InsnNode(Opcodes.AASTORE));
|
||||
i++;
|
||||
}
|
||||
|
||||
// Find the putstatic instruction for $VALUES
|
||||
// This is where the fields are injected into a list, we want to inject our instructions before this
|
||||
AbstractInsnNode values_injection_point = null;
|
||||
|
||||
AbstractInsnNode cursor = constructor_injection_point;
|
||||
while (cursor != null) {
|
||||
if (cursor.getOpcode() == Opcodes.PUTSTATIC) {
|
||||
values_injection_point = cursor;
|
||||
break;
|
||||
}
|
||||
cursor = cursor.getNext();
|
||||
}
|
||||
if (values_injection_point == null) {
|
||||
throw new RuntimeException("[CinderLoE] Could not locate $VALUES PUTSTATIC injection point for LOTRDimension!");
|
||||
}
|
||||
|
||||
// Insert the new instructions before the putstatic instruction
|
||||
if (values_injection_point != null) {
|
||||
insns.insertBefore(values_injection_point, values_array_injection);
|
||||
}
|
||||
|
||||
// Write the modified class back to a byte array
|
||||
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
classNode.accept(classWriter);
|
||||
return classWriter.toByteArray(); // Return the modified class
|
||||
}
|
||||
return basicClass; // Return the unmodified class for all other classes
|
||||
}
|
||||
|
||||
public void register(String enum_name, String dimension_name, int dimension_id, Class<?> world_provider, int spawn_limit) {
|
||||
System.out.println("Registering dimension " + enum_name + " " + dimension_name + " with ID " + dimension_id);
|
||||
custom_dimensions.add(new DimensionInfo(enum_name, dimension_name, dimension_id, world_provider, spawn_limit));
|
||||
}
|
||||
|
||||
public class DimensionInfo {
|
||||
String enum_name;
|
||||
String dimension_name;
|
||||
int dimension_id;
|
||||
Class<?> world_provider;
|
||||
int spawn_limit;
|
||||
|
||||
public DimensionInfo(String enum_name, String dimension_name, int dimension_id, Class<?> world_provider, int spawn_limit) {
|
||||
this.enum_name = enum_name;
|
||||
this.dimension_name = dimension_name;
|
||||
this.dimension_id = dimension_id;
|
||||
this.world_provider = world_provider;
|
||||
this.spawn_limit = spawn_limit;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Reference
|
||||
|
||||
|
||||
L0: new lotr/common/LOTRDimension
|
||||
L3: dup
|
||||
L4: ldc 'MIDDLE_EARTH'
|
||||
L6: iconst_0
|
||||
L7: ldc 'MiddleEarth'
|
||||
L9: bipush 100
|
||||
L11: ldc Class lotr/common/world/LOTRWorldProviderMiddleEarth
|
||||
L13: iconst_1
|
||||
L14: bipush 100
|
||||
L16: getstatic Field lotr/common/LOTRDimension$DimensionRegion WEST Llotr/common/LOTRDimension$DimensionRegion;
|
||||
L19: getstatic Field lotr/common/LOTRDimension$DimensionRegion EAST Llotr/common/LOTRDimension$DimensionRegion;
|
||||
L22: getstatic Field lotr/common/LOTRDimension$DimensionRegion SOUTH Llotr/common/LOTRDimension$DimensionRegion;
|
||||
L25: invokestatic Method java/util/EnumSet of (Ljava/lang/Enum;Ljava/lang/Enum;Ljava/lang/Enum;)Ljava/util/EnumSet;
|
||||
L28: invokespecial Method lotr/common/LOTRDimension <init> (Ljava/lang/String;ILjava/lang/String;ILjava/lang/Class;ZILjava/util/EnumSet;)V
|
||||
L31: putstatic Field lotr/common/LOTRDimension MIDDLE_EARTH Llotr/common/LOTRDimension;
|
||||
L34: new lotr/common/LOTRDimension
|
||||
|
||||
*/
|
||||
Loading…
Reference in New Issue