diff --git a/src/main/java/com/zivilon/cinder_loe/CinderDimension.java b/src/main/java/com/zivilon/cinder_loe/CinderDimension.java deleted file mode 100644 index 3f73fa1..0000000 --- a/src/main/java/com/zivilon/cinder_loe/CinderDimension.java +++ /dev/null @@ -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 colorsToBiomeIDs = new HashMap(); - public List majorBiomes = new ArrayList(); - public List achievementCategories = new ArrayList(); - public List allAchievements = new ArrayList(); - public List factionList = new ArrayList(); - public List dimensionRegions = new ArrayList(); - public int spawnCap; - - private CinderDimension(String s, int i, Class c, boolean flag, int spawns, EnumSet 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 factionList = new ArrayList(); - - 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; - } - } -} diff --git a/src/main/java/com/zivilon/cinder_loe/coremod/CoreMod.java b/src/main/java/com/zivilon/cinder_loe/coremod/CoreMod.java index 7a0c855..da70b67 100644 --- a/src/main/java/com/zivilon/cinder_loe/coremod/CoreMod.java +++ b/src/main/java/com/zivilon/cinder_loe/coremod/CoreMod.java @@ -14,7 +14,7 @@ import java.util.Map; public class CoreMod implements IFMLLoadingPlugin { @Override public String[] getASMTransformerClass() { - return new String[] {"com.zivilon.cinder_loe.coremod.LOTRMaterialTransformer","com.zivilon.cinder_loe.coremod.LOTRWeaponLinker", "com.zivilon.cinder_loe.coremod.LOTRBannerAdder", "com.zivilon.cinder_loe.coremod.LOTRSpawnListLinker", "com.zivilon.cinder_loe.coremod.OptiFinePatcher"}; + return new String[] {"com.zivilon.cinder_loe.coremod.LOTRMaterialTransformer","com.zivilon.cinder_loe.coremod.LOTRWeaponLinker", "com.zivilon.cinder_loe.coremod.LOTRBannerAdder", "com.zivilon.cinder_loe.coremod.LOTRDimensionAdder", "com.zivilon.cinder_loe.coremod.LOTRSpawnListLinker", "com.zivilon.cinder_loe.coremod.OptiFinePatcher"}; } @Override diff --git a/src/main/java/com/zivilon/cinder_loe/coremod/LOTRDimensionAdder.java b/src/main/java/com/zivilon/cinder_loe/coremod/LOTRDimensionAdder.java new file mode 100644 index 0000000..9795aa5 --- /dev/null +++ b/src/main/java/com/zivilon/cinder_loe/coremod/LOTRDimensionAdder.java @@ -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 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 + MethodNode clinit = null; + for (MethodNode method : classNode.methods) { + if ("".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", "", "(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 (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 + +*/ diff --git a/src/main/java/com/zivilon/cinder_loe/world/CinderWorldProviderIsland.java b/src/main/java/com/zivilon/cinder_loe/world/CinderWorldProviderIsland.java index 13101cb..acc24e8 100644 --- a/src/main/java/com/zivilon/cinder_loe/world/CinderWorldProviderIsland.java +++ b/src/main/java/com/zivilon/cinder_loe/world/CinderWorldProviderIsland.java @@ -1,6 +1,5 @@ package com.zivilon.cinder_loe.world; -import com.zivilon.cinder_loe.CinderDimension; import lotr.common.LOTRDimension; import lotr.common.LOTRLevelData; import lotr.common.world.LOTRChunkProvider; @@ -9,8 +8,13 @@ import net.minecraft.util.ChunkCoordinates; import net.minecraft.world.chunk.IChunkProvider; public class CinderWorldProviderIsland extends LOTRWorldProvider { + public static LOTRDimension cached_enum; - + static { + for (LOTRDimension dimension : LOTRDimension.values()) { + if (dimension.name().equals("ISLAND")) cached_enum = dimension; + } + } public IChunkProvider createChunkGenerator() { return new LOTRChunkProvider(this.worldObj, this.worldObj.getSeed()); @@ -34,6 +38,6 @@ public class CinderWorldProviderIsland extends LOTRWorldProvider { @Override public LOTRDimension getLOTRDimension() { - return CinderDimension.ISLAND; + return cached_enum; } -} \ No newline at end of file +}