From d76fd84b76bb29e8604f3a209b27e6f615c94902 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 27 Dec 2023 15:56:52 -0500 Subject: [PATCH 1/6] Support replaceAll on the wrapping model registry --- .../forge/dynresources/ModelBakeEventHelper.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java index c05dd603..7c82021f 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java @@ -21,6 +21,7 @@ import org.embeddedt.modernfix.util.ForwardingInclDefaultsMap; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.function.BiFunction; /** * Stores a list of all known default block/item models in the game, and provides a namespaced version @@ -79,7 +80,7 @@ public class ModelBakeEventHelper { private void logWarning() { if(!WARNED_MOD_IDS.add(modId)) return; - ModernFix.LOGGER.warn("Mod '{}' is accessing Map#keySet/entrySet/values on the model registry map inside its event handler." + + ModernFix.LOGGER.warn("Mod '{}' is accessing Map#keySet/entrySet/values/replaceAll on the model registry map inside its event handler." + " This probably won't work as expected with dynamic resources on. Prefer using Map#get/put and constructing ModelResourceLocations another way.", modId); } @@ -100,6 +101,12 @@ public class ModelBakeEventHelper { logWarning(); return super.values(); } + + @Override + public void replaceAll(BiFunction function) { + logWarning(); + super.replaceAll(function); + } }; } @@ -139,6 +146,13 @@ public class ModelBakeEventHelper { public boolean containsKey(@Nullable Object key) { return ourModelLocations.contains(key) || super.containsKey(key); } + + @Override + public void replaceAll(BiFunction function) { + for(ResourceLocation location : keySet()) { + put(location, function.apply(location, get(location))); + } + } }; } } From aee0b2a47dbaa4f5263327d66fd93a2d27cf9539 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 27 Dec 2023 15:59:51 -0500 Subject: [PATCH 2/6] Add Mekanism to model bake event helper --- .../modernfix/forge/dynresources/ModelBakeEventHelper.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java index 7c82021f..ee98dc5e 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java @@ -29,7 +29,11 @@ import java.util.function.BiFunction; */ public class ModelBakeEventHelper { // TODO: make into config option - private static final Set INCOMPATIBLE_MODS = ImmutableSet.of("industrialforegoing", "vampirism", "elevatorid"); + private static final Set INCOMPATIBLE_MODS = ImmutableSet.of( + "industrialforegoing", + "mekanism", + "vampirism", + "elevatorid"); private final Map modelRegistry; private final Set topLevelModelLocations; private final MutableGraph dependencyGraph; From 11508fbe07e1f8e0c8c37a1e7723a164d399e503 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 27 Dec 2023 16:03:54 -0500 Subject: [PATCH 3/6] Track duration of model bake events when dynamic resources is enabled --- .../perf/dynamic_resources/ForgeHooksClientMixin.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ForgeHooksClientMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ForgeHooksClientMixin.java index 1a631860..96d69295 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ForgeHooksClientMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ForgeHooksClientMixin.java @@ -1,5 +1,6 @@ package org.embeddedt.modernfix.forge.mixin.perf.dynamic_resources; +import com.google.common.base.Stopwatch; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.client.ForgeHooksClient; @@ -9,6 +10,7 @@ import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.common.ObfuscationReflectionHelper; +import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.forge.dynresources.ModelBakeEventHelper; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -16,6 +18,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; import java.lang.reflect.Method; import java.util.Map; +import java.util.concurrent.TimeUnit; @Mixin(ForgeHooksClient.class) public class ForgeHooksClientMixin { @@ -32,11 +35,16 @@ public class ForgeHooksClientMixin { ModList.get().forEachModContainer((id, mc) -> { Map newRegistry = helper.wrapRegistry(id); ModelBakeEvent postedEvent = new ModelBakeEvent(bakeEvent.getModelManager(), newRegistry, bakeEvent.getModelLoader()); + Stopwatch timer = Stopwatch.createStarted(); try { acceptEv.invoke(mc, postedEvent); } catch(ReflectiveOperationException e) { e.printStackTrace(); } + timer.stop(); + if(timer.elapsed(TimeUnit.SECONDS) >= 1) { + ModernFix.LOGGER.warn("Mod '{}' took {} in the model bake event", id, timer); + } }); } } From d1863cc66e77934e60fc402ddb290ff87ca420ad Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 27 Dec 2023 16:06:22 -0500 Subject: [PATCH 4/6] Make replaceAll implementation more robust, add warning --- .../modernfix/forge/dynresources/ModelBakeEventHelper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java index ee98dc5e..0e3dfca6 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java @@ -153,7 +153,9 @@ public class ModelBakeEventHelper { @Override public void replaceAll(BiFunction function) { - for(ResourceLocation location : keySet()) { + ModernFix.LOGGER.warn("Mod '{}' is calling replaceAll on the model registry. This requires temporarily loading every model for that mod, which is slow.", modId); + List locations = new ArrayList<>(keySet()); + for(ResourceLocation location : locations) { put(location, function.apply(location, get(location))); } } From 675c58a437f68b42f9daa7bfc4b143d0003f7dba Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 27 Dec 2023 16:13:45 -0500 Subject: [PATCH 5/6] Only call put on the model map if the replacement model is different --- .../modernfix/forge/dynresources/ModelBakeEventHelper.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java index 0e3dfca6..0aeb9fac 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java @@ -156,7 +156,11 @@ public class ModelBakeEventHelper { ModernFix.LOGGER.warn("Mod '{}' is calling replaceAll on the model registry. This requires temporarily loading every model for that mod, which is slow.", modId); List locations = new ArrayList<>(keySet()); for(ResourceLocation location : locations) { - put(location, function.apply(location, get(location))); + BakedModel existing = get(location); + BakedModel replacement = function.apply(location, existing); + if(replacement != existing) { + put(location, replacement); + } } } }; From a79ea9766aba1ba08ca111649347ab09e5bad5b6 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 27 Dec 2023 18:41:51 -0500 Subject: [PATCH 6/6] Prevent mod mixins from applying if there is a Forge loading error --- .../forge/classloading/ATInjector.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/classloading/ATInjector.java b/forge/src/main/java/org/embeddedt/modernfix/forge/classloading/ATInjector.java index f86b9623..35092190 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/classloading/ATInjector.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/classloading/ATInjector.java @@ -1,5 +1,10 @@ package org.embeddedt.modernfix.forge.classloading; +import cpw.mods.jarhandling.SecureJar; +import cpw.mods.modlauncher.LaunchPluginHandler; +import cpw.mods.modlauncher.Launcher; +import cpw.mods.modlauncher.api.NamedPath; +import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.fml.loading.moddiscovery.ModValidator; @@ -7,9 +12,15 @@ import net.minecraftforge.fml.util.ObfuscationReflectionHelper; import org.apache.commons.lang3.tuple.Pair; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.util.CommonModUtil; +import org.objectweb.asm.Type; +import java.lang.reflect.Field; import java.nio.file.Path; +import java.util.EnumSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.function.Consumer; import java.util.stream.Collectors; public class ATInjector { @@ -31,6 +42,80 @@ public class ATInjector { } } } + + // inject into Launcher.INSTANCE.launchPlugins and wrap the mixin plugin, so that mixin transformations + // are not applied + try { + Launcher launcher = Launcher.INSTANCE; + Field launchPlugins = Launcher.class.getDeclaredField("launchPlugins"); + launchPlugins.setAccessible(true); + + LaunchPluginHandler handler = (LaunchPluginHandler) launchPlugins.get(launcher); + Field plugins = LaunchPluginHandler.class.getDeclaredField("plugins"); + plugins.setAccessible(true); + + //noinspection unchecked + Map map = (Map) plugins.get(handler); + Map newMap = new HashMap<>(map); + NonTransformingLaunchPluginService.class.getName(); // trigger classloading, just to be safe + newMap.replaceAll((name, plugin) -> { + if(plugin.getClass().getName().startsWith("org.spongepowered.asm.launch.MixinLaunchPlugin")) { + ModernFixMixinPlugin.instance.logger.warn("Disabling plugin '{}': {}", name, plugin.getClass().getName()); + return new NonTransformingLaunchPluginService(plugin); + } else { + return plugin; + } + }); + plugins.set(handler, newMap); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } }, "applying mod ATs in errored state"); } + + static class NonTransformingLaunchPluginService implements ILaunchPluginService { + + private final ILaunchPluginService delegate; + + NonTransformingLaunchPluginService(ILaunchPluginService delegate) { + this.delegate = delegate; + } + + @Override + public String name() { + return delegate.name(); + } + + private static final EnumSet NEVER = EnumSet.noneOf(Phase.class); + + @Override + public EnumSet handlesClass(Type classType, boolean isEmpty) { + return NEVER; + } + + @Override + public void offerResource(Path resource, String name) { + delegate.offerResource(resource, name); + } + + @Override + public void addResources(List resources) { + delegate.addResources(resources); + } + + @Override + public void initializeLaunch(ITransformerLoader transformerLoader, NamedPath[] specialPaths) { + delegate.initializeLaunch(transformerLoader, specialPaths); + } + + @Override + public T getExtension() { + return delegate.getExtension(); + } + + @Override + public void customAuditConsumer(String className, Consumer auditDataAcceptor) { + delegate.customAuditConsumer(className, auditDataAcceptor); + } + } }