diff --git a/dependencies.gradle b/dependencies.gradle index 44f9ce6b..89cde434 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -24,4 +24,5 @@ dependencies { compileOnly("curse.maven:cofh-core-69162:2388751") { transitive = false } + compileOnly files("dependencies/Thermos-1.7.10-1614-stripped.jar") } diff --git a/dependencies/Thermos-1.7.10-1614-stripped.jar b/dependencies/Thermos-1.7.10-1614-stripped.jar new file mode 100644 index 00000000..66d3d045 Binary files /dev/null and b/dependencies/Thermos-1.7.10-1614-stripped.jar differ diff --git a/src/main/java/com/mitchej123/hodgepodge/asm/HodgePodgeASMLoader.java b/src/main/java/com/mitchej123/hodgepodge/asm/HodgePodgeASMLoader.java index de421faa..7621b6dc 100644 --- a/src/main/java/com/mitchej123/hodgepodge/asm/HodgePodgeASMLoader.java +++ b/src/main/java/com/mitchej123/hodgepodge/asm/HodgePodgeASMLoader.java @@ -42,7 +42,11 @@ public enum AsmTransformers { ), FIX_POTION_EFFECT_RENDERING("Move vanilla potion effect status rendering before everything else", () -> HodgepodgeMixinPlugin.config.fixPotionEffectRender, - Collections.singletonList("com.mitchej123.hodgepodge.asm.InventoryEffectRendererTransformer")) + Collections.singletonList("com.mitchej123.hodgepodge.asm.InventoryEffectRendererTransformer")), + THERMOS_SLEDGEHAMMER_FURNACE_FIX("Take a sledgehammer to CraftServer.resetRecipes() to prevent it from breaking our Furnace Fix", + ()-> HodgepodgeMixinPlugin.thermosTainted && HodgepodgeMixinPlugin.config.speedupVanillaFurnace, + Collections.singletonList("com.mitchej123.hodgepodge.asm.ThermosFurnaceSledgeHammer") + ) ; private final String name; diff --git a/src/main/java/com/mitchej123/hodgepodge/asm/SpeedupProgressBarTransformer.java b/src/main/java/com/mitchej123/hodgepodge/asm/SpeedupProgressBarTransformer.java index c6b5788f..7a431b1f 100644 --- a/src/main/java/com/mitchej123/hodgepodge/asm/SpeedupProgressBarTransformer.java +++ b/src/main/java/com/mitchej123/hodgepodge/asm/SpeedupProgressBarTransformer.java @@ -51,8 +51,9 @@ public byte[] transform(String name, String transformedName, byte[] basicClass) } cn.accept(cw); return cw.toByteArray(); - } else { + } + else { return basicClass; } } -} \ No newline at end of file +} diff --git a/src/main/java/com/mitchej123/hodgepodge/asm/ThermosFurnaceSledgeHammer.java b/src/main/java/com/mitchej123/hodgepodge/asm/ThermosFurnaceSledgeHammer.java new file mode 100644 index 00000000..89362504 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/asm/ThermosFurnaceSledgeHammer.java @@ -0,0 +1,47 @@ +package com.mitchej123.hodgepodge.asm; + +import com.mitchej123.hodgepodge.core.HodgepodgeMixinPlugin; +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.tree.ClassNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.MethodNode; + +import static org.objectweb.asm.Opcodes.ASM5; + +@SuppressWarnings("unused") +public class ThermosFurnaceSledgeHammer implements IClassTransformer { + private static final Logger LOGGER = LogManager.getLogger("ThermosFurnaceSledgeHammer"); + @Override + public byte[] transform(String name, String transformedName, byte[] basicClass) { + if (HodgepodgeMixinPlugin.config.thermosCraftServerClass.equals(transformedName)) { + LOGGER.info("Patching Thermos or derivative to not break our furnace fix"); + final ClassReader cr = new ClassReader(basicClass); + final ClassWriter cw = new ClassWriter(0); + + final ClassNode cn = new ClassNode(ASM5); + cr.accept(cn, 0); + for (MethodNode m : cn.methods) { + if ("resetRecipes".equals(m.name)) { + LOGGER.info("Taking a sledgehammer to CraftServer.resetRecipes()"); + //Replace the body with a RETURN opcode + InsnList insnList = new InsnList(); + insnList.add(new InsnNode(Opcodes.RETURN)); + m.instructions = insnList; + m.maxStack = 0; + } + } + cn.accept(cw); + return cw.toByteArray(); + } + else { + return basicClass; + } + } +} + diff --git a/src/main/java/com/mitchej123/hodgepodge/core/HodgepodgeMixinPlugin.java b/src/main/java/com/mitchej123/hodgepodge/core/HodgepodgeMixinPlugin.java index 907c74e9..8d31abd0 100644 --- a/src/main/java/com/mitchej123/hodgepodge/core/HodgepodgeMixinPlugin.java +++ b/src/main/java/com/mitchej123/hodgepodge/core/HodgepodgeMixinPlugin.java @@ -30,6 +30,7 @@ public class HodgepodgeMixinPlugin implements IMixinConfigPlugin { Class.forName("org.bukkit.World"); thermosTainted = true; log.warn("Thermos/Bukkit detected; This is an unsupported configuration -- Things may not function properly."); + log.warn(" Using `{}` for CraftServer Package. If this is not correct, please update your config file!", config.thermosCraftServerClass); } catch (ClassNotFoundException e) { thermosTainted = false; log.info("Thermos/Bukkit NOT detected :-D"); @@ -176,17 +177,23 @@ public enum MixinSets { () -> config.fixHopperHitBox, Collections.singletonList("fixHopperHitBox.MixinBlockHopper")), FIX_GET_BLOCK_LIGHT_VALUE("Fix vanilla light value calculation NPE", - () -> config.fixGetBlockLightValue, - Collections.singletonList("fixGetBlockLightValue.MixinWorld")), + () -> config.fixGetBlockLightValue, + Collections.singletonList("fixGetBlockLightValue.MixinWorld")), FIX_FIRE_SPREAD("Fix vanilla fire spread NPE", - () -> config.fixFireSpread, - Collections.singletonList("fixFireSpread.MixinBlockFire")), + () -> config.fixFireSpread, + Collections.singletonList("fixFireSpread.MixinBlockFire")), TILE_RENDERER_PROFILER("Shows renderer's impact on FPS in vanilla lagometer", - () -> config.enableTileRendererProfiler, - Arrays.asList("profiler.TileEntityRendererDispatcherMixin", "profiler.MinecraftMixin")), + () -> config.enableTileRendererProfiler, + Arrays.asList("profiler.TileEntityRendererDispatcherMixin", "profiler.MinecraftMixin")), ADD_CV_SUPPORT_TO_WAND_PEDESTAL("Add CV support to Thaumcraft wand recharge pedestal", - ()->config.addCVSupportToWandPedestal, - Collections.singletonList("wandPedestalCV.MixinTileWandPedestal")) + ()->config.addCVSupportToWandPedestal, + Collections.singletonList("wandPedestalCV.MixinTileWandPedestal")), + FIX_FORGE_URL_DETECTION("Fix URISyntaxException in Forge Chat", + () -> config.fixUrlDetection, + Collections.singletonList("fixUrlDetection.MixinForgeHooks")), + SPEEDUP_VANILLA_FURNACE("Speedup vanilla furnace recipe lookup", + () -> config.speedupVanillaFurnace, + Collections.singletonList("speedupVanillaFurnace.MixinFurnaceRecipes")) ; diff --git a/src/main/java/com/mitchej123/hodgepodge/core/LoadingConfig.java b/src/main/java/com/mitchej123/hodgepodge/core/LoadingConfig.java index 821234ff..d4e10043 100644 --- a/src/main/java/com/mitchej123/hodgepodge/core/LoadingConfig.java +++ b/src/main/java/com/mitchej123/hodgepodge/core/LoadingConfig.java @@ -33,10 +33,15 @@ public class LoadingConfig { public boolean fixPotionEffectRender; public boolean addCVSupportToWandPedestal; public boolean speedupProgressBar; + public boolean fixUrlDetection; + public boolean speedupVanillaFurnace; // ASM public boolean pollutionAsm; public boolean cofhWorldTransformer; public boolean enableTileRendererProfiler; + + public String thermosCraftServerClass; + public static Configuration config; @@ -63,6 +68,7 @@ public LoadingConfig(File file) { fixHopperHitBox = config.get("fixes", "fixHopperHitBox", true, "Fix vanilla hopper hit box").getBoolean(); fixGetBlockLightValue = config.get("fixes", "fixGetBlockLightValue", true, "Fix vanilla light calculation sometimes cause NPE on thermos").getBoolean(); fixFireSpread = config.get("fixes", "fixFireSpread", true, "Fix vanilla fire spread sometimes cause NPE on thermos").getBoolean(); + fixUrlDetection = config.get("fixes", "fixUrlDetection", true, "Fix URISyntaxException in forge.").getBoolean(); fixPotionEffectRender = config.get("tweaks", "fixPotionEffectRender", true, "Move vanilla potion effect status rendering before everything else").getBoolean(); installAnchorAlarm = config.get("tweaks", "installAnchorAlarm", true, "Wake up passive & personal anchors on player login").getBoolean(); @@ -75,19 +81,22 @@ public LoadingConfig(File file) { ic2SeedMaxStackSize = config.get("tweaks", "ic2SeedMaxStackSize", 64, "IC2 seed max stack size").getInt(); speedupChunkCoordinatesHashCode = config.get("speedups", "speedupChunkCoordinatesHashCode", true, "Speedup ChunkCoordinates hashCode").getBoolean(); + speedupVanillaFurnace = config.get("speedups", "speedupVanillaFurnace", true, "Speedup Vanilla Furnace recipe lookup").getBoolean(); speedupProgressBar = config.get("asm", "speedupProgressBar", true, "Speedup progressbar").getBoolean(); pollutionAsm = config.get("asm", "pollutionAsm", true, "Enable pollution rendering ASM").getBoolean(); cofhWorldTransformer = config.get("asm", "cofhWorldTransformer", true, "Enable Glease's ASM patch to disable unused CoFH tileentity cache").getBoolean(); + thermosCraftServerClass = config.get("asm", "thermosCraftServerClass", "org.bukkit.craftbukkit.v1_7_R4.CraftServer", "If using Bukkit/Thermos, the CraftServer package.").getString(); + if (config.hasChanged()) config.save(); } public static void postInitClient() { - //need to be done later cause it initializes classes + // need to be done later cause it initializes classes if (config == null) { - System.err.println("Didnt load HODGE"); + System.err.println("Didn't load HODGEPODGE"); config = new Configuration(new File(Launch.minecraftHome, "config/hodgepodge.cfg")); } diff --git a/src/main/java/com/mitchej123/hodgepodge/core/util/ItemStackHashingStrategy.java b/src/main/java/com/mitchej123/hodgepodge/core/util/ItemStackHashingStrategy.java new file mode 100644 index 00000000..943d7892 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/core/util/ItemStackHashingStrategy.java @@ -0,0 +1,24 @@ +package com.mitchej123.hodgepodge.core.util; + +import gnu.trove.strategy.HashingStrategy; +import net.minecraft.item.ItemStack; + +/* + * Strategy to make ItemStacks Hashable + * - Taken from https://github.com/hilburn/AdvancedSystemsManager/blob/master/src/main/java/advancedsystemsmanager/flow/execution/buffers/maps/ItemStackHashingStrategy.java + * under the the DBaJ (Don't Be a Jerk) non-commercial care-free license. + * (c) hilburn + */ +public class ItemStackHashingStrategy implements HashingStrategy { + public static final ItemStackHashingStrategy INSTANCE = new ItemStackHashingStrategy(); + + @Override + public int computeHashCode(ItemStack stack) { + return stack.getItem().hashCode() ^ (stack.getItemDamage() * 31) ^ (stack.hasTagCompound() ? stack.stackTagCompound.hashCode() : 0); + } + + @Override + public boolean equals(ItemStack stack1, ItemStack stack2) { + return stack1 != null && stack1.isItemEqual(stack2) && ItemStack.areItemStackTagsEqual(stack1, stack2); + } +} diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/fixUrlDetection/MixinForgeHooks.java b/src/main/java/com/mitchej123/hodgepodge/mixins/fixUrlDetection/MixinForgeHooks.java new file mode 100644 index 00000000..21f59572 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/fixUrlDetection/MixinForgeHooks.java @@ -0,0 +1,68 @@ +package com.mitchej123.hodgepodge.mixins.fixUrlDetection; + +import net.minecraft.event.ClickEvent; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; +import net.minecraftforge.common.ForgeHooks; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Mixin(ForgeHooks.class) +public class MixinForgeHooks { + private static final Pattern URL_PATTERN = Pattern.compile( + // schema ipv4 OR namespace port path ends + // |-----------------| |-------------------------| |-------------------------| |---------| |--| |---------------| + "((?:[a-z0-9]{2,}:\\/\\/)?(?:(?:[0-9]{1,3}\\.){3}[0-9]{1,3}|(?:[-\\w_]{1,}\\.[a-z]{2,}?))(?::[0-9]{1,5})?.*?(?=[!\"\u00A7 \n]|$))", + Pattern.CASE_INSENSITIVE); + + /** + * @author LexManos + * Backported from https://github.com/MinecraftForge/MinecraftForge/commit/5b28eb53e8623448b1c2bdb46b8924662e690995 + */ + @Overwrite + public static IChatComponent newChatWithLinks(String string) { + // Includes ipv4 and domain pattern + // Matches an ip (xx.xxx.xx.xxx) or a domain (something.com) with or + // without a protocol or path. + IChatComponent ichat = new ChatComponentText(""); + Matcher matcher = MixinForgeHooks.URL_PATTERN.matcher(string); + int lastEnd = 0; + + // Find all urls + while (matcher.find()) { + int start = matcher.start(); + int end = matcher.end(); + + // Append the previous left overs. + ichat.appendText(string.substring(lastEnd, start)); + lastEnd = end; + String url = string.substring(start, end); + IChatComponent link = new ChatComponentText(url); + + try { + // Add schema so client doesn't crash. + if ((new URI(url)).getScheme() == null) + url = "http://" + url; + } + catch (URISyntaxException e) { + // Bad syntax bail out! + ichat.appendText(url); + continue; + } + + // Set the click event and append the link. + ClickEvent click = new ClickEvent(ClickEvent.Action.OPEN_URL, url); + link.getChatStyle().setChatClickEvent(click); + ichat.appendSibling(link); + } + + // Append the rest of the message. + ichat.appendText(string.substring(lastEnd)); + return ichat; + } +} diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/speedupVanillaFurnace/MixinFurnaceRecipes.java b/src/main/java/com/mitchej123/hodgepodge/mixins/speedupVanillaFurnace/MixinFurnaceRecipes.java new file mode 100644 index 00000000..11ce6d1a --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/speedupVanillaFurnace/MixinFurnaceRecipes.java @@ -0,0 +1,97 @@ +package com.mitchej123.hodgepodge.mixins.speedupVanillaFurnace; + +import com.mitchej123.hodgepodge.core.HodgepodgeMixinPlugin; +import com.mitchej123.hodgepodge.core.util.ItemStackHashingStrategy; +import gnu.trove.map.hash.TCustomHashMap; +import net.minecraft.block.Block; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.FurnaceRecipes; +import net.minecraft.launchwrapper.Launch; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.lang.reflect.Field; +import java.util.Map; + +@Mixin(FurnaceRecipes.class) +public abstract class MixinFurnaceRecipes { + /* + * Speed up FurnaceRecipes.getSmeltingResult by: + * 1) Hijacking the constructor here to recreate the lists with a replacement hash map and an ItemStack hashing strategy + * 2) No longer looping over every. single. recipe. in the list and using the .get() + */ + @Shadow private Map smeltingList; + @Shadow private Map experienceList; + @Shadow abstract boolean func_151397_a(ItemStack p_151397_1_, ItemStack p_151397_2_); + + @Redirect( + at=@At( + value="INVOKE", + target="Lnet/minecraft/item/crafting/FurnaceRecipes;func_151393_a(Lnet/minecraft/block/Block;Lnet/minecraft/item/ItemStack;F)V", + ordinal = 0 + ), + method="Lnet/minecraft/item/crafting/FurnaceRecipes;()V" + ) + private void doStuff(FurnaceRecipes instance, Block p_151393_1_, ItemStack p_151393_2_, float p_151393_3_) throws NoSuchFieldException, IllegalAccessException { + HodgepodgeMixinPlugin.log.info("Swapping out smeltingList and experienceList in FurnaceRecipes"); + + // Hack into the first call in the constructor and replace the lists with a new hashmap that has an ItemStackMi hashing strategy + boolean devEnv = (Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment"); + try { + Class clazz = Class.forName("net.minecraft.item.crafting.FurnaceRecipes"); + + Field smeltingList = clazz.getDeclaredField(devEnv ? "smeltingList" : "field_77604_b"); + smeltingList.setAccessible(true); + smeltingList.set(instance, new TCustomHashMap(ItemStackHashingStrategy.INSTANCE)); + + Field experienceList = clazz.getDeclaredField(devEnv ? "experienceList" : "field_77605_c"); + experienceList.setAccessible(true); + experienceList.set(instance, new TCustomHashMap(ItemStackHashingStrategy.INSTANCE)); + + HodgepodgeMixinPlugin.log.info("Successfully swapped the lists in FurnaceRecipes"); + + } catch (ClassNotFoundException | IllegalAccessException e) { + e.printStackTrace(); + } + instance.func_151393_a(p_151393_1_, p_151393_2_, p_151393_3_); + } + /** + * @author mitchej123 + * Inspired by later versions of forge + */ + @SuppressWarnings("unchecked") + @Overwrite(remap = false) + public void func_151394_a /* addSmeltingRecipe */ (ItemStack input, ItemStack stack, float experience) { + if (getSmeltingResult(input) != null) { + HodgepodgeMixinPlugin.log.info("Overwriting smelting recipe for input: {} and output {} with {}", input, getSmeltingResult(input), stack); + } + this.smeltingList.put(input, stack); + this.experienceList.put(stack, experience); + } + + /** + * @author mitchej123 + * Significantly Faster + */ + @Overwrite + public ItemStack getSmeltingResult(ItemStack stack) { + return (ItemStack) this.smeltingList.get(stack); + } + + /** + * @author mitchej123 + * Significantly Faster + */ + @Overwrite(remap = false) + public float func_151398_b /* getSmeltingExperience */ (ItemStack stack) { + float exp = stack.getItem().getSmeltingExperience(stack); + if (exp == -1) { + exp = (Float) (this.experienceList.get(stack)); + } + return exp; + } + +}