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); + } + } } 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 57273215..d92361a2 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 @@ -28,7 +29,12 @@ import java.util.*; */ public class ModelBakeEventHelper { // TODO: make into config option - private static final Set INCOMPATIBLE_MODS = ImmutableSet.of("industrialforegoing", "vampirism", "elevatorid", "embers"); + private static final Set INCOMPATIBLE_MODS = ImmutableSet.of( + "industrialforegoing", + "mekanism", + "vampirism", + "elevatorid", + "embers"); private final Map modelRegistry; private final Set topLevelModelLocations; private final MutableGraph dependencyGraph; @@ -79,7 +85,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 +106,12 @@ public class ModelBakeEventHelper { logWarning(); return super.values(); } + + @Override + public void replaceAll(BiFunction function) { + logWarning(); + super.replaceAll(function); + } }; } @@ -139,6 +151,19 @@ public class ModelBakeEventHelper { public boolean containsKey(@Nullable Object key) { return ourModelLocations.contains(key) || super.containsKey(key); } + + @Override + public void replaceAll(BiFunction function) { + 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) { + BakedModel existing = get(location); + BakedModel replacement = function.apply(location, existing); + if(replacement != existing) { + put(location, replacement); + } + } + } }; } } 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 f51d9928..076ce3c6 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.util.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); ModelEvent.ModifyBakingResult postedEvent = new ModelEvent.ModifyBakingResult(newRegistry, bakeEvent.getModelBakery()); + 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); + } }); } }