From f43d54eafd23610f5c8ec43086bc4e674ab62554 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 6 Jan 2023 10:42:48 -0500 Subject: [PATCH] Implement facility to process specific JEI plugins on the main thread --- build.gradle | 5 - .../org/embeddedt/modernfix/ModernFix.java | 7 +- .../modernfix/core/ModernFixMixinPlugin.java | 6 +- .../core/config/ModernFixConfig.java | 197 ++++-------------- .../core/config/ModernFixEarlyConfig.java | 177 ++++++++++++++++ .../bugfix/concurrency/MinecraftMixin.java | 34 +++ .../ClientLifecycleHandlerMixin.java | 22 +- .../mixin/perf/async_jei/JeiStarterMixin.java | 2 +- .../perf/async_jei/PluginCallerMixin.java | 17 ++ src/main/resources/modernfix.mixins.json | 1 + 10 files changed, 285 insertions(+), 183 deletions(-) create mode 100644 src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/bugfix/concurrency/MinecraftMixin.java diff --git a/build.gradle b/build.gradle index 8ba8c331..c0c23ae8 100644 --- a/build.gradle +++ b/build.gradle @@ -126,11 +126,6 @@ repositories { name = "Progwml6 maven" url = "https://dvs1.progwml6.com/files/maven/" } - maven { - // location of a maven mirror for JEI files, as a fallback - name = "ModMaven" - url = "https://modmaven.dev" - } } dependencies { diff --git a/src/main/java/org/embeddedt/modernfix/ModernFix.java b/src/main/java/org/embeddedt/modernfix/ModernFix.java index ff5863e2..23d47671 100644 --- a/src/main/java/org/embeddedt/modernfix/ModernFix.java +++ b/src/main/java/org/embeddedt/modernfix/ModernFix.java @@ -16,6 +16,7 @@ import net.minecraftforge.fml.ExtensionPoint; import net.minecraftforge.fml.InterModComms; import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; @@ -26,16 +27,19 @@ import net.minecraftforge.fml.network.FMLNetworkConstants; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.embeddedt.modernfix.core.config.ModernFixConfig; import java.util.stream.Collectors; // The value here should match an entry in the META-INF/mods.toml file -@Mod("modernfix") +@Mod(ModernFix.MODID) public class ModernFix { // Directly reference a log4j logger. public static final Logger LOGGER = LogManager.getLogger("ModernFix"); + public static final String MODID = "modernfix"; + public static ModernFix INSTANCE; // Used to skip computing the blockstate caches twice @@ -48,5 +52,6 @@ public class ModernFix { MinecraftForge.EVENT_BUS.register(this); DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> MinecraftForge.EVENT_BUS.register(new ModernFixClient())); ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(() -> FMLNetworkConstants.IGNORESERVERONLY, (a, b) -> true)); + ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModernFixConfig.COMMON_CONFIG); } } diff --git a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index a2bf4725..d22b496e 100644 --- a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java +++ b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java @@ -2,7 +2,7 @@ package org.embeddedt.modernfix.core; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.embeddedt.modernfix.core.config.ModernFixConfig; +import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig; import org.embeddedt.modernfix.core.config.Option; import org.objectweb.asm.tree.ClassNode; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; @@ -16,12 +16,12 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { private static final String MIXIN_PACKAGE_ROOT = "org.embeddedt.modernfix.mixin."; private final Logger logger = LogManager.getLogger("ModernFix"); - public static ModernFixConfig config = null; + public static ModernFixEarlyConfig config = null; @Override public void onLoad(String mixinPackage) { try { - config = ModernFixConfig.load(new File("./config/modernfix-mixins.properties")); + config = ModernFixEarlyConfig.load(new File("./config/modernfix-mixins.properties")); } catch (Exception e) { throw new RuntimeException("Could not load configuration file for ModernFix", e); } diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixConfig.java index 325cbd70..fd9052fc 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixConfig.java @@ -1,179 +1,52 @@ package org.embeddedt.modernfix.core.config; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; +import org.embeddedt.modernfix.ModernFix; -import net.minecraftforge.fml.loading.FMLLoader; - -import java.io.*; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; +@Mod.EventBusSubscriber(modid = ModernFix.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) public class ModernFixConfig { - private static final Logger LOGGER = LogManager.getLogger("ModernFixConfig"); + private static final ForgeConfigSpec.Builder COMMON_BUILDER = new ForgeConfigSpec.Builder(); + public static ForgeConfigSpec COMMON_CONFIG; - private final Map options = new HashMap<>(); + public static ForgeConfigSpec.ConfigValue> BLACKLIST_ASYNC_JEI_PLUGINS; - private ModernFixConfig() { - // Defines the default rules which can be configured by the user or other mods. - // You must manually add a rule for any new mixins not covered by an existing package rule. - this.addMixinRule("core", true); // TODO: Don't actually allow the user to disable this - this.addMixinRule("feature.measure_time", true); - this.addMixinRule("perf.remove_biome_temperature_cache", true); - this.addMixinRule("perf.resourcepacks", true); - this.addMixinRule("perf.reduce_blockstate_cache_rebuilds", true); - this.addMixinRule("perf.boost_worker_count", true); - this.addMixinRule("perf.skip_first_datapack_reload", true); - this.addMixinRule("perf.parallelize_model_loading", true); - this.addMixinRule("perf.trim_model_caches", true); - this.addMixinRule("bugfix.concurrency", true); - this.addMixinRule("bugfix.edge_chunk_not_saved", true); - this.addMixinRule("perf.async_jei", true); + public static Set jeiPluginBlacklist; + + static { + List empty = Collections.emptyList(); + Predicate locationValidator = o -> o instanceof String && ((String)o).contains(":"); + BLACKLIST_ASYNC_JEI_PLUGINS = COMMON_BUILDER + .comment("These JEI plugins will be loaded on the main thread") + .defineList("blacklist_async_jei_plugins", ImmutableList.of( + "jepb:jei_plugin" + ), locationValidator); } - /** - * Defines a Mixin rule which can be configured by users and other mods. - * @throws IllegalStateException If a rule with that name already exists - * @param mixin The name of the mixin package which will be controlled by this rule - * @param enabled True if the rule will be enabled by default, otherwise false - */ - private void addMixinRule(String mixin, boolean enabled) { - String name = getMixinRuleName(mixin); + static { + COMMON_CONFIG = COMMON_BUILDER.build(); + } - if (this.options.putIfAbsent(name, new Option(name, enabled, false)) != null) { - throw new IllegalStateException("Mixin rule already defined: " + mixin); + @SubscribeEvent + public static void onModConfigEvent(final ModConfig.ModConfigEvent configEvent) { + if (configEvent.getConfig().getSpec() == COMMON_CONFIG) { + bakeConfig(); } } - private void readProperties(Properties props) { - for (Map.Entry entry : props.entrySet()) { - String key = (String) entry.getKey(); - String value = (String) entry.getValue(); - - Option option = this.options.get(key); - - if (option == null) { - LOGGER.warn("No configuration key exists with name '{}', ignoring", key); - continue; - } - - boolean enabled; - - if (value.equalsIgnoreCase("true")) { - enabled = true; - } else if (value.equalsIgnoreCase("false")) { - enabled = false; - } else { - LOGGER.warn("Invalid value '{}' encountered for configuration key '{}', ignoring", value, key); - continue; - } - - option.setEnabled(enabled, true); - } + public static void bakeConfig() { + jeiPluginBlacklist = BLACKLIST_ASYNC_JEI_PLUGINS.get().stream().map(ResourceLocation::new).collect(Collectors.toSet()); } - /** - * Returns the effective option for the specified class name. This traverses the package path of the given mixin - * and checks each root for configuration rules. If a configuration rule disables a package, all mixins located in - * that package and its children will be disabled. The effective option is that of the highest-priority rule, either - * a enable rule at the end of the chain or a disable rule at the earliest point in the chain. - * - * @return Null if no options matched the given mixin name, otherwise the effective option for this Mixin - */ - public Option getEffectiveOptionForMixin(String mixinClassName) { - int lastSplit = 0; - int nextSplit; - - Option rule = null; - - while ((nextSplit = mixinClassName.indexOf('.', lastSplit)) != -1) { - String key = getMixinRuleName(mixinClassName.substring(0, nextSplit)); - - Option candidate = this.options.get(key); - - if (candidate != null) { - rule = candidate; - - if (!rule.isEnabled()) { - return rule; - } - } - - lastSplit = nextSplit + 1; - } - - return rule; - } - - /** - * Loads the configuration file from the specified location. If it does not exist, a new configuration file will be - * created. The file on disk will then be updated to include any new options. - */ - public static ModernFixConfig load(File file) { - ModernFixConfig config = new ModernFixConfig(); - Properties props = new Properties(); - if(file.exists()) { - try (FileInputStream fin = new FileInputStream(file)){ - props.load(fin); - } catch (IOException e) { - throw new RuntimeException("Could not load config file", e); - } - config.readProperties(props); - } - - try { - config.writeConfig(file, props); - } catch (IOException e) { - LOGGER.warn("Could not write configuration file", e); - } - - return config; - } - - private void writeConfig(File file, Properties props) throws IOException { - File dir = file.getParentFile(); - - if (!dir.exists()) { - if (!dir.mkdirs()) { - throw new IOException("Could not create parent directories"); - } - } else if (!dir.isDirectory()) { - throw new IOException("The parent file is not a directory"); - } - - try (Writer writer = new FileWriter(file)) { - writer.write("# This is the configuration file for ModernFix.\n"); - writer.write("#\n"); - writer.write("# The following options are enabled by default and should only be disabled if there is a.\n"); - writer.write("# compatibility issue. Add a line mixin.example_name=false without the # sign to disable a rule.\n"); - List lines = this.options.keySet().stream() - .filter(key -> !key.equals("mixin.core")) - .sorted() - .map(key -> "# " + key + "\n") - .collect(Collectors.toList()); - for(String line : lines) { - writer.write(line); - } - for (Map.Entry entry : props.entrySet()) { - String key = (String) entry.getKey(); - String value = (String) entry.getValue(); - writer.write(key + "=" + value + "\n"); - } - } - } - - private static String getMixinRuleName(String name) { - return "mixin." + name; - } - - public int getOptionCount() { - return this.options.size(); - } - - public int getOptionOverrideCount() { - return (int) this.options.values() - .stream() - .filter(Option::isOverridden) - .count(); - } } diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java new file mode 100644 index 00000000..787c9be9 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -0,0 +1,177 @@ +package org.embeddedt.modernfix.core.config; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.*; +import java.util.*; +import java.util.stream.Collectors; + +public class ModernFixEarlyConfig { + private static final Logger LOGGER = LogManager.getLogger("ModernFixConfig"); + + private final Map options = new HashMap<>(); + + private ModernFixEarlyConfig() { + // Defines the default rules which can be configured by the user or other mods. + // You must manually add a rule for any new mixins not covered by an existing package rule. + this.addMixinRule("core", true); // TODO: Don't actually allow the user to disable this + this.addMixinRule("feature.measure_time", true); + this.addMixinRule("perf.remove_biome_temperature_cache", true); + this.addMixinRule("perf.resourcepacks", true); + this.addMixinRule("perf.reduce_blockstate_cache_rebuilds", true); + this.addMixinRule("perf.boost_worker_count", true); + this.addMixinRule("perf.skip_first_datapack_reload", true); + this.addMixinRule("perf.parallelize_model_loading", true); + this.addMixinRule("perf.trim_model_caches", true); + this.addMixinRule("bugfix.concurrency", true); + this.addMixinRule("bugfix.edge_chunk_not_saved", true); + this.addMixinRule("perf.async_jei", true); + } + + /** + * Defines a Mixin rule which can be configured by users and other mods. + * @throws IllegalStateException If a rule with that name already exists + * @param mixin The name of the mixin package which will be controlled by this rule + * @param enabled True if the rule will be enabled by default, otherwise false + */ + private void addMixinRule(String mixin, boolean enabled) { + String name = getMixinRuleName(mixin); + + if (this.options.putIfAbsent(name, new Option(name, enabled, false)) != null) { + throw new IllegalStateException("Mixin rule already defined: " + mixin); + } + } + + private void readProperties(Properties props) { + for (Map.Entry entry : props.entrySet()) { + String key = (String) entry.getKey(); + String value = (String) entry.getValue(); + + Option option = this.options.get(key); + + if (option == null) { + LOGGER.warn("No configuration key exists with name '{}', ignoring", key); + continue; + } + + boolean enabled; + + if (value.equalsIgnoreCase("true")) { + enabled = true; + } else if (value.equalsIgnoreCase("false")) { + enabled = false; + } else { + LOGGER.warn("Invalid value '{}' encountered for configuration key '{}', ignoring", value, key); + continue; + } + + option.setEnabled(enabled, true); + } + } + + /** + * Returns the effective option for the specified class name. This traverses the package path of the given mixin + * and checks each root for configuration rules. If a configuration rule disables a package, all mixins located in + * that package and its children will be disabled. The effective option is that of the highest-priority rule, either + * a enable rule at the end of the chain or a disable rule at the earliest point in the chain. + * + * @return Null if no options matched the given mixin name, otherwise the effective option for this Mixin + */ + public Option getEffectiveOptionForMixin(String mixinClassName) { + int lastSplit = 0; + int nextSplit; + + Option rule = null; + + while ((nextSplit = mixinClassName.indexOf('.', lastSplit)) != -1) { + String key = getMixinRuleName(mixinClassName.substring(0, nextSplit)); + + Option candidate = this.options.get(key); + + if (candidate != null) { + rule = candidate; + + if (!rule.isEnabled()) { + return rule; + } + } + + lastSplit = nextSplit + 1; + } + + return rule; + } + + /** + * Loads the configuration file from the specified location. If it does not exist, a new configuration file will be + * created. The file on disk will then be updated to include any new options. + */ + public static ModernFixEarlyConfig load(File file) { + ModernFixEarlyConfig config = new ModernFixEarlyConfig(); + Properties props = new Properties(); + if(file.exists()) { + try (FileInputStream fin = new FileInputStream(file)){ + props.load(fin); + } catch (IOException e) { + throw new RuntimeException("Could not load config file", e); + } + config.readProperties(props); + } + + try { + config.writeConfig(file, props); + } catch (IOException e) { + LOGGER.warn("Could not write configuration file", e); + } + + return config; + } + + private void writeConfig(File file, Properties props) throws IOException { + File dir = file.getParentFile(); + + if (!dir.exists()) { + if (!dir.mkdirs()) { + throw new IOException("Could not create parent directories"); + } + } else if (!dir.isDirectory()) { + throw new IOException("The parent file is not a directory"); + } + + try (Writer writer = new FileWriter(file)) { + writer.write("# This is the configuration file for ModernFix.\n"); + writer.write("#\n"); + writer.write("# The following options are enabled by default and should only be disabled if there is a.\n"); + writer.write("# compatibility issue. Add a line mixin.example_name=false without the # sign to disable a rule.\n"); + List lines = this.options.keySet().stream() + .filter(key -> !key.equals("mixin.core")) + .sorted() + .map(key -> "# " + key + "\n") + .collect(Collectors.toList()); + for(String line : lines) { + writer.write(line); + } + for (Map.Entry entry : props.entrySet()) { + String key = (String) entry.getKey(); + String value = (String) entry.getValue(); + writer.write(key + "=" + value + "\n"); + } + } + } + + private static String getMixinRuleName(String name) { + return "mixin." + name; + } + + public int getOptionCount() { + return this.options.size(); + } + + public int getOptionOverrideCount() { + return (int) this.options.values() + .stream() + .filter(Option::isOverridden) + .count(); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/bugfix/concurrency/MinecraftMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/concurrency/MinecraftMixin.java new file mode 100644 index 00000000..e83404d5 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/concurrency/MinecraftMixin.java @@ -0,0 +1,34 @@ +package org.embeddedt.modernfix.mixin.bugfix.concurrency; + +import net.minecraft.client.Minecraft; +import net.minecraft.util.concurrent.ThreadTaskExecutor; +import org.embeddedt.modernfix.ModernFix; +import org.spongepowered.asm.mixin.Mixin; + +import java.util.function.BooleanSupplier; + +@Mixin(Minecraft.class) +public abstract class MinecraftMixin extends ThreadTaskExecutor { + + protected MinecraftMixin(String p_i50403_1_) { + super(p_i50403_1_); + } + + @Override + public void managedBlock(BooleanSupplier pIsDone) { + if(!this.isSameThread()) { + ModernFix.LOGGER.warn("A mod is calling Minecraft.managedBlock from the wrong thread. This is most likely related to one of our parallelizations."); + ModernFix.LOGGER.warn("ModernFix will work around this, however ideally the issue should be patched in the other mod."); + ModernFix.LOGGER.warn("Stacktrace", new IllegalThreadStateException()); + while(!pIsDone.getAsBoolean()) { + try { + Thread.sleep(100); + } catch(InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } else { + super.managedBlock(pIsDone); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/ClientLifecycleHandlerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/ClientLifecycleHandlerMixin.java index 819759fc..a2e0e18d 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/ClientLifecycleHandlerMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/ClientLifecycleHandlerMixin.java @@ -27,17 +27,17 @@ import java.util.List; @Mixin(ClientLifecycleHandler.class) public class ClientLifecycleHandlerMixin { - @Shadow @Final private JeiStarter starter; - @Shadow @Final private List plugins; - @Shadow @Final private Textures textures; - @Shadow @Final private IClientConfig clientConfig; - @Shadow @Final private IEditModeConfig editModeConfig; - @Shadow @Final private IngredientFilterConfig ingredientFilterConfig; - @Shadow @Final private WorldConfig worldConfig; - @Shadow @Final private BookmarkConfig bookmarkConfig; - @Shadow @Final private IModIdHelper modIdHelper; - @Shadow @Final private RecipeCategorySortingConfig recipeCategorySortingConfig; - @Shadow @Final private IIngredientSorter ingredientSorter; + @Shadow(remap = false) @Final private JeiStarter starter; + @Shadow(remap = false) @Final private List plugins; + @Shadow(remap = false) @Final private Textures textures; + @Shadow(remap = false) @Final private IClientConfig clientConfig; + @Shadow(remap = false) @Final private IEditModeConfig editModeConfig; + @Shadow(remap = false) @Final private IngredientFilterConfig ingredientFilterConfig; + @Shadow(remap = false) @Final private WorldConfig worldConfig; + @Shadow(remap = false) @Final private BookmarkConfig bookmarkConfig; + @Shadow(remap = false) @Final private IModIdHelper modIdHelper; + @Shadow(remap = false) @Final private RecipeCategorySortingConfig recipeCategorySortingConfig; + @Shadow(remap = false) @Final private IIngredientSorter ingredientSorter; private volatile Thread reloadThread = null; @Inject(method = "setupJEI", at = @At(value = "INVOKE", target = "Lmezz/jei/startup/ClientLifecycleHandler;startJEI()V"), cancellable = true, remap = false) private void startAsync(CallbackInfo ci) { diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/JeiStarterMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/JeiStarterMixin.java index 4da9dec1..1578a97e 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/JeiStarterMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/JeiStarterMixin.java @@ -26,7 +26,7 @@ import java.util.function.Consumer; @Mixin(JeiStarter.class) public class JeiStarterMixin { - @Shadow private boolean started; + @Shadow(remap = false) private boolean started; @Inject(method = "start", at = @At(value = "INVOKE", target = "Lmezz/jei/util/ErrorUtil;checkNotEmpty(Ljava/util/Collection;Ljava/lang/String;)V", ordinal = 0, shift = At.Shift.AFTER), remap = false) private void setStartedFlag(List plugins, Textures textures, IClientConfig clientConfig, IEditModeConfig editModeConfig, IIngredientFilterConfig ingredientFilterConfig, IWorldConfig worldConfig, BookmarkConfig bookmarkConfig, IModIdHelper modIdHelper, RecipeCategorySortingConfig recipeCategorySortingConfig, IIngredientSorter ingredientSorter, CallbackInfo ci) { diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/PluginCallerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/PluginCallerMixin.java index a6e21979..d089afa2 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/PluginCallerMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/async_jei/PluginCallerMixin.java @@ -2,6 +2,9 @@ package org.embeddedt.modernfix.mixin.perf.async_jei; import mezz.jei.api.IModPlugin; import mezz.jei.load.PluginCaller; +import net.minecraft.client.Minecraft; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.core.config.ModernFixConfig; import org.embeddedt.modernfix.jei.async.IAsyncJeiStarter; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -10,6 +13,8 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletionException; import java.util.function.Consumer; @Mixin(PluginCaller.class) @@ -18,4 +23,16 @@ public class PluginCallerMixin { private static void checkForInterrupt(String title, List plugins, Consumer func, CallbackInfo ci) { IAsyncJeiStarter.checkForLoadInterruption(); } + + @SuppressWarnings({"unchecked","rawtypes"}) + @Redirect(method = "callOnPlugins", at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V"), remap = false) + private static void runOnMainThreadIfNeeded(Consumer instance, Object pluginObj) { + IModPlugin plugin = (IModPlugin)pluginObj; + if(ModernFixConfig.jeiPluginBlacklist.contains(plugin.getPluginUid())) { + ModernFix.LOGGER.warn("Going to main thread for " + plugin.getPluginUid()); + Minecraft.getInstance().executeBlocking(() -> instance.accept(plugin)); + } else { + instance.accept(plugin); + } + } } diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 9748ed78..c201dcfc 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -21,6 +21,7 @@ "client": [ "perf.skip_first_datapack_reload.MinecraftMixin", "bugfix.concurrency.RenderTypeMixin", + "bugfix.concurrency.MinecraftMixin", "perf.parallelize_model_loading.ModelBakeryMixin", "perf.trim_model_caches.ModelManagerMixin", "perf.async_jei.IngredientListElementFactoryMixin",