diff --git a/build.gradle b/build.gradle index 69482412..50e54003 100644 --- a/build.gradle +++ b/build.gradle @@ -89,8 +89,9 @@ dependencies { libraries('com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.0.10') { exclude group: 'org.apache.logging.log4j' } - libraries 'org.apache.commons:commons-lang3:3.12.0' - libraries 'org.apache.commons:commons-compress:1.21' + libraries 'com.google.code.gson:gson:2.13.2' + libraries 'org.apache.commons:commons-lang3:3.17.0' + libraries 'org.apache.commons:commons-compress:1.27.1' libraries 'org.ow2.asm:asm:9.8' libraries 'org.ow2.asm:asm-commons:9.8' libraries 'org.ow2.asm:asm-tree:9.8' @@ -104,14 +105,12 @@ dependencies { libraries 'it.unimi.dsi:fastutil:8.5.12' // Other libs - libraries 'pw.prok:Damask:0.1.2@jar' libraries 'commons-cli:commons-cli:1.3@jar' libraries 'org.slf4j:slf4j-simple:1.6.2@jar' libraries 'org.eclipse.jetty:jetty-servlet:9.0.3.v20130506@jar' - libraries 'commons-io:commons-io:2.4@jar' + libraries 'commons-io:commons-io:2.18.0' libraries 'net.sf.opencsv:opencsv:2.0@jar' libraries 'com.beust:jcommander:1.30@jar' - libraries 'io.github.cruciblemc:launchwrapper:1.13@jar' libraries 'com.typesafe.akka:akka-actor_2.11:2.3.3' libraries 'com.typesafe:config:1.2.1' libraries 'org.scala-lang:scala-actors-migration_2.11:1.1.0' @@ -135,7 +134,6 @@ dependencies { libraries 'org.xerial:sqlite-jdbc:3.7.2' libraries 'mysql:mysql-connector-java:5.1.14' libraries 'javax.persistence:persistence-api:1.0.2' - libraries 'pw.prok:KImagine:0.2.0@jar' libraries 'org.apache.httpcomponents:httpclient:4.4.1' libraries 'net.openhft:affinity:3.0.1' libraries 'org.fusesource.jansi:jansi:1.11' diff --git a/patches/cpw/mods/fml/common/patcher/ClassPatchManager.java.patch b/patches/cpw/mods/fml/common/patcher/ClassPatchManager.java.patch deleted file mode 100644 index eb3c6262..00000000 --- a/patches/cpw/mods/fml/common/patcher/ClassPatchManager.java.patch +++ /dev/null @@ -1,57 +0,0 @@ ---- ../src-base/minecraft/cpw/mods/fml/common/patcher/ClassPatchManager.java -+++ ../src-work/minecraft/cpw/mods/fml/common/patcher/ClassPatchManager.java -@@ -11,14 +11,15 @@ - import java.util.jar.JarEntry; - import java.util.jar.JarInputStream; - import java.util.jar.JarOutputStream; --import java.util.jar.Pack200; - import java.util.regex.Pattern; - -+import org.apache.commons.compress.harmony.unpack200.Archive; - import org.apache.logging.log4j.Level; - - import net.minecraft.launchwrapper.LaunchClassLoader; - --import LZMA.LzmaInputStream; -+import org.apache.commons.compress.harmony.unpack200.Pack200UnpackerAdapter; -+import org.apache.logging.log4j.Level; - - import com.google.common.base.Joiner; - import com.google.common.base.Throwables; -@@ -30,11 +31,13 @@ - import com.google.common.io.ByteStreams; - import com.google.common.io.Files; - -+import LZMA.LzmaInputStream; - import cpw.mods.fml.relauncher.FMLRelaunchLog; - import cpw.mods.fml.relauncher.Side; - import cpw.mods.fml.repackage.com.nothome.delta.GDiffPatcher; - - public class ClassPatchManager { -+ - public static final ClassPatchManager INSTANCE = new ClassPatchManager(); - - public static final boolean dumpPatched = Boolean.parseBoolean(System.getProperty("fml.dumpPatchedClasses", "false")); -@@ -155,10 +158,20 @@ - FMLRelaunchLog.log(Level.ERROR, "The binary patch set is missing. Either you are in a development environment, or things are not going to work!"); - return; - } -- LzmaInputStream binpatchesDecompressed = new LzmaInputStream(binpatchesCompressed); -+ // Crucible start - apply lwjgl3ify patch -+ // LzmaInputStream binpatchesDecompressed = new LzmaInputStream(binpatchesCompressed); -+ LzmaInputStream binpatchesDecompressedLzma = new LzmaInputStream(binpatchesCompressed); -+ // The Apache pack200 stream chokes on a lzmainputstream for some reason -+ byte[] decompressed = ByteStreams.toByteArray(binpatchesDecompressedLzma); -+ binpatchesDecompressedLzma.close(); -+ ByteArrayInputStream binpatchesDecompressed = new ByteArrayInputStream(decompressed); - ByteArrayOutputStream jarBytes = new ByteArrayOutputStream(); - JarOutputStream jos = new JarOutputStream(jarBytes); -- Pack200.newUnpacker().unpack(binpatchesDecompressed, jos); -+ -+ //Pack200.newUnpacker().unpack(binpatchesDecompressed, jos); -+ new Pack200UnpackerAdapter().unpack(binpatchesDecompressed, jos); -+ // Crucible end -+ - jis = new JarInputStream(new ByteArrayInputStream(jarBytes.toByteArray())); - } - catch (Exception e) diff --git a/patches/cpw/mods/fml/common/registry/ObjectHolderRegistry.java.patch b/patches/cpw/mods/fml/common/registry/ObjectHolderRegistry.java.patch deleted file mode 100644 index 23de36ff..00000000 --- a/patches/cpw/mods/fml/common/registry/ObjectHolderRegistry.java.patch +++ /dev/null @@ -1,22 +0,0 @@ ---- ../src-base/minecraft/cpw/mods/fml/common/registry/ObjectHolderRegistry.java -+++ ../src-work/minecraft/cpw/mods/fml/common/registry/ObjectHolderRegistry.java -@@ -12,6 +12,7 @@ - import cpw.mods.fml.common.discovery.ASMDataTable; - import cpw.mods.fml.common.discovery.ASMDataTable.ASMData; - import cpw.mods.fml.common.registry.GameRegistry.ObjectHolder; -+import me.eigenraven.lwjgl3ify.WasFinalObjectHolder; - - /** - * Internal registry for tracking {@link ObjectHolder} references -@@ -111,6 +112,11 @@ - for (Field f : clazz.getFields()) - { - int mods = f.getModifiers(); -+ // Crucible start - lwjgl3ify patch -+ if (f.isAnnotationPresent(WasFinalObjectHolder.class)) { -+ mods |= Modifier.FINAL; -+ } -+ // Crucible end - boolean isMatch = Modifier.isPublic(mods) && Modifier.isStatic(mods) && Modifier.isFinal(mods); - if (!isMatch || f.isAnnotationPresent(ObjectHolder.class)) - { diff --git a/patches/cpw/mods/fml/relauncher/CoreModManager.java.patch b/patches/cpw/mods/fml/relauncher/CoreModManager.java.patch index 496115d1..43a48556 100644 --- a/patches/cpw/mods/fml/relauncher/CoreModManager.java.patch +++ b/patches/cpw/mods/fml/relauncher/CoreModManager.java.patch @@ -29,7 +29,7 @@ import net.minecraft.launchwrapper.ITweaker; import net.minecraft.launchwrapper.Launch; import net.minecraft.launchwrapper.LaunchClassLoader; -@@ -57,10 +61,11 @@ +@@ -57,6 +61,7 @@ import cpw.mods.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions; public class CoreModManager { @@ -37,11 +37,6 @@ private static final Attributes.Name COREMODCONTAINSFMLMOD = new Attributes.Name("FMLCorePluginContainsFMLMod"); private static final Attributes.Name MODTYPE = new Attributes.Name("ModType"); private static final Attributes.Name MODSIDE = new Attributes.Name("ModSide"); -- private static String[] rootPlugins = { "cpw.mods.fml.relauncher.FMLCorePlugin", "net.minecraftforge.classloading.FMLForgePlugin" }; -+ private static String[] rootPlugins = { "cpw.mods.fml.relauncher.FMLCorePlugin", "net.minecraftforge.classloading.FMLForgePlugin", "pw.prok.imagine.ImagineLoadingPlugin" }; - private static List loadedCoremods = Lists.newArrayList(); - private static List loadPlugins; - private static boolean deobfuscatedEnvironment; @@ -70,6 +75,7 @@ private static List accessTransformers = Lists.newArrayList(); diff --git a/patches/net/minecraft/world/WorldType.java.patch b/patches/net/minecraft/world/WorldType.java.patch index 3fcd4197..6b94f495 100644 --- a/patches/net/minecraft/world/WorldType.java.patch +++ b/patches/net/minecraft/world/WorldType.java.patch @@ -4,7 +4,7 @@ import net.minecraft.world.gen.layer.GenLayerZoom; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; -+import net.minecraftforge.common.util.EnumHelper; // Cauldron ++import io.github.crucible.util.BukkitEnumHelper; public class WorldType { @@ -15,7 +15,7 @@ + // Cauldron start - add worldtype for bukkit if it does not already exist + if (org.bukkit.WorldType.getByName(p_i1960_2_) == null) + { -+ EnumHelper.addBukkitWorldType(p_i1960_2_); ++ BukkitEnumHelper.addBukkitWorldType(p_i1960_2_); + } + // Cauldron end } diff --git a/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch b/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch index ac772a00..a80299e7 100644 --- a/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch +++ b/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch @@ -8,21 +8,27 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; -@@ -33,6 +34,13 @@ +@@ -26,6 +27,7 @@ + import net.minecraft.world.storage.IThreadedFileIO; + import net.minecraft.world.storage.ThreadedFileIOBase; + import net.minecraftforge.common.MinecraftForge; ++import io.github.crucible.util.BukkitEnumHelper; + import net.minecraftforge.event.world.ChunkDataEvent; + + import org.apache.logging.log4j.Level; +@@ -33,6 +35,11 @@ import org.apache.logging.log4j.Logger; import cpw.mods.fml.common.FMLLog; +// Cauldron start -+import java.util.Map; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.cauldron.CauldronUtils; -+import net.minecraftforge.common.util.EnumHelper; +import cpw.mods.fml.common.asm.transformers.SideTransformer; +// Cauldron end public class AnvilChunkLoader implements IChunkLoader, IThreadedFileIO { -@@ -41,6 +49,7 @@ +@@ -41,6 +48,7 @@ private Set pendingAnvilChunksCoordinates = new HashSet(); private Object syncLockObject = new Object(); public final File chunkSaveLocation; @@ -30,7 +36,7 @@ private static final String __OBFID = "CL_00000384"; public AnvilChunkLoader(File p_i2003_1_) -@@ -68,18 +77,22 @@ +@@ -68,18 +76,22 @@ } } @@ -54,7 +60,7 @@ return chunk; } -@@ -156,8 +169,8 @@ +@@ -156,8 +168,8 @@ if (!chunk.isAtLocation(p_75822_2_, p_75822_3_)) { logger.error("Chunk file at " + p_75822_2_ + "," + p_75822_3_ + " is in the wrong location; relocating. (Expected " + p_75822_2_ + ", " + p_75822_3_ + ", got " + chunk.xPosition + ", " + chunk.zPosition + ")"); @@ -65,7 +71,7 @@ // Have to move tile entities since we don't load them at this stage NBTTagList tileEntities = p_75822_4_.getCompoundTag("Level").getTagList("TileEntities", 10); -@@ -187,8 +200,18 @@ +@@ -187,8 +199,18 @@ public void saveChunk(World p_75816_1_, Chunk p_75816_2_) throws MinecraftException, IOException { @@ -85,7 +91,7 @@ try { NBTTagCompound nbttagcompound = new NBTTagCompound(); -@@ -230,7 +253,7 @@ +@@ -230,7 +252,7 @@ public boolean writeNextIO() { @@ -94,7 +100,7 @@ Object object = this.syncLockObject; synchronized (this.syncLockObject) -@@ -240,15 +263,15 @@ +@@ -240,15 +262,15 @@ return false; } @@ -114,7 +120,7 @@ } catch (Exception exception) { -@@ -259,7 +282,7 @@ +@@ -259,7 +281,7 @@ return true; } @@ -123,7 +129,7 @@ { DataOutputStream dataoutputstream = RegionFileCache.getChunkOutputStream(this.chunkSaveLocation, p_75821_1_.chunkCoordinate.chunkXPos, p_75821_1_.chunkCoordinate.chunkZPos); CompressedStreamTools.write(p_75821_1_.nbtTags, dataoutputstream); -@@ -307,19 +330,19 @@ +@@ -307,19 +329,19 @@ if (extendedblockstorage.getBlockMSBArray() != null) { @@ -148,7 +154,7 @@ } nbttaglist.appendTag(nbttagcompound1); -@@ -379,17 +402,16 @@ +@@ -379,17 +401,16 @@ } p_75820_3_.setTag("TileEntities", nbttaglist3); @@ -172,7 +178,7 @@ NBTTagCompound nbttagcompound2 = new NBTTagCompound(); nbttagcompound2.setInteger("i", Block.getIdFromBlock(nextticklistentry.func_151351_a())); nbttagcompound2.setInteger("x", nextticklistentry.xCoord); -@@ -401,7 +423,6 @@ +@@ -401,7 +422,6 @@ } p_75820_3_.setTag("TileTicks", nbttaglist1); @@ -180,7 +186,7 @@ } private Chunk readChunkFromNBT(World p_75823_1_, NBTTagCompound p_75823_2_) -@@ -455,6 +476,8 @@ +@@ -455,6 +475,8 @@ public void loadEntities(World p_75823_1_, NBTTagCompound p_75823_2_, Chunk chunk) { @@ -189,7 +195,7 @@ NBTTagList nbttaglist1 = p_75823_2_.getTagList("Entities", 10); if (nbttaglist1 != null) -@@ -468,24 +491,31 @@ +@@ -468,24 +490,31 @@ if (entity2 != null) { chunk.addEntity(entity2); @@ -230,7 +236,7 @@ NBTTagList nbttaglist2 = p_75823_2_.getTagList("TileEntities", 10); if (nbttaglist2 != null) -@@ -497,11 +527,35 @@ +@@ -497,11 +526,35 @@ if (tileentity != null) { @@ -252,7 +258,7 @@ + SideTransformer.allowInvalidSide = false; + } + // inject TE into InventoryType to support inventory events -+ EnumHelper.addInventoryType(tileentity); ++ BukkitEnumHelper.addInventoryType(tileentity); + this.checkedTileEntities.add(tileentity.getClass()); + } + // Cauldron end @@ -266,7 +272,7 @@ if (p_75823_2_.hasKey("TileTicks", 9)) { NBTTagList nbttaglist3 = p_75823_2_.getTagList("TileTicks", 10); -@@ -515,6 +569,7 @@ +@@ -515,6 +568,7 @@ } } } diff --git a/patches/net/minecraftforge/common/DimensionManager.java.patch b/patches/net/minecraftforge/common/DimensionManager.java.patch index 6844fd93..188edc5b 100644 --- a/patches/net/minecraftforge/common/DimensionManager.java.patch +++ b/patches/net/minecraftforge/common/DimensionManager.java.patch @@ -1,14 +1,23 @@ --- ../src-base/minecraft/net/minecraftforge/common/DimensionManager.java +++ ../src-work/minecraft/net/minecraftforge/common/DimensionManager.java -@@ -11,6 +11,7 @@ +@@ -11,6 +11,8 @@ import java.util.Map.Entry; import java.util.concurrent.ConcurrentMap; +import io.github.crucible.CrucibleConfigs; ++import io.github.crucible.util.BukkitEnumHelper; import org.apache.logging.log4j.Level; import com.google.common.collect.HashMultiset; -@@ -34,6 +35,15 @@ +@@ -21,7 +23,6 @@ + import cpw.mods.fml.common.FMLLog; + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.server.MinecraftServer; +-import net.minecraft.world.MinecraftException; + import net.minecraft.world.World; + import net.minecraft.world.WorldManager; + import net.minecraft.world.WorldProvider; +@@ -34,6 +35,14 @@ import net.minecraft.world.storage.ISaveHandler; import net.minecraft.world.storage.SaveHandler; import net.minecraftforge.event.world.WorldEvent; @@ -16,7 +25,6 @@ +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.world.chunk.storage.AnvilSaveHandler; +import net.minecraftforge.cauldron.CauldronUtils; -+import net.minecraftforge.common.util.EnumHelper; +import org.bukkit.World.Environment; +import org.bukkit.WorldCreator; +import org.bukkit.generator.ChunkGenerator; @@ -24,7 +32,7 @@ public class DimensionManager { -@@ -46,6 +56,11 @@ +@@ -46,6 +55,11 @@ private static BitSet dimensionMap = new BitSet(Long.SIZE << 4); private static ConcurrentMap weakWorldMap = new MapMaker().weakKeys().weakValues().makeMap(); private static Multiset leakedWorlds = HashMultiset.create(); @@ -36,7 +44,7 @@ public static boolean registerProviderType(int id, Class provider, boolean keepLoaded) { -@@ -53,7 +68,23 @@ +@@ -53,7 +67,23 @@ { return false; } @@ -60,7 +68,7 @@ spawnSettings.put(id, keepLoaded); return true; } -@@ -157,28 +188,33 @@ +@@ -157,28 +187,33 @@ public static Integer[] getIDs(boolean check) { @@ -108,7 +116,7 @@ return getIDs(); } public static Integer[] getIDs() -@@ -191,12 +227,23 @@ +@@ -191,12 +226,23 @@ if (world != null) { worlds.put(id, world); @@ -133,7 +141,7 @@ worlds.remove(id); MinecraftServer.getServer().worldTickTimes.remove(id); FMLLog.info("Unloading dimension %d", id); -@@ -224,6 +271,7 @@ +@@ -224,6 +270,7 @@ } public static void initDimension(int dim) { @@ -141,7 +149,7 @@ WorldServer overworld = getWorld(0); if (overworld == null) { -@@ -231,6 +279,12 @@ +@@ -231,6 +278,12 @@ } try { @@ -154,7 +162,7 @@ DimensionManager.getProviderType(dim); } catch (Exception e) -@@ -242,9 +296,63 @@ +@@ -242,9 +295,63 @@ ISaveHandler savehandler = overworld.getSaveHandler(); WorldSettings worldSettings = new WorldSettings(overworld.getWorldInfo()); @@ -219,7 +227,7 @@ if (!mcServer.isSinglePlayer()) { world.getWorldInfo().setGameType(mcServer.getGameType()); -@@ -253,6 +361,79 @@ +@@ -253,6 +360,79 @@ mcServer.func_147139_a(mcServer.func_147135_j()); } @@ -299,7 +307,7 @@ public static WorldServer getWorld(int id) { return worlds.get(id); -@@ -266,7 +447,7 @@ +@@ -266,7 +446,7 @@ public static boolean shouldLoadSpawn(int dim) { int id = getProviderType(dim); @@ -308,7 +316,7 @@ } static -@@ -306,7 +487,8 @@ +@@ -306,7 +486,8 @@ } public static void unloadWorld(int id) { @@ -318,7 +326,7 @@ } /* -@@ -315,26 +497,9 @@ +@@ -315,26 +496,9 @@ public static void unloadWorlds(Hashtable worldTickTimes) { for (int id : unloadQueue) { WorldServer w = worlds.get(id); @@ -347,7 +355,7 @@ } } unloadQueue.clear(); -@@ -425,4 +590,45 @@ +@@ -425,4 +589,45 @@ return null; } } @@ -359,7 +367,7 @@ + if (env == null) // Cauldron if environment not found, register one + { + providerName = providerName.replace("WorldProvider", ""); -+ env = EnumHelper.addBukkitEnvironment(dim, providerName.toUpperCase()); ++ env = BukkitEnumHelper.addBukkitEnvironment(dim, providerName.toUpperCase()); + Environment.registerEnvironment(env); + } + return env; diff --git a/patches/net/minecraftforge/common/util/EnumHelper.java.patch b/patches/net/minecraftforge/common/util/EnumHelper.java.patch deleted file mode 100644 index be667f59..00000000 --- a/patches/net/minecraftforge/common/util/EnumHelper.java.patch +++ /dev/null @@ -1,517 +0,0 @@ ---- ../src-base/minecraft/net/minecraftforge/common/util/EnumHelper.java -+++ ../src-work/minecraft/net/minecraftforge/common/util/EnumHelper.java -@@ -1,8 +1,11 @@ - package net.minecraftforge.common.util; - -+import java.lang.invoke.MethodHandle; -+import java.lang.invoke.MethodHandles; - import java.lang.reflect.*; - import java.util.*; - -+import com.google.common.base.Throwables; - import cpw.mods.fml.common.FMLLog; - import net.minecraft.block.BlockPressurePlate.Sensitivity; - import net.minecraft.block.material.Material; -@@ -21,186 +24,120 @@ - import net.minecraft.world.EnumSkyBlock; - import net.minecraft.world.gen.structure.StructureStrongholdPieces.Stronghold.Door; - import net.minecraftforge.classloading.FMLForgePlugin; -+// Cauldron start -+import cpw.mods.fml.relauncher.ReflectionHelper; -+import net.minecraft.inventory.IInventory; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.tileentity.TileEntity; - --public class EnumHelper --{ -- private static Object reflectionFactory = null; -+import org.apache.commons.lang3.ArrayUtils; -+import org.apache.logging.log4j.Level; -+import org.apache.logging.log4j.LogManager; -+import org.apache.logging.log4j.Logger; -+import org.bukkit.World; -+import org.bukkit.WorldType; -+import org.bukkit.block.Biome; -+import org.bukkit.entity.EntityType; -+import org.bukkit.event.inventory.InventoryType; -+// Cauldron end -+ -+public class EnumHelper { -+ private static final Logger logger = LogManager.getLogger(); -+ private static final Map>, Map>> enumConstants = new HashMap<>(); -+ private static Object reflectionFactory = null; - private static Method newConstructorAccessor = null; -- private static Method newInstance = null; -- private static Method newFieldAccessor = null; -- private static Method fieldAccessorSet = null; -- private static boolean isSetup = false; -+ private static Method newInstance = null; -+ private static Method newFieldAccessor = null; -+ private static Method fieldAccessorSet = null; -+ private static boolean isSetup = false; - -- //Some enums are decompiled with extra arguments, so lets check for that -+ // Some enums are decompiled with extra arguments, so lets check for that - @SuppressWarnings("rawtypes") -- private static Class[][] commonTypes = -- { -- {EnumAction.class}, -- {ArmorMaterial.class, int.class, int[].class, int.class}, -- {EnumArt.class, String.class, int.class, int.class, int.class, int.class}, -- {EnumCreatureAttribute.class}, -- {EnumCreatureType.class, Class.class, int.class, Material.class, boolean.class, boolean.class}, -- {Door.class}, -- {EnumEnchantmentType.class}, -- {EnumEntitySize.class}, -- {Sensitivity.class}, -- {MovingObjectType.class}, -- {EnumSkyBlock.class, int.class}, -- {EnumStatus.class}, -- {ToolMaterial.class, int.class, int.class, float.class, float.class, int.class}, -- {EnumRarity.class, EnumChatFormatting.class, String.class} -- }; -+ private static Class[][] commonTypes = {{EnumAction.class}, -+ {ArmorMaterial.class, int.class, int[].class, int.class}, -+ {EnumArt.class, String.class, int.class, int.class, int.class, int.class}, {EnumCreatureAttribute.class}, -+ {EnumCreatureType.class, Class.class, int.class, Material.class, boolean.class, boolean.class}, -+ {Door.class}, {EnumEnchantmentType.class}, {EnumEntitySize.class}, {Sensitivity.class}, -+ {MovingObjectType.class}, {EnumSkyBlock.class, int.class}, {EnumStatus.class}, -+ {ToolMaterial.class, int.class, int.class, float.class, float.class, int.class}, -+ {EnumRarity.class, EnumChatFormatting.class, String.class}}; - -- public static EnumAction addAction(String name) -- { -+ public static EnumAction addAction(String name) { - return addEnum(EnumAction.class, name); - } -- public static ArmorMaterial addArmorMaterial(String name, int durability, int[] reductionAmounts, int enchantability) -- { -+ -+ public static ArmorMaterial addArmorMaterial(String name, int durability, int[] reductionAmounts, int enchantability) { - return addEnum(ArmorMaterial.class, name, durability, reductionAmounts, enchantability); - } -- public static EnumArt addArt(String name, String tile, int sizeX, int sizeY, int offsetX, int offsetY) -- { -+ -+ public static EnumArt addArt(String name, String tile, int sizeX, int sizeY, int offsetX, int offsetY) { - return addEnum(EnumArt.class, name, tile, sizeX, sizeY, offsetX, offsetY); - } -- public static EnumCreatureAttribute addCreatureAttribute(String name) -- { -+ -+ public static EnumCreatureAttribute addCreatureAttribute(String name) { - return addEnum(EnumCreatureAttribute.class, name); - } -+ - @SuppressWarnings("rawtypes") -- public static EnumCreatureType addCreatureType(String name, Class typeClass, int maxNumber, Material material, boolean peaceful, boolean animal) -- { -+ public static EnumCreatureType addCreatureType(String name, Class typeClass, int maxNumber, Material material, boolean peaceful, boolean animal) { - return addEnum(EnumCreatureType.class, name, typeClass, maxNumber, material, peaceful, animal); - } -- public static Door addDoor(String name) -- { -+ -+ public static Door addDoor(String name) { - return addEnum(Door.class, name); - } -- public static EnumEnchantmentType addEnchantmentType(String name) -- { -+ -+ public static EnumEnchantmentType addEnchantmentType(String name) { - return addEnum(EnumEnchantmentType.class, name); - } -- public static EnumEntitySize addEntitySize(String name) -- { -+ -+ public static EnumEntitySize addEntitySize(String name) { - return addEnum(EnumEntitySize.class, name); - } -- public static Sensitivity addSensitivity(String name) -- { -+ -+ public static Sensitivity addSensitivity(String name) { - return addEnum(Sensitivity.class, name); - } -- public static MovingObjectType addMovingObjectType(String name) -- { -+ -+ public static MovingObjectType addMovingObjectType(String name) { - return addEnum(MovingObjectType.class, name); - } -- public static EnumSkyBlock addSkyBlock(String name, int lightValue) -- { -+ -+ public static EnumSkyBlock addSkyBlock(String name, int lightValue) { - return addEnum(EnumSkyBlock.class, name, lightValue); - } -- public static EnumStatus addStatus(String name) -- { -+ -+ public static EnumStatus addStatus(String name) { - return addEnum(EnumStatus.class, name); - } -- public static ToolMaterial addToolMaterial(String name, int harvestLevel, int maxUses, float efficiency, float damage, int enchantability) -- { -+ -+ public static ToolMaterial addToolMaterial(String name, int harvestLevel, int maxUses, float efficiency, float damage, int enchantability) { - return addEnum(ToolMaterial.class, name, harvestLevel, maxUses, efficiency, damage, enchantability); - } -- public static EnumRarity addRarity(String name, EnumChatFormatting color, String displayName) -- { -+ -+ public static EnumRarity addRarity(String name, EnumChatFormatting color, String displayName) { - return addEnum(EnumRarity.class, name, color, displayName); - } - -- private static void setup() -- { -- if (isSetup) -- { -- return; -+ public static void setFailsafeFieldValue(Field field, Object target, Object value) throws Exception { -+ try { -+ setFieldHandle.invokeExact(field, target, value); -+ } catch (Throwable e) { -+ Throwables.propagate(e); - } -- -- try -- { -- Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory"); -- reflectionFactory = getReflectionFactory.invoke(null); -- newConstructorAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newConstructorAccessor", Constructor.class); -- newInstance = Class.forName("sun.reflect.ConstructorAccessor").getDeclaredMethod("newInstance", Object[].class); -- newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", Field.class, boolean.class); -- fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", Object.class, Object.class); -- } -- catch (Exception e) -- { -- e.printStackTrace(); -- } -- -- isSetup = true; - } - -- /* -- * Everything below this is found at the site below, and updated to be able to compile in Eclipse/Java 1.6+ -- * Also modified for use in decompiled code. -- * Found at: http://niceideas.ch/roller2/badtrash/entry/java_create_enum_instances_dynamically -- */ -- private static Object getConstructorAccessor(Class enumClass, Class[] additionalParameterTypes) throws Exception -- { -- Class[] parameterTypes = new Class[additionalParameterTypes.length + 2]; -- parameterTypes[0] = String.class; -- parameterTypes[1] = int.class; -- System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length); -- return newConstructorAccessor.invoke(reflectionFactory, enumClass.getDeclaredConstructor(parameterTypes)); -- } -- -- private static < T extends Enum> T makeEnum(Class enumClass, String value, int ordinal, Class[] additionalTypes, Object[] additionalValues) throws Exception -- { -- Object[] parms = new Object[additionalValues.length + 2]; -- parms[0] = value; -- parms[1] = Integer.valueOf(ordinal); -- System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length); -- return enumClass.cast(newInstance.invoke(getConstructorAccessor(enumClass, additionalTypes), new Object[] {parms})); -- } -- -- public static void setFailsafeFieldValue(Field field, Object target, Object value) throws Exception -- { -- field.setAccessible(true); -- Field modifiersField = Field.class.getDeclaredField("modifiers"); -- modifiersField.setAccessible(true); -- modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); -- Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false); -- fieldAccessorSet.invoke(fieldAccessor, target, value); -- } -- -- private static void blankField(Class enumClass, String fieldName) throws Exception -- { -- for (Field field : Class.class.getDeclaredFields()) -- { -- if (field.getName().contains(fieldName)) -- { -- field.setAccessible(true); -- setFailsafeFieldValue(field, enumClass, null); -- break; -- } -- } -- } -- -- private static void cleanEnumCache(Class enumClass) throws Exception -- { -- blankField(enumClass, "enumConstantDirectory"); -- blankField(enumClass, "enumConstants"); -- } -- -- public static > T addEnum(Class enumType, String enumName, Object... paramValues) -- { -- setup(); -+ public static > T addEnum(Class enumType, String enumName, Object... paramValues) { - return addEnum(commonTypes, enumType, enumName, paramValues); - } - - @SuppressWarnings("rawtypes") -- public static > T addEnum(Class[][] map, Class enumType, String enumName, Object... paramValues) -- { -- for (Class[] lookup : map) -- { -- if (lookup[0] == enumType) -- { -+ public static > T addEnum(Class[][] map, Class enumType, String enumName, -+ Object... paramValues) { -+ for (Class[] lookup : map) { -+ if (lookup[0] == enumType) { - Class[] paramTypes = new Class[lookup.length - 1]; -- if (paramTypes.length > 0) -- { -+ if (paramTypes.length > 0) { - System.arraycopy(lookup, 1, paramTypes, 0, paramTypes.length); - } - return addEnum(enumType, enumName, paramTypes, paramValues); -@@ -210,81 +147,186 @@ - } - - @SuppressWarnings("unchecked") -- public static > T addEnum(Class enumType, String enumName, Class[] paramTypes, Object[] paramValues) -- { -- if (!isSetup) -- { -- setup(); -+ public static > T addEnum(Class enumType, String enumName, Class[] paramTypes, -+ Object[] paramValues) { -+ if (!extensibleEnumIface.isAssignableFrom(enumType)) { -+ throw new RuntimeException( -+ "Enum " + enumType.getName() + " was not made extensible, add it to Crucible.yml"); - } - -- Field valuesField = null; -- Field[] fields = enumType.getDeclaredFields(); -+ synchronized (enumConstants) { -+ Map> enumMap = enumConstants.computeIfAbsent(enumType, k -> new HashMap<>()); -+ if (enumMap.containsKey(enumName.toUpperCase())) { -+ // Inside the addEnum method -+ int suffix = 1; -+ String newName; -+ do { -+ newName = enumName + "$" + suffix; -+ suffix++; -+ } while (enumMap.containsKey(newName.toUpperCase())); - -- for (Field field : fields) -- { -- String name = field.getName(); -- if (name.equals("$VALUES") || name.equals("ENUM$VALUES")) //Added 'ENUM$VALUES' because Eclipse's internal compiler doesn't follow standards -- { -- valuesField = field; -- break; -+ // Log Enum Name Change -+ logger.info("Duplicate Enum found! Remapping {} to {}", enumName, newName); -+ enumName = newName; - } -- } -+ try { -+ paramTypes = ArrayUtils.add(paramTypes, 0, String.class); -+ paramValues = ArrayUtils.add(paramValues, 0, enumName); - -- int flags = (FMLForgePlugin.RUNTIME_DEOBF ? Modifier.PUBLIC : Modifier.PRIVATE) | Modifier.STATIC | Modifier.FINAL | 0x1000 /*SYNTHETIC*/; -- if (valuesField == null) -- { -- String valueType = String.format("[L%s;", enumType.getName().replace('.', '/')); -+ final Method creatorHandle = enumType.getMethod("dynamicCreate", paramTypes); -+ T newValue = (T) creatorHandle.invoke(null, paramValues); - -- for (Field field : fields) -- { -- if ((field.getModifiers() & flags) == flags && -- field.getType().getName().replace('.', '/').equals(valueType)) //Apparently some JVMs return .'s and some don't.. -- { -- valuesField = field; -- break; -- } -+ // Add Enum Name / Class to Map -+ enumMap.put(enumName.toUpperCase(), newValue); -+ return newValue; -+ } catch (Exception e) { -+ throw new RuntimeException("Failed to add enum constant: " + enumName, e); - } - } -+ } - -- if (valuesField == null) -- { -- FMLLog.severe("Could not find $VALUES field for enum: %s", enumType.getName()); -- FMLLog.severe("Runtime Deobf: %s", FMLForgePlugin.RUNTIME_DEOBF); -- FMLLog.severe("Flags: %s", String.format("%16s", Integer.toBinaryString(flags)).replace(' ', '0')); -- FMLLog.severe("Fields:"); -- for (Field field : fields) -- { -- String mods = String.format("%16s", Integer.toBinaryString(field.getModifiers())).replace(' ', '0'); -- FMLLog.severe(" %s %s: %s", mods, field.getName(), field.getType().getName()); -- } -- return null; -+ private static final Class extensibleEnumIface; -+ private static final MethodHandle setFieldHandle; -+ -+ static { -+ try { -+ extensibleEnumIface = Class.forName("me.eigenraven.lwjgl3ify.IExtensibleEnum"); -+ Class unsafeHacks = Class.forName("me.eigenraven.lwjgl3ify.UnsafeHacks"); -+ Method setFieldM = unsafeHacks.getMethod("setField", Field.class, Object.class, Object.class); -+ setFieldHandle = MethodHandles.publicLookup() -+ .unreflect(setFieldM); -+ } catch (ReflectiveOperationException e) { -+ throw new RuntimeException(e); - } -+ } - -- valuesField.setAccessible(true); -+ // Cauldron start -+ public static Biome addBukkitBiome(String name) -+ { -+ return (Biome)addEnum(Biome.class, name, new Class[0], new Object[0]); -+ } - -- try -- { -- T[] previousValues = (T[])valuesField.get(enumType); -- List values = new ArrayList(Arrays.asList(previousValues)); -- T newValue = (T)makeEnum(enumType, enumName, values.size(), paramTypes, paramValues); -- values.add(newValue); -- setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0))); -- cleanEnumCache(enumType); -+ public static World.Environment addBukkitEnvironment(int id, String name) -+ { -+ return (World.Environment)addEnum(World.Environment.class, name, new Class[] { Integer.TYPE }, new Object[] { Integer.valueOf(id) }); -+ } - -- return newValue; -- } -- catch (Exception e) -- { -- e.printStackTrace(); -- throw new RuntimeException(e.getMessage(), e); -- } -+ public static WorldType addBukkitWorldType(String name) -+ { -+ WorldType worldType = addEnum(WorldType.class, name, new Class [] { String.class }, new Object[] { name }); -+ Map BY_NAME = ReflectionHelper.getPrivateValue(WorldType.class, null, "BY_NAME"); -+ BY_NAME.put(name.toUpperCase(), worldType); -+ -+ return worldType; - } - -- static -+ public static EntityType addBukkitEntityType(String name, Class clazz, int typeId, boolean independent) { -+ String entityType = name.replace("-", "_").toUpperCase(); -+ EntityType bukkitType = addEnum(EntityType.class, entityType, new Class[] { String.class, Class.class, Integer.TYPE, Boolean.TYPE }, new Object[] { name, clazz, typeId, independent }); -+ -+ Map NAME_MAP = ReflectionHelper.getPrivateValue(EntityType.class, null, "NAME_MAP"); -+ Map ID_MAP = ReflectionHelper.getPrivateValue(EntityType.class, null, "ID_MAP"); -+ -+ NAME_MAP.put(name.toLowerCase(), bukkitType); -+ ID_MAP.put((short)typeId, bukkitType); -+ -+ -+ return bukkitType; -+ } -+ -+ public static InventoryType addInventoryType(TileEntity tileentity) - { -- if (!isSetup) -+ if (!IInventory.class.isAssignableFrom(tileentity.getClass())) return null; -+ String id = (String)TileEntity.classToNameMap.get(tileentity.getClass()); -+ -+ try - { -- setup(); -+ IInventory teInv = (IInventory)tileentity; -+ int size = teInv.getSizeInventory(); -+ return addEnum(org.bukkit.event.inventory.InventoryType.class, id, new Class[]{Integer.TYPE, String.class}, new Object[]{size, id}); - } -+ catch (Throwable e) -+ { -+ if (MinecraftServer.getServer().tileEntityConfig.enableTEInventoryWarning.getValue()) -+ { -+ logger.log(Level.WARN, "Could not create inventory type " + tileentity.getClass().getName() + " Exception: " + e.toString()); -+ logger.log(Level.WARN, "Could not determine default inventory size for type " + tileentity.getClass().getName() + " using size of 9"); -+ } -+ return addEnum(org.bukkit.event.inventory.InventoryType.class, id, new Class[]{Integer.TYPE, String.class}, new Object[]{9, id}); -+ } - } -+ -+// @SuppressWarnings("unchecked") -+// public static > T replaceEnum(Class enumType, String enumName, int ordinal, Class[] paramTypes, Object[] paramValues) -+// { -+// -+// Field valuesField = null; -+// Field[] fields = enumType.getDeclaredFields(); -+// -+// for (Field field : fields) -+// { -+// String name = field.getName(); -+// if (name.equals("$VALUES") || name.equals("ENUM$VALUES")) //Added 'ENUM$VALUES' because Eclipse's internal compiler doesn't follow standards -+// { -+// valuesField = field; -+// break; -+// } -+// } -+// -+// int flags = (FMLForgePlugin.RUNTIME_DEOBF ? Modifier.PUBLIC : Modifier.PRIVATE) | Modifier.STATIC | Modifier.FINAL | 0x1000 /*SYNTHETIC*/; -+// if (valuesField == null) -+// { -+// String valueType = String.format("[L%s;", enumType.getName().replace('.', '/')); -+// -+// for (Field field : fields) -+// { -+// if ((field.getModifiers() & flags) == flags && -+// field.getType().getName().replace('.', '/').equals(valueType)) //Apparently some JVMs return .'s and some don't.. -+// { -+// valuesField = field; -+// break; -+// } -+// } -+// } -+// -+// if (valuesField == null) -+// { -+// FMLLog.severe("Could not find $VALUES field for enum: %s", enumType.getName()); -+// FMLLog.severe("Runtime Deobf: %s", FMLForgePlugin.RUNTIME_DEOBF); -+// FMLLog.severe("Flags: %s", String.format("%16s", Integer.toBinaryString(flags)).replace(' ', '0')); -+// FMLLog.severe("Fields:"); -+// for (Field field : fields) -+// { -+// String mods = String.format("%16s", Integer.toBinaryString(field.getModifiers())).replace(' ', '0'); -+// FMLLog.severe(" %s %s: %s", mods, field.getName(), field.getType().getName()); -+// } -+// return null; -+// } -+// -+// valuesField.setAccessible(true); -+// try -+// { -+// Enum[] previousValues = (Enum[])(Enum[])valuesField.get(enumType); -+// Enum[] newValues = new Enum[previousValues.length]; -+// Enum newValue = null; -+// for (Enum enumValue : previousValues) -+// { -+// if (enumValue.ordinal() == ordinal) -+// { -+// newValue = makeEnum(enumType, enumName, ordinal, paramTypes, paramValues); -+// newValues[enumValue.ordinal()] = newValue; -+// } -+// else newValues[enumValue.ordinal()] = enumValue; -+// } -+// List values = new ArrayList(Arrays.asList(newValues)); -+// setFailsafeFieldValue(valuesField, null, values.toArray((Enum[])(Enum[])Array.newInstance(enumType, 0))); -+// return (T) newValue; -+// } -+// catch (Exception e) -+// { -+// e.printStackTrace(); -+// throw new RuntimeException(e.getMessage(), e); -+// } -+// } -+ // Cauldron end - } diff --git a/src/main/java/io/github/crucible/bootstrap/CrucibleCoremodHook.java b/src/main/java/io/github/crucible/bootstrap/CrucibleCoremodHook.java index 9621729b..85df6e8d 100644 --- a/src/main/java/io/github/crucible/bootstrap/CrucibleCoremodHook.java +++ b/src/main/java/io/github/crucible/bootstrap/CrucibleCoremodHook.java @@ -15,6 +15,8 @@ public static void coremodHandleLaunch(File mcDir, LaunchClassLoader classLoader } catch (ClassNotFoundException e) { throw new RuntimeException(e); } - Lwjgl3ifyGlue.doCoremodWork(classLoader); + classLoader.registerTransformer("io.github.crucible.patches.RecurrentComplexTransformer"); + classLoader.registerTransformer("io.github.crucible.patches.StreamsTransformer"); + classLoader.registerTransformer("thermos.ThermosClassTransformer"); } } diff --git a/src/main/java/io/github/crucible/bootstrap/CrucibleServerMainHook.java b/src/main/java/io/github/crucible/bootstrap/CrucibleServerMainHook.java index 7b9db192..d1958377 100644 --- a/src/main/java/io/github/crucible/bootstrap/CrucibleServerMainHook.java +++ b/src/main/java/io/github/crucible/bootstrap/CrucibleServerMainHook.java @@ -58,8 +58,6 @@ public static void relaunchMain(String[] args) throws Exception { System.setProperty((String) entry.getKey(), (String) entry.getValue()); } - Lwjgl3ifyGlue.checkJava(); - if(System.getProperty("java.class.loader") == null) { System.setProperty("rfb.skipClassLoaderCheck", "true"); } diff --git a/src/main/java/io/github/crucible/patches/RecurrentComplexTransformer.java b/src/main/java/io/github/crucible/patches/RecurrentComplexTransformer.java index 5a9ac637..93ffe5aa 100644 --- a/src/main/java/io/github/crucible/patches/RecurrentComplexTransformer.java +++ b/src/main/java/io/github/crucible/patches/RecurrentComplexTransformer.java @@ -1,41 +1,62 @@ package io.github.crucible.patches; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.MethodInsnNode; -import pw.prok.imagine.asm.ImagineASM; -import pw.prok.imagine.asm.Transformer; +import cpw.mods.fml.common.FMLLog; +import net.minecraft.launchwrapper.IClassTransformer; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.*; import static org.objectweb.asm.Opcodes.INVOKESTATIC; -@Transformer.RegisterTransformer -public class RecurrentComplexTransformer implements Transformer { +public class RecurrentComplexTransformer implements IClassTransformer { + private static final String TARGET_CLASS = + "ivorius.reccomplex.structures.generic.matchers.BiomeMatcher"; + @Override - public void transform(ImagineASM asm) { - if (asm.is("ivorius.reccomplex.structures.generic.matchers.BiomeMatcher")) { - System.out.println("[Crucible] Found ivorius.reccomplex.structures.generic.matchers.BiomeMatcher, trying to patch it!"); - InsnList instructions = asm.method("ofTypes", "([Lnet/minecraftforge/common/BiomeDictionary$Type;)Ljava/lang/String;").instructions(); - - AbstractInsnNode abstractInsnNode = instructions.getFirst(); - boolean appliedPatch = false; - while (abstractInsnNode != null) { - if (abstractInsnNode.getOpcode() == INVOKESTATIC) { - MethodInsnNode methodInsnNode = (MethodInsnNode) abstractInsnNode; - if ("joptsimple/internal/Strings".equals(methodInsnNode.owner) && - "join".equals(methodInsnNode.name) && - "(Ljava/util/List;Ljava/lang/String;)Ljava/lang/String;" - .equals(methodInsnNode.desc)) { - methodInsnNode.owner = "io/github/crucible/patches/AsmHooks"; - appliedPatch = true; - System.out.println("[Crucible] Patched joptsimple.internal.Strings#join() call!"); + public byte[] transform(String name, String transformedName, byte[] basicClass) { + if (!transformedName.equals(TARGET_CLASS)) { + return basicClass; + } + + FMLLog.info("[Crucible] Found %s, attempting ASM patch", TARGET_CLASS); + + ClassNode classNode = new ClassNode(); + new ClassReader(basicClass).accept(classNode, 0); + + boolean patched = false; + + for (MethodNode method : classNode.methods) { + if (!method.name.equals("ofTypes")) continue; + if (!method.desc.equals("([Lnet/minecraftforge/common/BiomeDictionary$Type;)Ljava/lang/String;")) + continue; + + InsnList insns = method.instructions; + + for (AbstractInsnNode insn = insns.getFirst(); insn != null; insn = insn.getNext()) { + if (insn.getOpcode() == INVOKESTATIC && insn instanceof MethodInsnNode) { + MethodInsnNode m = (MethodInsnNode) insn; + + if (m.owner.equals("joptsimple/internal/Strings") + && m.name.equals("join") + && m.desc.equals("(Ljava/util/List;Ljava/lang/String;)Ljava/lang/String;")) { + + m.owner = "io/github/crucible/patches/AsmHooks"; + patched = true; + + FMLLog.info("[Crucible] Patched Strings.join() call in BiomeMatcher"); + break; } } - abstractInsnNode = abstractInsnNode.getNext(); - } - if (!appliedPatch) { - System.out.println("[Crucible] RecurrentComplexTransformer: " + - "unable to find joptsimple.internal.Strings#join(), skipping it!"); } } + + if (!patched) { + FMLLog.warning("[Crucible] Failed to patch BiomeMatcher: join() call not found"); + return basicClass; + } + + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + classNode.accept(writer); + return writer.toByteArray(); } } diff --git a/src/main/java/io/github/crucible/patches/StreamsTransformer.java b/src/main/java/io/github/crucible/patches/StreamsTransformer.java index fb947bee..2c6f82b7 100644 --- a/src/main/java/io/github/crucible/patches/StreamsTransformer.java +++ b/src/main/java/io/github/crucible/patches/StreamsTransformer.java @@ -1,42 +1,59 @@ package io.github.crucible.patches; +import net.minecraft.launchwrapper.IClassTransformer; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.InsnNode; -import pw.prok.imagine.asm.ImagineASM; -import pw.prok.imagine.asm.Transformer; +import org.objectweb.asm.tree.*; import java.util.Iterator; -@Transformer.RegisterTransformer -public class StreamsTransformer implements Transformer { +public class StreamsTransformer implements IClassTransformer { + private static final String TARGET_CLASS = + "streams.world.gen.structure.RiverComponent$"; + @Override - public void transform(ImagineASM asm) { - if (asm.is("streams.world.gen.structure.RiverComponent$")) { - System.out.println("[Crucible] Found streams.world.gen.structure.RiverComponent$, trying to patch it!"); - InsnList instructions = asm.method("", "()V").instructions(); //We just need to replace a number - AbstractInsnNode toReplace = null; - Iterator i = instructions.iterator(); - while (i.hasNext()) { - AbstractInsnNode ins = i.next(); - if (ins.getOpcode() == Opcodes.ICONST_2) { - if (ins.getNext() instanceof FieldInsnNode) { - FieldInsnNode fieldAccess = (FieldInsnNode) ins.getNext(); - if (fieldAccess.name.contains("MinSourceBackWallHeight")) { - toReplace = fieldAccess.getPrevious(); - break; - } - } - } + public byte[] transform(String name, String transformedName, byte[] basicClass) { + if (!transformedName.equals(TARGET_CLASS)) { + return basicClass; + } + + System.out.println("[Crucible] Found " + TARGET_CLASS + ", patching…"); + + ClassNode classNode = new ClassNode(); + ClassReader reader = new ClassReader(basicClass); + reader.accept(classNode, 0); + + for (MethodNode method : classNode.methods) { + if ("".equals(method.name) && "()V".equals(method.desc)) { + patchConstructor(method); } - if (toReplace == null) { - System.out.println("[Crucible] Unable to find MinSourceBackWallHeight, skipping patch!"); - } else { - instructions.set(toReplace, new InsnNode(Opcodes.ICONST_0)); - System.out.println("[Crucible] Patched MinSourceBackWallHeight's previous opcode!"); + } + + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + classNode.accept(writer); + return writer.toByteArray(); + } + + private void patchConstructor(MethodNode method) { + InsnList insns = method.instructions; + + for (Iterator it = insns.iterator(); it.hasNext(); ) { + AbstractInsnNode insn = it.next(); + + if (insn.getOpcode() == Opcodes.ICONST_2 && + insn.getNext() instanceof FieldInsnNode) { + + FieldInsnNode field = (FieldInsnNode) insn.getNext(); + + if (field.name.contains("MinSourceBackWallHeight")) { + insns.set(insn, new InsnNode(Opcodes.ICONST_0)); + System.out.println("[Crucible] Patched MinSourceBackWallHeight"); + return; + } } } + + System.out.println("[Crucible] Failed to find MinSourceBackWallHeight"); } } diff --git a/src/main/java/io/github/crucible/util/BukkitEnumHelper.java b/src/main/java/io/github/crucible/util/BukkitEnumHelper.java new file mode 100644 index 00000000..b22be111 --- /dev/null +++ b/src/main/java/io/github/crucible/util/BukkitEnumHelper.java @@ -0,0 +1,76 @@ +package io.github.crucible.util; + +import cpw.mods.fml.relauncher.ReflectionHelper; +import net.minecraft.inventory.IInventory; +import net.minecraft.server.MinecraftServer; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.EnumHelper; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.World; +import org.bukkit.WorldType; +import org.bukkit.block.Biome; +import org.bukkit.entity.EntityType; +import org.bukkit.event.inventory.InventoryType; + +import java.util.Map; + +public class BukkitEnumHelper { + private static final Logger logger = LogManager.getLogger(); + // Cauldron start + public static Biome addBukkitBiome(String name) + { + return (Biome) EnumHelper.addEnum(Biome.class, name, new Class[0], new Object[0]); + } + + public static World.Environment addBukkitEnvironment(int id, String name) + { + return (World.Environment) EnumHelper.addEnum(World.Environment.class, name, new Class[] { Integer.TYPE }, new Object[] { Integer.valueOf(id) }); + } + + public static WorldType addBukkitWorldType(String name) + { + WorldType worldType = EnumHelper.addEnum(WorldType.class, name, new Class [] { String.class }, new Object[] { name }); + Map BY_NAME = ReflectionHelper.getPrivateValue(WorldType.class, null, "BY_NAME"); + BY_NAME.put(name.toUpperCase(), worldType); + + return worldType; + } + + public static EntityType addBukkitEntityType(String name, Class clazz, int typeId, boolean independent) { + String entityType = name.replace("-", "_").toUpperCase(); + EntityType bukkitType = EnumHelper.addEnum(EntityType.class, entityType, new Class[] { String.class, Class.class, Integer.TYPE, Boolean.TYPE }, new Object[] { name, clazz, typeId, independent }); + + Map NAME_MAP = ReflectionHelper.getPrivateValue(EntityType.class, null, "NAME_MAP"); + Map ID_MAP = ReflectionHelper.getPrivateValue(EntityType.class, null, "ID_MAP"); + + NAME_MAP.put(name.toLowerCase(), bukkitType); + ID_MAP.put((short)typeId, bukkitType); + + + return bukkitType; + } + + public static InventoryType addInventoryType(TileEntity tileentity) + { + if (!IInventory.class.isAssignableFrom(tileentity.getClass())) return null; + String id = (String)TileEntity.classToNameMap.get(tileentity.getClass()); + + try + { + IInventory teInv = (IInventory)tileentity; + int size = teInv.getSizeInventory(); + return EnumHelper.addEnum(InventoryType.class, id, new Class[]{Integer.TYPE, String.class}, new Object[]{size, id}); + } + catch (Throwable e) + { + if (MinecraftServer.getServer().tileEntityConfig.enableTEInventoryWarning.getValue()) + { + logger.log(Level.WARN, "Could not create inventory type " + tileentity.getClass().getName() + " Exception: " + e.toString()); + logger.log(Level.WARN, "Could not determine default inventory size for type " + tileentity.getClass().getName() + " using size of 9"); + } + return EnumHelper.addEnum(InventoryType.class, id, new Class[]{Integer.TYPE, String.class}, new Object[]{9, id}); + } + } +} diff --git a/src/main/java/me/eigenraven/lwjgl3ify/IExtensibleEnum.java b/src/main/java/me/eigenraven/lwjgl3ify/IExtensibleEnum.java deleted file mode 100644 index e1a55261..00000000 --- a/src/main/java/me/eigenraven/lwjgl3ify/IExtensibleEnum.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors SPDX-License-Identifier: LGPL-2.1-only - */ -package me.eigenraven.lwjgl3ify; - -/** - * To be implemented on vanilla enums that should be enhanced with ASM to be extensible. If this is implemented on a - * class, the class must define a static method called "create" which takes a String (enum name), and the rest of the - * parameters matching a constructor. - *

- * For example, an enum with the constructor {@code MyEnum(Object foo)} would require the method: - * - *

- *
- * public static MyEnum create(String name, Object foo) {
- *     throw new IllegalStateException("Enum not extended");
- * }
- * 
- * - * The method contents will be replaced with ASM at runtime. Multiple {@code create} methods can be - * defined as long as each matches a constructor. - */ -public interface IExtensibleEnum { - - /** - * Called by generated factory code to do any post-constructor setup required by the enum. Should not be called - * manually. - */ - @Deprecated - default void init() {} -} \ No newline at end of file diff --git a/src/main/java/me/eigenraven/lwjgl3ify/UnsafeHacks.java b/src/main/java/me/eigenraven/lwjgl3ify/UnsafeHacks.java deleted file mode 100644 index ac8a6711..00000000 --- a/src/main/java/me/eigenraven/lwjgl3ify/UnsafeHacks.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Minecraft Forge Copyright (c) 2016-2019. This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the Free Software Foundation version 2.1 of - * the License. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. You should have received a copy of the GNU Lesser General Public License along with this - * library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ -package me.eigenraven.lwjgl3ify; - -import java.lang.reflect.Field; -import java.util.Optional; - -@SuppressWarnings({ "restriction", "sunapi" }) -public class UnsafeHacks { - - private static final sun.misc.Unsafe UNSAFE; - - static { - try { - final Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - UNSAFE = (sun.misc.Unsafe) theUnsafe.get(null); - } catch (IllegalAccessException | NoSuchFieldException e) { - throw new RuntimeException("BARF!", e); - } - } - - @SuppressWarnings("unchecked") - public static T newInstance(Class clazz) { - try { - return (T) UNSAFE.allocateInstance(clazz); - } catch (InstantiationException e) { - throw new RuntimeException(e); - } - } - - @SuppressWarnings("unchecked") - public static T getField(Field field, Object object) { - if (object == null) { - long offset = UNSAFE.staticFieldOffset(field); - Object base = UNSAFE.staticFieldBase(field); - return (T) UNSAFE.getObject(base, offset); - } else { - long offset = UNSAFE.objectFieldOffset(field); - return (T) UNSAFE.getObject(object, offset); - } - } - - public static void setField(Field data, Object object, Object value) { - if (object == null) { - long offset = UNSAFE.staticFieldOffset(data); - Object base = UNSAFE.staticFieldBase(data); - UNSAFE.putObject(base, offset, value); - } else { - long offset = UNSAFE.objectFieldOffset(data); - UNSAFE.putObject(object, offset, value); - } - } - - public static int getIntField(Field field, Object object) { - if (object == null) { - long offset = UNSAFE.staticFieldOffset(field); - Object base = UNSAFE.staticFieldBase(field); - return UNSAFE.getInt(base, offset); - } else { - long offset = UNSAFE.objectFieldOffset(field); - return UNSAFE.getInt(object, offset); - } - } - - public static void setIntField(Field data, Object object, int value) { - if (object == null) { - long offset = UNSAFE.staticFieldOffset(data); - Object base = UNSAFE.staticFieldBase(data); - UNSAFE.putInt(base, offset, value); - } else { - long offset = UNSAFE.objectFieldOffset(data); - UNSAFE.putInt(object, offset, value); - } - } - - // Make sure we don't crash if any future versions change field names - private static Optional findField(Class clazz, String name) { - for (Field f : clazz.getDeclaredFields()) { - if (f.getName() - .equals(name)) { - return Optional.of(f); - } - } - return Optional.empty(); - } - - public static void cleanEnumCache(Class> enumClass) throws Exception { - findField(Class.class, "enumConstantDirectory").ifPresent(f -> setField(f, enumClass, null)); - findField(Class.class, "enumConstants").ifPresent(f -> setField(f, enumClass, null)); - } -} \ No newline at end of file diff --git a/src/main/java/me/eigenraven/lwjgl3ify/WasFinalObjectHolder.java b/src/main/java/me/eigenraven/lwjgl3ify/WasFinalObjectHolder.java deleted file mode 100644 index f91c600c..00000000 --- a/src/main/java/me/eigenraven/lwjgl3ify/WasFinalObjectHolder.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.eigenraven.lwjgl3ify; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface WasFinalObjectHolder {} \ No newline at end of file diff --git a/src/main/java/me/eigenraven/lwjgl3ify/api/ConfigUtils.java b/src/main/java/me/eigenraven/lwjgl3ify/api/ConfigUtils.java deleted file mode 100644 index 228854a7..00000000 --- a/src/main/java/me/eigenraven/lwjgl3ify/api/ConfigUtils.java +++ /dev/null @@ -1,34 +0,0 @@ -package me.eigenraven.lwjgl3ify.api; - -import io.github.crucible.CrucibleConfigs; -import org.apache.logging.log4j.Logger; - -import java.util.HashSet; -import java.util.Set; - -/** - * Proxy everything to Crucible - * TODO: perhaps we need to ensure this class is loaded by us? - */ -public class ConfigUtils { - - public ConfigUtils(Logger logger) { - // NO-OP - } - - public boolean isLwjgl3ifyLoaded() { - return true; - } - - public Set getExtensibleEnums() { - return new HashSet<>(CrucibleConfigs.configs.lwjgl3ify_extensibleEnums); - } - - public void addExtensibleEnum(String className) { - CrucibleConfigs.configs.lwjgl3ify_extensibleEnums.add(className); - } - - public boolean isConfigLoaded() { - return true; - } -} diff --git a/src/main/java/me/eigenraven/lwjgl3ify/api/Lwjgl3Aware.java b/src/main/java/me/eigenraven/lwjgl3ify/api/Lwjgl3Aware.java deleted file mode 100644 index 28760fbf..00000000 --- a/src/main/java/me/eigenraven/lwjgl3ify/api/Lwjgl3Aware.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.eigenraven.lwjgl3ify.api; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Mark a class to not be transformed for lwjgl3 compatibility - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface Lwjgl3Aware {} \ No newline at end of file diff --git a/src/main/java/me/eigenraven/lwjgl3ify/api/MakeEnumExtensible.java b/src/main/java/me/eigenraven/lwjgl3ify/api/MakeEnumExtensible.java deleted file mode 100644 index bc1346b0..00000000 --- a/src/main/java/me/eigenraven/lwjgl3ify/api/MakeEnumExtensible.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.eigenraven.lwjgl3ify.api; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Mark an enum for an automatic IExtensibleEnum implementation. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface MakeEnumExtensible {} \ No newline at end of file diff --git a/src/main/java/me/eigenraven/lwjgl3ify/core/ExtensibleEnumTransformerHelper.java b/src/main/java/me/eigenraven/lwjgl3ify/core/ExtensibleEnumTransformerHelper.java deleted file mode 100644 index f98312e5..00000000 --- a/src/main/java/me/eigenraven/lwjgl3ify/core/ExtensibleEnumTransformerHelper.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors SPDX-License-Identifier: LGPL-2.1-only - */ -package me.eigenraven.lwjgl3ify.core; - -import java.util.List; -import java.util.stream.Collectors; - -import io.github.crucible.CrucibleModContainer; -import org.apache.commons.lang3.ArrayUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.objectweb.asm.Label; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.commons.InstructionAdapter; -import org.objectweb.asm.tree.AnnotationNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldNode; -import org.objectweb.asm.tree.MethodNode; - -import me.eigenraven.lwjgl3ify.IExtensibleEnum; -import me.eigenraven.lwjgl3ify.api.MakeEnumExtensible; - -public class ExtensibleEnumTransformerHelper { - - private final Logger LOGGER = LogManager.getLogger("lwjgl3ify"); - private final Type STRING = Type.getType(String.class); - private final Type ENUM = Type.getType(Enum.class); - public final Type MARKER_IFACE = Type.getType(IExtensibleEnum.class); - public final Type MARKER_ANNOTATION = Type.getType(MakeEnumExtensible.class); - private final Type ARRAY_UTILS = Type.getType("Lorg/apache/commons/lang3/ArrayUtils;"); // Don't directly reference - // this to prevent class - // loading. - private final String ADD_DESC = Type - .getMethodDescriptor(Type.getType(Object[].class), Type.getType(Object[].class), Type.getType(Object.class)); - private final Type UNSAFE_HACKS = Type.getType("Lme/eigenraven/lwjgl3ify/UnsafeHacks;"); // Again, not direct - // reference to prevent - // class loading. - private final String CLEAN_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class)); - private final String NAME_DESC = Type.getMethodDescriptor(STRING); - private final String EQUALS_DESC = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, STRING); - public static final String CREATE_METHOD_NAME = "dynamicCreate"; - - /** - * @return Were changes made? - */ - public boolean processClassWithFlags(final ClassNode classNode, final Type classType) { - if ((classNode.access & Opcodes.ACC_ENUM) == 0) return false; - - Type array = Type.getType("[" + classType.getDescriptor()); - final int flags = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC; - - FieldNode values = classNode.fields.stream() - .filter(f -> f.desc.contentEquals(array.getDescriptor()) && ((f.access & flags) == flags)) - .findFirst() - .orElse(null); - - boolean process = false; - if (classNode.interfaces.contains(MARKER_IFACE.getInternalName())) { - process = true; - } else if (classNode.visibleAnnotations != null && !classNode.visibleAnnotations.isEmpty()) { - for (AnnotationNode annotation : classNode.visibleAnnotations) { - if (annotation.desc.equals(MARKER_ANNOTATION.getDescriptor())) { - process = true; - } - } - } - if (!process) { - return false; - } - - List constructors = classNode.methods.stream() - .filter(m -> m.name.equals("")) - .collect(Collectors.toList()); - - // Static methods named "create" with first argument as a string - List candidates = constructors.stream() - .map(ctor -> { - final String[] exceptions = ctor.exceptions == null ? null : ctor.exceptions.toArray(new String[0]); - final Type ctorDesc = Type.getMethodType(ctor.desc); - final Type creatorDesc = Type - .getMethodType(classType, ArrayUtils.remove(ctorDesc.getArgumentTypes(), 1)); - final MethodNode creator = new MethodNode( - ctor.access, - CREATE_METHOD_NAME, - creatorDesc.getDescriptor(), - null, - exceptions); - creator.access = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC; - return creator; - }) - .collect(Collectors.toList()); - - if (candidates.isEmpty()) { - throw new IllegalStateException( - "IExtensibleEnum has no candidate factory methods: " + classType.getClassName()); - } - - classNode.methods.addAll(candidates); - - candidates.forEach(mtd -> { - Type[] args = Type.getArgumentTypes(mtd.desc); - if (args.length == 0 || !args[0].equals(STRING)) { - if (LOGGER.isErrorEnabled()) { - String sb = "Enum has create method without String as first parameter:\n" + " Enum: " - + classType.getDescriptor() - + "\n" - + " Target: " - + mtd.name - + mtd.desc - + "\n"; - LOGGER.error(sb); - } - throw new IllegalStateException( - "Enum has create method without String as first parameter: " + mtd.name + mtd.desc); - } - - Type ret = Type.getReturnType(mtd.desc); - if (!ret.equals(classType)) { - if (LOGGER.isErrorEnabled()) { - String sb = "Enum has create method with incorrect return type:\n" + " Enum: " - + classType.getDescriptor() - + "\n" - + " Target: " - + mtd.name - + mtd.desc - + "\n" - + " Found: " - + ret.getClassName() - + ", Expected: " - + classType.getClassName(); - LOGGER.error(sb); - } - throw new IllegalStateException( - "Enum has create method with incorrect return type: " + mtd.name + mtd.desc); - } - - Type[] ctrArgs = new Type[args.length + 1]; - ctrArgs[0] = STRING; - ctrArgs[1] = Type.INT_TYPE; - for (int x = 1; x < args.length; x++) ctrArgs[1 + x] = args[x]; - - String desc = Type.getMethodDescriptor(Type.VOID_TYPE, ctrArgs); - - MethodNode ctr = classNode.methods.stream() - .filter(m -> m.name.equals("") && m.desc.equals(desc)) - .findFirst() - .orElse(null); - if (ctr == null) { - if (LOGGER.isErrorEnabled()) { - StringBuilder sb = new StringBuilder(); - sb.append("Enum has create method with no matching constructor:\n"); - sb.append(" Enum: ") - .append(classType.getDescriptor()) - .append("\n"); - sb.append(" Candidate: ") - .append(mtd.desc) - .append("\n"); - sb.append(" Target: ") - .append(desc) - .append("\n"); - classNode.methods.stream() - .filter(m -> m.name.equals("")) - .forEach( - m -> sb.append(" : ") - .append(m.desc) - .append("\n")); - LOGGER.error(sb.toString()); - } - throw new IllegalStateException("Enum has create method with no matching constructor: " + desc); - } - - if (values == null) { - if (LOGGER.isErrorEnabled()) { - StringBuilder sb = new StringBuilder(); - sb.append("Enum has create method but we could not find $VALUES. Found:\n"); - classNode.fields.stream() - .filter(f -> (f.access & Opcodes.ACC_STATIC) != 0) - .forEach( - m -> sb.append(" ") - .append(m.name) - .append(" ") - .append(m.desc) - .append("\n")); - LOGGER.error(sb.toString()); - } - throw new IllegalStateException("Enum has create method but we could not find $VALUES"); - } - - values.access &= values.access & ~Opcodes.ACC_FINAL; // Strip the final so JITer doesn't inline things. - - mtd.access |= Opcodes.ACC_SYNCHRONIZED; - mtd.instructions.clear(); - mtd.localVariables.clear(); - if (mtd.tryCatchBlocks != null) { - mtd.tryCatchBlocks.clear(); - } - if (mtd.visibleLocalVariableAnnotations != null) { - mtd.visibleLocalVariableAnnotations.clear(); - } - if (mtd.invisibleLocalVariableAnnotations != null) { - mtd.invisibleLocalVariableAnnotations.clear(); - } - InstructionAdapter ins = new InstructionAdapter(mtd); - - int vars = 0; - for (Type arg : args) vars += arg.getSize(); - - { - vars += 1; // int x - Label for_start = new Label(); - Label for_condition = new Label(); - Label for_inc = new Label(); - - ins.iconst(0); - ins.store(vars, Type.INT_TYPE); - ins.goTo(for_condition); - // if (!VALUES[x].name().equalsIgnoreCase(name)) goto for_inc - ins.mark(for_start); - ins.getstatic(classType.getInternalName(), values.name, values.desc); - ins.load(vars, Type.INT_TYPE); - ins.aload(array); - ins.invokevirtual(ENUM.getInternalName(), "name", NAME_DESC, false); - ins.load(0, STRING); - ins.invokevirtual(STRING.getInternalName(), "equalsIgnoreCase", EQUALS_DESC, false); - ins.ifeq(for_inc); - // return VALUES[x]; - ins.getstatic(classType.getInternalName(), values.name, values.desc); - ins.load(vars, Type.INT_TYPE); - ins.aload(array); - ins.areturn(classType); - // x++ - ins.mark(for_inc); - ins.iinc(vars, 1); - // if (x < VALUES.length) goto for_start - ins.mark(for_condition); - ins.load(vars, Type.INT_TYPE); - ins.getstatic(classType.getInternalName(), values.name, values.desc); - ins.arraylength(); - ins.ificmplt(for_start); - } - - { - vars += 1; // enum ret; - // ret = new ThisType(name, VALUES.length, args..) - ins.anew(classType); - ins.dup(); - ins.load(0, STRING); - ins.getstatic(classType.getInternalName(), values.name, values.desc); - ins.arraylength(); - int idx = 1; - for (int x = 1; x < args.length; x++) { - ins.load(idx, args[x]); - idx += args[x].getSize(); - } - ins.invokespecial(classType.getInternalName(), "", desc, false); - ins.store(vars, classType); - // VALUES = ArrayUtils.add(VALUES, ret) - ins.getstatic(classType.getInternalName(), values.name, values.desc); - ins.load(vars, classType); - ins.invokestatic(ARRAY_UTILS.getInternalName(), "add", ADD_DESC, false); - ins.checkcast(array); - ins.putstatic(classType.getInternalName(), values.name, values.desc); - // EnumHelper.cleanEnumCache(ThisType.class) - ins.visitLdcInsn(classType); - ins.invokestatic(UNSAFE_HACKS.getInternalName(), "cleanEnumCache", CLEAN_DESC, false); - // init ret - ins.load(vars, classType); - ins.invokeinterface(MARKER_IFACE.getInternalName(), "init", "()V"); - // return ret - ins.load(vars, classType); - ins.areturn(classType); - } - }); - return true; - } -} \ No newline at end of file diff --git a/src/main/java/me/eigenraven/lwjgl3ify/core/FixConstantPoolInterfaceMethodRefHelper.java b/src/main/java/me/eigenraven/lwjgl3ify/core/FixConstantPoolInterfaceMethodRefHelper.java deleted file mode 100644 index 29b0f363..00000000 --- a/src/main/java/me/eigenraven/lwjgl3ify/core/FixConstantPoolInterfaceMethodRefHelper.java +++ /dev/null @@ -1,95 +0,0 @@ -package me.eigenraven.lwjgl3ify.core; - -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.objectweb.asm.Handle; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InvokeDynamicInsnNode; -import org.objectweb.asm.tree.MethodNode; - -/** - * Fixes a compilation bug of the java 8 compiler leading to the following exception at runtime: - * cpw.mods.fml.common.LoaderException: java.lang.IncompatibleClassChangeError: Inconsistent constant pool data in - * classfile for class com/gtnewhorizon/structurelib/alignment/IAlignmentLimits. Method 'boolean - * lambda$static$0(net.minecraftforge.common.util.ForgeDirection, - * com.gtnewhorizon.structurelib.alignment.enumerable.Rotation, - * com.gtnewhorizon.structurelib.alignment.enumerable.Flip)' at index 77 is CONSTANT_MethodRef and should be - * CONSTANT_InterfaceMethodRef - */ -public class FixConstantPoolInterfaceMethodRefHelper { - private final Logger LOGGER = LogManager.getLogger("lwjgl3ify"); - - public boolean transform(ClassNode node) { - if (System.getProperty("java.specification.version", "1.8") - .trim() - .startsWith("1.8")) { - return false; - } - final boolean iAmAnInterface = ((node.access & Opcodes.ACC_INTERFACE) != 0); - boolean changesMade = false; - final String internalClassName = node.name; - if (node.methods != null) { - for (MethodNode method : node.methods) { - if (method.instructions != null) { - for (AbstractInsnNode insn : method.instructions) { - changesMade |= validateInstruction(internalClassName, iAmAnInterface, insn); - } - } - } - } - return changesMade; - } - - private boolean validateInstruction(String internalClassName, boolean iAmAnInterface, AbstractInsnNode rawInsn) { - AtomicBoolean changed = new AtomicBoolean(false); - switch (rawInsn.getType()) { - case AbstractInsnNode.INVOKE_DYNAMIC_INSN: - final InvokeDynamicInsnNode insn = (InvokeDynamicInsnNode) rawInsn; - insn.bsm = fixHandle(internalClassName, iAmAnInterface, insn.bsm, changed); - if (insn.bsmArgs != null) { - for (int i = 0; i < insn.bsmArgs.length; i++) { - final Object arg = insn.bsmArgs[i]; - if (arg instanceof Handle) { - Handle handle = (Handle) arg; - insn.bsmArgs[i] = fixHandle(internalClassName, iAmAnInterface, handle, changed); - } - } - } - break; - default: - // no-op - } - return changed.get(); - } - - private Handle fixHandle(String internalClassName, boolean iAmAnInterface, Handle handle, AtomicBoolean changed) { - if (!handle.isInterface()) { - final boolean fixSelfReference = handle.getOwner() - .equals(internalClassName) && iAmAnInterface; - boolean fixJavaReference = false; - if (!fixSelfReference && handle.getOwner() - .startsWith("java/")) { - final String regularName = handle.getOwner() - .replace('/', '.'); - try { - final Class javaClass = Class.forName(regularName); - if (javaClass.isInterface()) { - fixJavaReference = true; - } - } catch (ClassNotFoundException cnfe) { - // no-op - LOGGER.warn("Reference to non-existing java class {} found.", regularName, cnfe); - } - } - if (fixSelfReference || fixJavaReference) { - changed.set(true); - return new Handle(handle.getTag(), handle.getOwner(), handle.getName(), handle.getDesc(), true); - } - } - return handle; - } -} \ No newline at end of file diff --git a/src/main/java/me/eigenraven/lwjgl3ify/core/LwjglRedirectTransformer.java b/src/main/java/me/eigenraven/lwjgl3ify/core/LwjglRedirectTransformer.java deleted file mode 100644 index e0a6b329..00000000 --- a/src/main/java/me/eigenraven/lwjgl3ify/core/LwjglRedirectTransformer.java +++ /dev/null @@ -1,86 +0,0 @@ -package me.eigenraven.lwjgl3ify.core; - -import io.github.crucible.CrucibleModContainer; -import io.github.crucible.bootstrap.CrucibleMetadata; -import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware; -import net.minecraft.launchwrapper.IClassTransformer; -import org.objectweb.asm.*; -import org.objectweb.asm.commons.ClassRemapper; -import org.objectweb.asm.commons.Remapper; - -public class LwjglRedirectTransformer extends Remapper implements IClassTransformer { - - int remaps = 0, calls = 0; - - public static LwjglRedirectTransformer activeInstance = null; - - public LwjglRedirectTransformer() { - // Only use the last constructed transformer - activeInstance = this; - } - - @Override - public byte[] transform(String name, String transformedName, byte[] basicClass) { - if (this != activeInstance) { - return basicClass; - } - if (basicClass == null) { - return null; - } - if (name.contains("lwjgl3ify")) { - return basicClass; - } - ClassReader reader = new ClassReader(basicClass); - ClassWriter writer = new ClassWriter(0); - ClassVisitor visitor = new EscapingClassRemapper(writer); - - try { - reader.accept(visitor, ClassReader.EXPAND_FRAMES); - } catch (Lwjgl3AwareException e) { - return basicClass; - } catch (Exception e) { - CrucibleModContainer.logger.warn("Couldn't remap class {}", transformedName, e); - return basicClass; - } - - return writer.toByteArray(); - } - - final String[] fromPrefixes = new String[] { "org/lwjgl/", "javax/xml/bind/", "javax/servlet/" }; - - final String[] toPrefixes = new String[] { "org/lwjglx/", "jakarta/xml/bind/", "jakarta/servlet/" }; - - @Override - public String map(String typeName) { - if (typeName == null) { - return null; - } - calls++; - for (int pfx = 0; pfx < fromPrefixes.length; pfx++) { - if (typeName.startsWith(fromPrefixes[pfx])) { - remaps++; - return toPrefixes[pfx] + typeName.substring(fromPrefixes[pfx].length()); - } - } - - return typeName; - } - - public static class Lwjgl3AwareException extends RuntimeException { - } - - public class EscapingClassRemapper extends ClassRemapper { - - public EscapingClassRemapper(ClassWriter writer) { - super(writer, LwjglRedirectTransformer.this); - } - - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if (desc.equals(Type.getDescriptor(Lwjgl3Aware.class))) { - throw new Lwjgl3AwareException(); - } - return super.visitAnnotation(desc, visible); - } - } -} \ No newline at end of file diff --git a/src/main/java/me/eigenraven/lwjgl3ify/core/UnfinalizeObjectHoldersTransformer.java b/src/main/java/me/eigenraven/lwjgl3ify/core/UnfinalizeObjectHoldersTransformer.java deleted file mode 100644 index f184e8ae..00000000 --- a/src/main/java/me/eigenraven/lwjgl3ify/core/UnfinalizeObjectHoldersTransformer.java +++ /dev/null @@ -1,125 +0,0 @@ -package me.eigenraven.lwjgl3ify.core; - -import java.util.ArrayList; -import java.util.List; - -import io.github.crucible.CrucibleConfigs; -import net.minecraft.launchwrapper.IClassTransformer; - -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.Type; -import org.objectweb.asm.tree.AnnotationNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldNode; - -import me.eigenraven.lwjgl3ify.WasFinalObjectHolder; - -public class UnfinalizeObjectHoldersTransformer implements IClassTransformer { - private final Logger LOGGER = LogManager.getLogger("lwjgl3ify"); - // Keep ClassNode-operating transformers together for efficiency (don't read/write the class multiple times) - final ExtensibleEnumTransformerHelper enumTransformer = new ExtensibleEnumTransformerHelper(); - final FixConstantPoolInterfaceMethodRefHelper cpiMethodRefTransformer = new FixConstantPoolInterfaceMethodRefHelper(); - - private static boolean isHolder(List annotations) { - if (annotations == null) { - return false; - } - for (AnnotationNode annotationNode : annotations) { - // Java 17 uses $ instead of / - final String desc = annotationNode.desc.replace('$', '/'); - if (desc.contains("cpw/mods/fml/common/registry/GameRegistry/ObjectHolder")) { - return true; - } - if (desc.contains("cpw/mods/fml/common/registry/GameRegistry/ItemStackHolder")) { - return true; - } - } - return false; - } - - @Override - public byte[] transform(String name, String transformedName, byte[] basicClass) { - if (basicClass == null) { - return null; - } - - if (transformedName.startsWith("me.eigenraven.lwjgl3ify")) { - return basicClass; - } - try { - final ClassReader reader = new ClassReader(basicClass); - final ClassNode node = new ClassNode(); - reader.accept(node, ClassReader.EXPAND_FRAMES); - final Type classType = Type.getType("L" + name.replace('.', '/') + ";"); - - boolean transformClass = false; - boolean workDone = false; - if (transformedName.equals("net.minecraft.init.Blocks") - || transformedName.equals("net.minecraft.init.Items")) { - transformClass = true; - } - transformClass |= isHolder(node.visibleAnnotations); - if (transformedName.equals("team.chisel.init.ChiselBlocks")) { - LOGGER.debug("chiselblocks"); - } - int fieldsModified = 0; - for (FieldNode field : node.fields) { - boolean transform = transformClass; - if (!transform) { - transform = isHolder(field.visibleAnnotations); - } - if (transform) { - workDone = true; - if ((field.access & Opcodes.ACC_FINAL) != 0) { - if (field.visibleAnnotations == null) { - field.visibleAnnotations = new ArrayList<>(1); - field.visibleAnnotations - .add(new AnnotationNode(Type.getDescriptor(WasFinalObjectHolder.class))); - } - field.access = field.access & (~Opcodes.ACC_FINAL); - } - fieldsModified++; - } - } - if (workDone) { - LOGGER.info("Unfinalized {} Holder fields in {}", fieldsModified, transformedName); - } - - if (CrucibleConfigs.configs.lwjgl3ify_extensibleEnums - .contains(transformedName)) { - if (node.interfaces == null) { - node.interfaces = new ArrayList<>(1); - } - node.interfaces.add(enumTransformer.MARKER_IFACE.getInternalName()); - } - - final boolean enumsTransformed = enumTransformer.processClassWithFlags(node, classType); - - if (enumsTransformed) { - workDone = true; - LOGGER.info("Dynamicized enum {}={}", name, transformedName); - } - - final boolean ifaceMethodRefsTransformed = cpiMethodRefTransformer.transform(node); - - if (ifaceMethodRefsTransformed) { - workDone = true; - LOGGER - .warn("Fixed missing CONSTANT_InterfaceMethodRef miscompilation in {}={}", name, transformedName); - } - - if (workDone) { - final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - node.accept(writer); - return writer.toByteArray(); - } - } catch (Exception e) { - LOGGER.error("Error when unfinalizing ObjectHolder transformer", e); - } - return basicClass; - } -} \ No newline at end of file diff --git a/src/main/java/org/bukkit/craftbukkit/v1_7_R4/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/v1_7_R4/block/CraftBlock.java index 8021f905..542adced 100644 --- a/src/main/java/org/bukkit/craftbukkit/v1_7_R4/block/CraftBlock.java +++ b/src/main/java/org/bukkit/craftbukkit/v1_7_R4/block/CraftBlock.java @@ -14,6 +14,7 @@ import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.biome.BiomeGenBase; import net.minecraftforge.cauldron.block.CraftCustomContainer; +import io.github.crucible.util.BukkitEnumHelper; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; @@ -191,7 +192,7 @@ public static BiomeGenBase biomeToBiomeBase(Biome bio) { int id = BiomeGenBase.getBiome(i).biomeID; System.out.println("Adding biome mapping " + BiomeGenBase.getBiome(i).biomeID + " " + name + " at BiomeBase[" + i + "]"); - net.minecraftforge.common.util.EnumHelper.addBukkitBiome(name); // Forge + BukkitEnumHelper.addBukkitBiome(name); // Forge BIOME_MAPPING[BiomeGenBase.getBiome(i).biomeID] = Enum.valueOf(Biome.class, name); // Cauldron end } diff --git a/src/main/java/org/bukkit/craftbukkit/v1_7_R4/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/v1_7_R4/entity/CraftEntity.java index 2ea2aea7..242e8d58 100644 --- a/src/main/java/org/bukkit/craftbukkit/v1_7_R4/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/v1_7_R4/entity/CraftEntity.java @@ -3,7 +3,7 @@ import net.minecraft.entity.Entity; import io.github.crucible.entity.CraftCustomEntity; import net.minecraftforge.common.DimensionManager; -import net.minecraftforge.common.util.EnumHelper; +import io.github.crucible.util.BukkitEnumHelper; import net.minecraftforge.common.util.FakePlayerFactory; import org.bukkit.EntityEffect; import org.bukkit.Location; @@ -501,7 +501,7 @@ public static void initMappings() { int entityId = getEntityTypeIDfromClass(entityClass); Class bukkitEntityClass = CraftEntity.getEntityClass(entityClass); - EnumHelper.addBukkitEntityType(entityName, bukkitEntityClass, entityId, false); + BukkitEnumHelper.addBukkitEntityType(entityName, bukkitEntityClass, entityId, false); } } // Cauldron end diff --git a/src/main/java/thermos/ThermosClassTransformer.java b/src/main/java/thermos/ThermosClassTransformer.java index 73f9ed24..385fd00f 100644 --- a/src/main/java/thermos/ThermosClassTransformer.java +++ b/src/main/java/thermos/ThermosClassTransformer.java @@ -1,40 +1,82 @@ package thermos; import cpw.mods.fml.common.FMLLog; +import net.minecraft.launchwrapper.IClassTransformer; import org.apache.logging.log4j.Level; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.InsnNode; -import org.objectweb.asm.tree.IntInsnNode; -import pw.prok.imagine.asm.ImagineASM; -import pw.prok.imagine.asm.Transformer; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.*; import static org.objectweb.asm.Opcodes.*; -@Transformer.RegisterTransformer -public class ThermosClassTransformer implements Transformer { +public class ThermosClassTransformer implements IClassTransformer { + private static final String TARGET_CLASS = + "climateControl/utils/ChunkGeneratorExtractor"; + @Override - public void transform(final ImagineASM asm) { - if (asm.is("climateControl.utils.ChunkGeneratorExtractor")) { - boolean undergroundBiomesInstalled = false; - try { - Class.forName("exterminatorJeff.undergroundBiomes.worldGen.ChunkProviderWrapper"); - undergroundBiomesInstalled = true; - } catch (Exception ignored) { - } - if (!undergroundBiomesInstalled) { - FMLLog.log(Level.INFO, "Thermos: Patching " + asm.getActualName() + " for compatibility with Climate Control"); - extractFrom(asm, asm.method("extractFrom", - "(Lnet/minecraft/world/WorldServer;)Lnet/minecraft/world/chunk/IChunkProvider;").instructions()); + public byte[] transform(String name, String transformedName, byte[] basicClass) { + if (!TARGET_CLASS.equals(transformedName.replace('.', '/'))) { + return basicClass; + } + + if (isUndergroundBiomesPresent()) { + return basicClass; + } + + FMLLog.log(Level.INFO, + "Thermos: Patching ChunkGeneratorExtractor for Climate Control compatibility"); + + ClassNode cn = new ClassNode(); + ClassReader cr = new ClassReader(basicClass); + cr.accept(cn, 0); + + for (MethodNode mn : cn.methods) { + if (mn.name.equals("extractFrom") + && mn.desc.equals("(Lnet/minecraft/world/WorldServer;)Lnet/minecraft/world/chunk/IChunkProvider;")) { + + patchMethod(mn); + break; } } + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cn.accept(cw); + return cw.toByteArray(); + } + + private void patchMethod(MethodNode mn) { + mn.instructions.clear(); + + boolean obf = isObfuscated(); + + String worldClass = obf ? "ahb" : "net/minecraft/world/World"; + String fieldName = obf ? "v" : "chunkProvider"; + String fieldDesc = obf ? "Lapu;" : + "Lnet/minecraft/world/chunk/IChunkProvider;"; + + InsnList insns = new InsnList(); + insns.add(new VarInsnNode(ALOAD, 1)); + insns.add(new FieldInsnNode(GETFIELD, worldClass, fieldName, fieldDesc)); + insns.add(new InsnNode(ARETURN)); + + mn.instructions.add(insns); + } + + private boolean isUndergroundBiomesPresent() { + try { + Class.forName("exterminatorJeff.undergroundBiomes.worldGen.ChunkProviderWrapper"); + return true; + } catch (Throwable ignored) { + return false; + } } - public void extractFrom(ImagineASM asm, InsnList list) { - //Pair fieldChunkProvider = asm.field("net/minecraft/world/World", "chunkProvider"); - list.clear(); - list.add(new IntInsnNode(ALOAD, 1)); - list.add(new FieldInsnNode(GETFIELD, "ahb", "v", "Lapu;")); - list.add(new InsnNode(ARETURN)); + private boolean isObfuscated() { + try { + Class.forName("net.minecraft.world.World"); + return false; + } catch (Throwable t) { + return true; + } } }