From 8a443c84c1c8603128c56d4ac36c3b0bac562412 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 12 Mar 2023 19:45:44 -0400 Subject: [PATCH 01/22] Add config to reload JEI asynchronously --- .../common/async/JeiAsyncStartInterrupt.java | 6 +++ .../mezz/jei/common/async/JeiStartTask.java | 46 +++++++++++++++++++ .../mezz/jei/common/config/ClientConfig.java | 11 +++++ .../mezz/jei/common/config/IClientConfig.java | 2 + .../jei/gui/ingredients/IngredientFilter.java | 6 +++ .../mezz/jei/library/load/PluginCaller.java | 2 + .../recipes/RecipeManagerInternal.java | 2 + .../mezz/jei/library/startup/JeiStarter.java | 29 +++++++++++- 8 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 Common/src/main/java/mezz/jei/common/async/JeiAsyncStartInterrupt.java create mode 100644 Common/src/main/java/mezz/jei/common/async/JeiStartTask.java diff --git a/Common/src/main/java/mezz/jei/common/async/JeiAsyncStartInterrupt.java b/Common/src/main/java/mezz/jei/common/async/JeiAsyncStartInterrupt.java new file mode 100644 index 000000000..5911c1990 --- /dev/null +++ b/Common/src/main/java/mezz/jei/common/async/JeiAsyncStartInterrupt.java @@ -0,0 +1,6 @@ +package mezz.jei.common.async; + +final class JeiAsyncStartInterrupt extends Error { + public JeiAsyncStartInterrupt() { + } +} diff --git a/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java b/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java new file mode 100644 index 000000000..983c7edb4 --- /dev/null +++ b/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java @@ -0,0 +1,46 @@ +package mezz.jei.common.async; + +public class JeiStartTask extends Thread { + private final Runnable startTask; + private boolean isCancelled = false; + + public JeiStartTask(Runnable startTask) { + this.startTask = startTask; + this.setName("JEI Start"); + } + + public void interruptStart() { + isCancelled = true; + } + + /** + * Check whether the startup should be interrupted. If this is not running on a JEI startup thread, + * false is returned. + */ + private static boolean isStartInterrupted() { + Thread t = Thread.currentThread(); + if(t instanceof JeiStartTask) { + return ((JeiStartTask)t).isCancelled; + } else + return false; + } + + private static final JeiAsyncStartInterrupt INTERRUPT_START = new JeiAsyncStartInterrupt(); + + public static void checkStartInterruption() { + if(isStartInterrupted()) + forceInterrupt(); + } + + public static void forceInterrupt() { + throw INTERRUPT_START; + } + + @Override + public void run() { + try { + startTask.run(); + } catch(JeiAsyncStartInterrupt ignored) { + } + } +} diff --git a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java index 7da5b5ee5..669f71e0e 100644 --- a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java @@ -17,6 +17,7 @@ public final class ClientConfig implements IClientConfig { private final Supplier centerSearchBarEnabled; private final Supplier lowMemorySlowSearchEnabled; private final Supplier cheatToHotbarUsingHotkeysEnabled; + private final Supplier asyncLoadingEnabled; private final Supplier addBookmarksToFront; private final Supplier giveMode; private final Supplier maxRecipeGuiHeight; @@ -58,6 +59,11 @@ public ClientConfig(IConfigSchemaBuilder schema) { Integer.MAX_VALUE, "Max. recipe gui height" ); + asyncLoadingEnabled = advanced.addBoolean( + "AsyncLoading", + false, + "Whether JEI should load asynchronously" + ); IConfigCategoryBuilder sorting = schema.addCategory("sorting"); ingredientSorterStages = sorting.addList( @@ -92,6 +98,11 @@ public boolean isCheatToHotbarUsingHotkeysEnabled() { return cheatToHotbarUsingHotkeysEnabled.get(); } + @Override + public boolean isAsyncLoadingEnabled() { + return asyncLoadingEnabled.get(); + } + @Override public boolean isAddingBookmarksToFront() { return addBookmarksToFront.get(); diff --git a/Common/src/main/java/mezz/jei/common/config/IClientConfig.java b/Common/src/main/java/mezz/jei/common/config/IClientConfig.java index 63407fc27..973b4a635 100644 --- a/Common/src/main/java/mezz/jei/common/config/IClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/IClientConfig.java @@ -13,6 +13,8 @@ public interface IClientConfig { boolean isCheatToHotbarUsingHotkeysEnabled(); + boolean isAsyncLoadingEnabled(); + boolean isAddingBookmarksToFront(); GiveMode getGiveMode(); diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java index b4cddf555..a18266d34 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java @@ -8,6 +8,7 @@ import mezz.jei.api.ingredients.subtypes.UidContext; import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.api.runtime.IIngredientVisibility; +import mezz.jei.common.async.JeiStartTask; import mezz.jei.common.config.DebugConfig; import mezz.jei.common.util.Translator; import mezz.jei.common.config.IClientConfig; @@ -95,7 +96,12 @@ public IngredientFilter( }); } + /* used to check for interruption periodically */ + private int ingredientNum = 0; + public void addIngredient(IListElementInfo info) { + if(((ingredientNum++) % 100) == 0) + JeiStartTask.checkStartInterruption(); IListElement element = info.getElement(); updateHiddenState(element); diff --git a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java index 7fb0605bc..6750f8dac 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java @@ -2,6 +2,7 @@ import com.google.common.base.Stopwatch; import mezz.jei.api.IModPlugin; +import mezz.jei.common.async.JeiStartTask; import net.minecraft.resources.ResourceLocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -21,6 +22,7 @@ public static void callOnPlugins(String title, List plugins, Consume List erroredPlugins = new ArrayList<>(); for (IModPlugin plugin : plugins) { + JeiStartTask.checkStartInterruption(); try { ResourceLocation pluginUid = plugin.getPluginUid(); timer.begin(title, pluginUid); diff --git a/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java b/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java index a2ead9eff..dcef66de2 100644 --- a/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java +++ b/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java @@ -9,6 +9,7 @@ import mezz.jei.api.recipe.category.IRecipeCategory; import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.api.runtime.IIngredientVisibility; +import mezz.jei.common.async.JeiStartTask; import mezz.jei.common.util.ErrorUtil; import mezz.jei.library.config.RecipeCategorySortingConfig; import mezz.jei.library.ingredients.IIngredientSupplier; @@ -116,6 +117,7 @@ private void addRecipes(RecipeTypeData recipeTypeData, Collection reci if (ingredientSupplier == null) { return false; } + JeiStartTask.checkStartInterruption(); return addRecipe(recipeCategory, recipe, ingredientSupplier); }) .toList(); diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index 5b6cf1a41..22dbbe486 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -11,6 +11,7 @@ import mezz.jei.api.runtime.IIngredientVisibility; import mezz.jei.api.runtime.IScreenHelper; import mezz.jei.common.Internal; +import mezz.jei.common.async.JeiStartTask; import mezz.jei.common.config.ConfigManager; import mezz.jei.common.config.DebugConfig; import mezz.jei.common.config.IClientToggleState; @@ -59,6 +60,8 @@ public final class JeiStarter { private final FileWatcher fileWatcher = new FileWatcher("JEI Config File Watcher"); private final ConfigManager configManager; + private JeiStartTask currentStartTask = null; + public JeiStarter(StartData data) { ErrorUtil.checkNotEmpty(data.plugins(), "plugins"); this.data = data; @@ -96,15 +99,32 @@ public JeiStarter(StartData data) { PluginCaller.callOnPlugins("Sending ConfigManager", plugins, p -> p.onConfigManagerAvailable(configManager)); } + /** + * Starts JEI, either synchronously or asynchronously depending on config. Should only be called from + * the main thread. + */ public void start() { + if(currentStartTask != null) { + LOGGER.error("JEI start requested but it is already starting."); + return; + } Minecraft minecraft = Minecraft.getInstance(); if (minecraft.level == null) { LOGGER.error("Failed to start JEI, there is no Minecraft client level."); return; } + JeiStartTask task = new JeiStartTask(this::doActualStart); + if(Internal.getJeiClientConfigs().getClientConfig().isAsyncLoadingEnabled()) { + currentStartTask = task; + task.start(); + } else { + task.run(); + } + } + private void doActualStart() { LoggedTimer totalTime = new LoggedTimer(); - totalTime.start("Starting JEI"); + totalTime.start("Starting JEI" + ((Thread.currentThread() instanceof JeiStartTask) ? " (asynchronously)" : "")); IColorHelper colorHelper = new ColorHelper(colorNameConfig); @@ -137,7 +157,7 @@ public void start() { ingredientVisibility ); ImmutableTable, RecipeType, IRecipeTransferHandler> recipeTransferHandlers = - pluginLoader.createRecipeTransferHandlers(plugins); + pluginLoader.createRecipeTransferHandlers(plugins); IRecipeTransferManager recipeTransferManager = new RecipeTransferManager(recipeTransferHandlers); LoggedTimer timer = new LoggedTimer(); @@ -179,6 +199,11 @@ public void start() { public void stop() { LOGGER.info("Stopping JEI"); + if(currentStartTask != null) { + currentStartTask.interruptStart(); + Minecraft.getInstance().managedBlock(() -> !currentStartTask.isAlive()); + currentStartTask = null; + } List plugins = data.plugins(); PluginCaller.callOnPlugins("Sending Runtime Unavailable", plugins, IModPlugin::onRuntimeUnavailable); } From 4f575042247e302cd575b39c33be13ddf15fb48f Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 20 Mar 2023 13:08:54 -0400 Subject: [PATCH 02/22] Add task executor system that runs on the end of a client tick --- .../jei/fabric/events/JeiLifecycleEvents.java | 8 +++++ .../mezz/jei/fabric/mixin/MinecraftMixin.java | 7 ++++ .../startup/ClientLifecycleHandler.java | 1 + .../mezz/jei/forge/JustEnoughItemsClient.java | 2 +- .../jei/forge/startup/StartEventObserver.java | 11 +++++- .../mezz/jei/library/startup/JeiStarter.java | 35 +++++++++++++++++++ 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/Fabric/src/main/java/mezz/jei/fabric/events/JeiLifecycleEvents.java b/Fabric/src/main/java/mezz/jei/fabric/events/JeiLifecycleEvents.java index 1bed2f100..e7a8a89f3 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/events/JeiLifecycleEvents.java +++ b/Fabric/src/main/java/mezz/jei/fabric/events/JeiLifecycleEvents.java @@ -36,6 +36,14 @@ public class JeiLifecycleEvents { } }); + public static final Event CLIENT_TICK_END = + EventFactory.createArrayBacked(Runnable.class, callbacks -> () -> { + for (Runnable callback : callbacks) { + callback.run(); + } + }); + + @Environment(EnvType.CLIENT) @FunctionalInterface public interface RegisterResourceReloadListener { diff --git a/Fabric/src/main/java/mezz/jei/fabric/mixin/MinecraftMixin.java b/Fabric/src/main/java/mezz/jei/fabric/mixin/MinecraftMixin.java index 265dadaf4..14c23eeea 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/mixin/MinecraftMixin.java +++ b/Fabric/src/main/java/mezz/jei/fabric/mixin/MinecraftMixin.java @@ -48,4 +48,11 @@ public void beforeInitialResourceReload(GameConfig gameConfig, CallbackInfo ci) public void clearLevel(Screen screen, CallbackInfo ci) { JeiLifecycleEvents.GAME_STOP.invoker().run(); } + @Inject( + method = "tick", + at = @At("TAIL") + ) + private void jeiOnTickEnd(CallbackInfo ci) { + JeiLifecycleEvents.CLIENT_TICK_END.invoker().run(); + } } diff --git a/Fabric/src/main/java/mezz/jei/fabric/startup/ClientLifecycleHandler.java b/Fabric/src/main/java/mezz/jei/fabric/startup/ClientLifecycleHandler.java index f0dc5ec74..83bfc531c 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/startup/ClientLifecycleHandler.java +++ b/Fabric/src/main/java/mezz/jei/fabric/startup/ClientLifecycleHandler.java @@ -54,6 +54,7 @@ public void registerEvents() { }) ); JeiLifecycleEvents.GAME_STOP.register(this::stopJei); + JeiLifecycleEvents.CLIENT_TICK_END.register(this.jeiStarter::tick); } public ResourceManagerReloadListener getReloadListener() { diff --git a/Forge/src/main/java/mezz/jei/forge/JustEnoughItemsClient.java b/Forge/src/main/java/mezz/jei/forge/JustEnoughItemsClient.java index 69e20c218..5ac2a0f50 100644 --- a/Forge/src/main/java/mezz/jei/forge/JustEnoughItemsClient.java +++ b/Forge/src/main/java/mezz/jei/forge/JustEnoughItemsClient.java @@ -50,7 +50,7 @@ public JustEnoughItemsClient( JeiStarter jeiStarter = new JeiStarter(startData); - this.startEventObserver = new StartEventObserver(jeiStarter::start, jeiStarter::stop); + this.startEventObserver = new StartEventObserver(jeiStarter::start, jeiStarter::stop, jeiStarter::tick); this.startEventObserver.register(subscriptions); } diff --git a/Forge/src/main/java/mezz/jei/forge/startup/StartEventObserver.java b/Forge/src/main/java/mezz/jei/forge/startup/StartEventObserver.java index 13ea02768..67b453bc5 100644 --- a/Forge/src/main/java/mezz/jei/forge/startup/StartEventObserver.java +++ b/Forge/src/main/java/mezz/jei/forge/startup/StartEventObserver.java @@ -10,6 +10,7 @@ import net.minecraftforge.client.event.RecipesUpdatedEvent; import net.minecraftforge.client.event.ScreenEvent; import net.minecraftforge.event.TagsUpdatedEvent; +import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.Event; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -37,11 +38,13 @@ private enum State { private final Set> observedEvents = new HashSet<>(); private final Runnable startRunnable; private final Runnable stopRunnable; + private final Runnable tickRunnable; private State state = State.DISABLED; - public StartEventObserver(Runnable startRunnable, Runnable stopRunnable) { + public StartEventObserver(Runnable startRunnable, Runnable stopRunnable, Runnable tickRunnable) { this.startRunnable = startRunnable; this.stopRunnable = stopRunnable; + this.tickRunnable = tickRunnable; } public void register(PermanentEventSubscriptions subscriptions) { @@ -79,6 +82,12 @@ public void register(PermanentEventSubscriptions subscriptions) { } } }); + + subscriptions.register(TickEvent.ClientTickEvent.class, event -> { + if(event.phase == TickEvent.Phase.END && this.state == State.JEI_STARTED) { + this.tickRunnable.run(); + } + }); } /** diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index 22dbbe486..5434bc5a5 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -1,6 +1,7 @@ package mezz.jei.library.startup; import com.google.common.collect.ImmutableTable; +import com.google.common.util.concurrent.MoreExecutors; import mezz.jei.api.IModPlugin; import mezz.jei.api.helpers.IColorHelper; import mezz.jei.api.helpers.IModIdHelper; @@ -43,9 +44,13 @@ import net.minecraft.world.inventory.AbstractContainerMenu; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import java.nio.file.Path; import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; public final class JeiStarter { private static final Logger LOGGER = LogManager.getLogger(); @@ -59,6 +64,7 @@ public final class JeiStarter { @SuppressWarnings("FieldCanBeLocal") private final FileWatcher fileWatcher = new FileWatcher("JEI Config File Watcher"); private final ConfigManager configManager; + private Executor taskExecutor; private JeiStartTask currentStartTask = null; @@ -116,8 +122,10 @@ public void start() { JeiStartTask task = new JeiStartTask(this::doActualStart); if(Internal.getJeiClientConfigs().getClientConfig().isAsyncLoadingEnabled()) { currentStartTask = task; + this.taskExecutor = new ClientTaskExecutor(); task.start(); } else { + this.taskExecutor = MoreExecutors.directExecutor(); task.run(); } } @@ -207,4 +215,31 @@ public void stop() { List plugins = data.plugins(); PluginCaller.callOnPlugins("Sending Runtime Unavailable", plugins, IModPlugin::onRuntimeUnavailable); } + + static final class ClientTaskExecutor implements Executor { + private static final long TICK_BUDGET = TimeUnit.MILLISECONDS.toNanos(2); + + final ConcurrentLinkedQueue startTasks = new ConcurrentLinkedQueue<>(); + + public void tick() { + long startTime = System.nanoTime(); + do { + Runnable r = this.startTasks.poll(); + if(r != null) + r.run(); + else + break; + } while((System.nanoTime() - startTime) < TICK_BUDGET); + } + + @Override + public void execute(@NotNull Runnable runnable) { + this.startTasks.add(runnable); + } + } + + public void tick() { + if(this.taskExecutor instanceof ClientTaskExecutor) + ((ClientTaskExecutor)this.taskExecutor).tick(); + } } From f73ff520941559af423cba53eaa08ca9071be06a Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 20 Mar 2023 16:25:04 -0400 Subject: [PATCH 03/22] Add executor to IRuntimeRegistration, and modify ingredient filter to cache the tooltips using it --- .../registration/IRuntimeRegistration.java | 7 +++++ .../mezz/jei/test/IngredientFilterTest.java | 4 ++- .../jei/gui/ingredients/IListElementInfo.java | 4 +++ .../jei/gui/ingredients/IngredientFilter.java | 18 +++++++++-- .../jei/gui/ingredients/ListElementInfo.java | 31 ++++++++++++++----- .../mezz/jei/gui/startup/JeiGuiStarter.java | 3 +- .../registration/RuntimeRegistration.java | 12 ++++++- .../mezz/jei/library/startup/JeiStarter.java | 3 +- 8 files changed, 68 insertions(+), 14 deletions(-) diff --git a/CommonApi/src/main/java/mezz/jei/api/registration/IRuntimeRegistration.java b/CommonApi/src/main/java/mezz/jei/api/registration/IRuntimeRegistration.java index d731cb2e2..7ce3a30ab 100644 --- a/CommonApi/src/main/java/mezz/jei/api/registration/IRuntimeRegistration.java +++ b/CommonApi/src/main/java/mezz/jei/api/registration/IRuntimeRegistration.java @@ -12,6 +12,8 @@ import mezz.jei.api.runtime.IRecipesGui; import mezz.jei.api.runtime.IScreenHelper; +import java.util.concurrent.Executor; + /** * Allows mods to override the runtime classes for JEI with their own implementation. * @@ -88,4 +90,9 @@ public interface IRuntimeRegistration { * This is used by JEI's GUI and can be used by other mods that want to use the same information from JEI. */ IEditModeConfig getEditModeConfig(); + + /** + * Get access to the client executor, which budgets. running background tasks on the main thread + */ + Executor getClientExecutor(); } diff --git a/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java b/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java index 9f5fba368..e32fadb31 100644 --- a/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java +++ b/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java @@ -1,5 +1,6 @@ package mezz.jei.test; +import com.google.common.util.concurrent.MoreExecutors; import mezz.jei.api.helpers.IColorHelper; import mezz.jei.api.helpers.IModIdHelper; import mezz.jei.api.ingredients.IIngredientRenderer; @@ -95,7 +96,8 @@ public void setup() { baseList, modIdHelper, ingredientVisibility, - colorHelper + colorHelper, + MoreExecutors.directExecutor() ); this.ingredientManager.registerIngredientListener(ingredientFilter); diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java index 0eb89b0e7..1e59bdcb8 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java @@ -3,6 +3,8 @@ import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.stream.Stream; import mezz.jei.api.ingredients.ITypedIngredient; @@ -38,4 +40,6 @@ public interface IListElementInfo { int getSortedIndex(); + CompletableFuture cacheTooltips(IIngredientFilterConfig config, IIngredientManager ingredientManager, Executor clientExecutor); + } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java index a18266d34..5de77e468 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java @@ -35,9 +35,12 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import java.util.stream.Stream; public class IngredientFilter implements IIngredientGridSource, IIngredientManager.IIngredientListener { @@ -68,7 +71,8 @@ public IngredientFilter( NonNullList> ingredients, IModIdHelper modIdHelper, IIngredientVisibility ingredientVisibility, - IColorHelper colorHelper + IColorHelper colorHelper, + Executor clientExecutor ) { this.filterTextSource = filterTextSource; this.ingredientManager = ingredientManager; @@ -84,10 +88,18 @@ public IngredientFilter( } LOGGER.info("Adding {} ingredients", ingredients.size()); - ingredients.stream() + List> elementInfos = ingredients.stream() .map(i -> ListElementInfo.create(i, ingredientManager, modIdHelper)) .flatMap(Optional::stream) - .forEach(this::addIngredient); + .collect(Collectors.toList()); + List> futures = new ArrayList<>(); + for(IListElementInfo elementInfo : elementInfos) { + futures.add(elementInfo.cacheTooltips(config, ingredientManager, clientExecutor)); + } + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); + for(IListElementInfo elementInfo : elementInfos) { + this.addIngredient(elementInfo); + } LOGGER.info("Added {} ingredients", ingredients.size()); this.filterTextSource.addListener(filterText -> { diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java b/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java index 7239fd90b..1ed48e5d4 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java @@ -1,5 +1,6 @@ package mezz.jei.gui.ingredients; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import mezz.jei.api.helpers.IModIdHelper; import mezz.jei.api.ingredients.IIngredientHelper; @@ -19,6 +20,8 @@ import java.util.Locale; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -32,6 +35,7 @@ public class ListElementInfo implements IListElementInfo { private final List modNames; private final ResourceLocation resourceLocation; private int sortedIndex = Integer.MAX_VALUE; + private List tooltipCache = null; public static Optional> create(IListElement element, IIngredientManager ingredientManager, IModIdHelper modIdHelper) { ITypedIngredient value = element.getTypedIngredient(); @@ -101,13 +105,18 @@ private static void addModNameStrings(Set modNames, String modId, String @Override public final List getTooltipStrings(IIngredientFilterConfig config, IIngredientManager ingredientManager) { - String modName = this.modNames.get(0); - String modId = this.modIds.get(0); - String modNameLowercase = modName.toLowerCase(Locale.ENGLISH); - ITypedIngredient value = element.getTypedIngredient(); - IIngredientRenderer ingredientRenderer = ingredientManager.getIngredientRenderer(value.getType()); - ImmutableSet toRemove = ImmutableSet.of(modId, modNameLowercase, displayNameLowercase, resourceLocation.getPath()); - return IngredientInformationUtil.getTooltipStrings(value.getIngredient(), ingredientRenderer, toRemove, config); + if(this.tooltipCache == null) { + String modName = this.modNames.get(0); + String modId = this.modIds.get(0); + String modNameLowercase = modName.toLowerCase(Locale.ENGLISH); + ITypedIngredient value = element.getTypedIngredient(); + IIngredientRenderer ingredientRenderer = ingredientManager.getIngredientRenderer(value.getType()); + ImmutableSet toRemove = ImmutableSet.of(modId, modNameLowercase, displayNameLowercase, resourceLocation.getPath()); + // use ImmutableList to automatically deduplicate empty lists to the singleton empty list + this.tooltipCache = ImmutableList.copyOf(IngredientInformationUtil.getTooltipStrings(value.getIngredient(), + ingredientRenderer, toRemove, config)); + } + return this.tooltipCache; } @Override @@ -159,4 +168,12 @@ public int getSortedIndex() { return sortedIndex; } + @Override + public CompletableFuture cacheTooltips(IIngredientFilterConfig config, IIngredientManager ingredientManager, + Executor clientExecutor) { + if(this.tooltipCache == null) { + return CompletableFuture.runAsync(() -> this.getTooltipStrings(config, ingredientManager), clientExecutor); + } else + return CompletableFuture.completedFuture(null); + } } diff --git a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java index 3831ce349..9d954fdaf 100644 --- a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java +++ b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java @@ -114,7 +114,8 @@ public static JeiEventHandlers start(IRuntimeRegistration registration) { ingredientList, modIdHelper, ingredientVisibility, - colorHelper + colorHelper, + registration.getClientExecutor() ); ingredientManager.registerIngredientListener(ingredientFilter); ingredientVisibility.registerListener(ingredientFilter::onIngredientVisibilityChanged); diff --git a/Library/src/main/java/mezz/jei/library/load/registration/RuntimeRegistration.java b/Library/src/main/java/mezz/jei/library/load/registration/RuntimeRegistration.java index 8c330f9d3..b625ffdc1 100644 --- a/Library/src/main/java/mezz/jei/library/load/registration/RuntimeRegistration.java +++ b/Library/src/main/java/mezz/jei/library/load/registration/RuntimeRegistration.java @@ -17,6 +17,8 @@ import mezz.jei.library.gui.recipes.RecipesGuiDummy; import mezz.jei.library.ingredients.IngredientFilterApiDummy; +import java.util.concurrent.Executor; + public class RuntimeRegistration implements IRuntimeRegistration { private final IRecipeManager recipeManager; private final IJeiHelpers jeiHelpers; @@ -25,6 +27,7 @@ public class RuntimeRegistration implements IRuntimeRegistration { private final IIngredientVisibility ingredientVisibility; private final IRecipeTransferManager recipeTransferManager; private final IScreenHelper screenHelper; + private final Executor clientExecutor; private IIngredientListOverlay ingredientListOverlay = IngredientListOverlayDummy.INSTANCE; private IBookmarkOverlay bookmarkOverlay = BookmarkOverlayDummy.INSTANCE; @@ -38,7 +41,8 @@ public RuntimeRegistration( IIngredientManager ingredientManager, IIngredientVisibility ingredientVisibility, IRecipeTransferManager recipeTransferManager, - IScreenHelper screenHelper + IScreenHelper screenHelper, + Executor clientExecutor ) { this.recipeManager = recipeManager; this.jeiHelpers = jeiHelpers; @@ -47,6 +51,7 @@ public RuntimeRegistration( this.ingredientVisibility = ingredientVisibility; this.recipeTransferManager = recipeTransferManager; this.screenHelper = screenHelper; + this.clientExecutor = clientExecutor; } @Override @@ -119,4 +124,9 @@ public IRecipesGui getRecipesGui() { public IIngredientFilter getIngredientFilter() { return this.ingredientFilter; } + + @Override + public Executor getClientExecutor() { + return this.clientExecutor; + } } diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index 5434bc5a5..ef2952e2d 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -179,7 +179,8 @@ private void doActualStart() { ingredientManager, ingredientVisibility, recipeTransferManager, - screenHelper + screenHelper, + taskExecutor ); PluginCaller.callOnPlugins("Registering Runtime", plugins, p -> p.registerRuntime(runtimeRegistration)); From 2e07a44f80b57030249cd193a8b3ee28f834b485 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 20 Mar 2023 20:23:06 -0400 Subject: [PATCH 04/22] Add sanity check for tasks enqueued on main thread --- .../src/main/java/mezz/jei/library/startup/JeiStarter.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index ef2952e2d..8e713a930 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -235,7 +235,11 @@ public void tick() { @Override public void execute(@NotNull Runnable runnable) { - this.startTasks.add(runnable); + // sanity check, in case a task is submitted from the main thread to the main thread + if(Minecraft.getInstance().isSameThread()) + runnable.run(); + else + this.startTasks.add(runnable); } } From d4ce812d4e872ba1d722fd96437fbd0e4ea88281 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 20 Mar 2023 20:49:19 -0400 Subject: [PATCH 05/22] Add ability to configure specific JEI plugins to run on main thread --- .../mezz/jei/common/config/ClientConfig.java | 12 ++++++ .../mezz/jei/common/config/IClientConfig.java | 1 + .../file/serializers/StringSerializer.java | 37 +++++++++++++++++++ .../main/java/mezz/jei/api/IModPlugin.java | 14 +++++++ .../mezz/jei/library/load/PluginCaller.java | 18 ++++++++- .../mezz/jei/library/startup/JeiStarter.java | 2 + 6 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 Common/src/main/java/mezz/jei/common/config/file/serializers/StringSerializer.java diff --git a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java index 669f71e0e..0c14fa478 100644 --- a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java @@ -5,6 +5,7 @@ import mezz.jei.common.config.file.IConfigSchemaBuilder; import mezz.jei.common.config.file.serializers.EnumSerializer; import mezz.jei.common.config.file.serializers.ListSerializer; +import mezz.jei.common.config.file.serializers.StringSerializer; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -18,6 +19,7 @@ public final class ClientConfig implements IClientConfig { private final Supplier lowMemorySlowSearchEnabled; private final Supplier cheatToHotbarUsingHotkeysEnabled; private final Supplier asyncLoadingEnabled; + private final Supplier> mainThreadPluginUids; private final Supplier addBookmarksToFront; private final Supplier giveMode; private final Supplier maxRecipeGuiHeight; @@ -64,6 +66,11 @@ public ClientConfig(IConfigSchemaBuilder schema) { false, "Whether JEI should load asynchronously" ); + mainThreadPluginUids = advanced.addList("AsyncPluginCompat", + List.of("namespace:mod"), + new ListSerializer<>(new StringSerializer()), + "List of plugin UIDs that should be loaded on the main thread" + ); IConfigCategoryBuilder sorting = schema.addCategory("sorting"); ingredientSorterStages = sorting.addList( @@ -103,6 +110,11 @@ public boolean isAsyncLoadingEnabled() { return asyncLoadingEnabled.get(); } + @Override + public List getAsyncCompatPluginUids() { + return mainThreadPluginUids.get(); + } + @Override public boolean isAddingBookmarksToFront() { return addBookmarksToFront.get(); diff --git a/Common/src/main/java/mezz/jei/common/config/IClientConfig.java b/Common/src/main/java/mezz/jei/common/config/IClientConfig.java index 973b4a635..a00e3f01e 100644 --- a/Common/src/main/java/mezz/jei/common/config/IClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/IClientConfig.java @@ -14,6 +14,7 @@ public interface IClientConfig { boolean isCheatToHotbarUsingHotkeysEnabled(); boolean isAsyncLoadingEnabled(); + List getAsyncCompatPluginUids(); boolean isAddingBookmarksToFront(); diff --git a/Common/src/main/java/mezz/jei/common/config/file/serializers/StringSerializer.java b/Common/src/main/java/mezz/jei/common/config/file/serializers/StringSerializer.java new file mode 100644 index 000000000..410062947 --- /dev/null +++ b/Common/src/main/java/mezz/jei/common/config/file/serializers/StringSerializer.java @@ -0,0 +1,37 @@ +package mezz.jei.common.config.file.serializers; + +import mezz.jei.api.runtime.config.IJeiConfigValueSerializer; + +import java.util.Collection; +import java.util.Optional; + +public class StringSerializer implements IJeiConfigValueSerializer { + @Override + public String serialize(String value) { + return value; + } + + @Override + public IDeserializeResult deserialize(String string) { + string = string.trim(); + if (string.startsWith("\"") && string.endsWith("\"")) { + string = string.substring(1, string.length() - 1); + }; + return new DeserializeResult<>(string); + } + + @Override + public boolean isValid(String value) { + return true; + } + + @Override + public Optional> getAllValidValues() { + return Optional.empty(); + } + + @Override + public String getValidValuesDescription() { + return ""; + } +} diff --git a/CommonApi/src/main/java/mezz/jei/api/IModPlugin.java b/CommonApi/src/main/java/mezz/jei/api/IModPlugin.java index eda5cae04..f55f0994a 100644 --- a/CommonApi/src/main/java/mezz/jei/api/IModPlugin.java +++ b/CommonApi/src/main/java/mezz/jei/api/IModPlugin.java @@ -16,6 +16,8 @@ import mezz.jei.api.registration.IVanillaCategoryExtensionRegistration; import mezz.jei.api.runtime.IJeiRuntime; +import java.util.EnumSet; + /** * The main class to implement to create a JEI plugin. Everything communicated between a mod and JEI is through this class. * IModPlugins must have the {@link JeiPlugin} annotation to get loaded by JEI. @@ -137,4 +139,16 @@ default void onRuntimeUnavailable() { default void onConfigManagerAvailable(IJeiConfigManager configManager) { } + + /** + * Called to find out whether this plugin wants to load on the main thread (legacy behavior), instead of the async + * loading thread. + *

+ * Most plugins should use Minecraft.getInstance().executeBlocking() for their purposes, as plugins loading on the + * main thread will cause lag spikes. + * @since TODO + */ + default boolean needsLoadingOnClientThread() { + return false; + } } diff --git a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java index 6750f8dac..761155181 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java @@ -2,13 +2,19 @@ import com.google.common.base.Stopwatch; import mezz.jei.api.IModPlugin; +import mezz.jei.common.Internal; import mezz.jei.common.async.JeiStartTask; +import mezz.jei.library.startup.JeiStarter; +import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; public class PluginCaller { @@ -26,7 +32,10 @@ public static void callOnPlugins(String title, List plugins, Consume try { ResourceLocation pluginUid = plugin.getPluginUid(); timer.begin(title, pluginUid); - func.accept(plugin); + if(plugin.needsLoadingOnClientThread() || isLegacyPlugin(plugin.getPluginUid())) { + Minecraft.getInstance().executeBlocking(() -> func.accept(plugin)); + } else + func.accept(plugin); timer.end(); } catch (RuntimeException | LinkageError e) { LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), plugin.getPluginUid(), e); @@ -38,4 +47,11 @@ public static void callOnPlugins(String title, List plugins, Consume LOGGER.info("{} took {}", title, stopwatch); } + + private static boolean isLegacyPlugin(ResourceLocation uid) { + String uidString = uid.toString(); + // scales poorly if there are many plugins, but there shouldn't be as modders should support this mode + // in the worst case, this can be cached + return Internal.getJeiClientConfigs().getClientConfig().getAsyncCompatPluginUids().contains(uidString); + } } diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index 8e713a930..6f09db5db 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -41,6 +41,7 @@ import mezz.jei.library.runtime.JeiHelpers; import mezz.jei.library.runtime.JeiRuntime; import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.AbstractContainerMenu; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -48,6 +49,7 @@ import java.nio.file.Path; import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; From d29f535b94901df943dfdaff1bb698f9151593c8 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 14 Apr 2023 17:46:36 -0400 Subject: [PATCH 06/22] Move executor to IJeiHelpers --- .../main/java/mezz/jei/api/helpers/IJeiHelpers.java | 6 ++++++ .../jei/api/registration/IRuntimeRegistration.java | 7 ------- .../java/mezz/jei/gui/startup/JeiGuiStarter.java | 2 +- .../java/mezz/jei/library/load/PluginLoader.java | 5 +++-- .../load/registration/RuntimeRegistration.java | 12 +----------- .../java/mezz/jei/library/runtime/JeiHelpers.java | 11 ++++++++++- .../java/mezz/jei/library/startup/JeiStarter.java | 5 ++--- 7 files changed, 23 insertions(+), 25 deletions(-) diff --git a/CommonApi/src/main/java/mezz/jei/api/helpers/IJeiHelpers.java b/CommonApi/src/main/java/mezz/jei/api/helpers/IJeiHelpers.java index d4c6f5899..5475056f8 100644 --- a/CommonApi/src/main/java/mezz/jei/api/helpers/IJeiHelpers.java +++ b/CommonApi/src/main/java/mezz/jei/api/helpers/IJeiHelpers.java @@ -7,6 +7,7 @@ import net.minecraft.resources.ResourceLocation; import java.util.Optional; +import java.util.concurrent.Executor; /** * {@link IJeiHelpers} provides helpers and tools for addon mods. @@ -67,4 +68,9 @@ public interface IJeiHelpers { * @since 11.5.0 */ IIngredientManager getIngredientManager(); + + /** + * Get access to the client executor, which budgets running background tasks on the main thread. + */ + Executor getClientExecutor(); } diff --git a/CommonApi/src/main/java/mezz/jei/api/registration/IRuntimeRegistration.java b/CommonApi/src/main/java/mezz/jei/api/registration/IRuntimeRegistration.java index 7ce3a30ab..d731cb2e2 100644 --- a/CommonApi/src/main/java/mezz/jei/api/registration/IRuntimeRegistration.java +++ b/CommonApi/src/main/java/mezz/jei/api/registration/IRuntimeRegistration.java @@ -12,8 +12,6 @@ import mezz.jei.api.runtime.IRecipesGui; import mezz.jei.api.runtime.IScreenHelper; -import java.util.concurrent.Executor; - /** * Allows mods to override the runtime classes for JEI with their own implementation. * @@ -90,9 +88,4 @@ public interface IRuntimeRegistration { * This is used by JEI's GUI and can be used by other mods that want to use the same information from JEI. */ IEditModeConfig getEditModeConfig(); - - /** - * Get access to the client executor, which budgets. running background tasks on the main thread - */ - Executor getClientExecutor(); } diff --git a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java index 9d954fdaf..8cd205604 100644 --- a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java +++ b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java @@ -115,7 +115,7 @@ public static JeiEventHandlers start(IRuntimeRegistration registration) { modIdHelper, ingredientVisibility, colorHelper, - registration.getClientExecutor() + jeiHelpers.getClientExecutor() ); ingredientManager.registerIngredientListener(ingredientFilter); ingredientVisibility.registerListener(ingredientFilter::onIngredientVisibilityChanged); diff --git a/Library/src/main/java/mezz/jei/library/load/PluginLoader.java b/Library/src/main/java/mezz/jei/library/load/PluginLoader.java index 7a914783b..671c8f26e 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginLoader.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginLoader.java @@ -51,6 +51,7 @@ import org.jetbrains.annotations.Unmodifiable; import java.util.List; +import java.util.concurrent.Executor; public class PluginLoader { private final StartData data; @@ -58,7 +59,7 @@ public class PluginLoader { private final IIngredientManager ingredientManager; private final JeiHelpers jeiHelpers; - public PluginLoader(StartData data, IModIdFormatConfig modIdFormatConfig, IColorHelper colorHelper) { + public PluginLoader(StartData data, IModIdFormatConfig modIdFormatConfig, IColorHelper colorHelper, Executor clientExecutor) { this.data = data; this.timer = new LoggedTimer(); @@ -80,7 +81,7 @@ public PluginLoader(StartData data, IModIdFormatConfig modIdFormatConfig, IColor GuiHelper guiHelper = new GuiHelper(ingredientManager); FocusFactory focusFactory = new FocusFactory(ingredientManager); IModIdHelper modIdHelper = new ModIdHelper(modIdFormatConfig, ingredientManager); - this.jeiHelpers = new JeiHelpers(guiHelper, stackHelper, modIdHelper, focusFactory, colorHelper, ingredientManager); + this.jeiHelpers = new JeiHelpers(guiHelper, stackHelper, modIdHelper, focusFactory, colorHelper, ingredientManager, clientExecutor); } @Unmodifiable diff --git a/Library/src/main/java/mezz/jei/library/load/registration/RuntimeRegistration.java b/Library/src/main/java/mezz/jei/library/load/registration/RuntimeRegistration.java index b625ffdc1..8c330f9d3 100644 --- a/Library/src/main/java/mezz/jei/library/load/registration/RuntimeRegistration.java +++ b/Library/src/main/java/mezz/jei/library/load/registration/RuntimeRegistration.java @@ -17,8 +17,6 @@ import mezz.jei.library.gui.recipes.RecipesGuiDummy; import mezz.jei.library.ingredients.IngredientFilterApiDummy; -import java.util.concurrent.Executor; - public class RuntimeRegistration implements IRuntimeRegistration { private final IRecipeManager recipeManager; private final IJeiHelpers jeiHelpers; @@ -27,7 +25,6 @@ public class RuntimeRegistration implements IRuntimeRegistration { private final IIngredientVisibility ingredientVisibility; private final IRecipeTransferManager recipeTransferManager; private final IScreenHelper screenHelper; - private final Executor clientExecutor; private IIngredientListOverlay ingredientListOverlay = IngredientListOverlayDummy.INSTANCE; private IBookmarkOverlay bookmarkOverlay = BookmarkOverlayDummy.INSTANCE; @@ -41,8 +38,7 @@ public RuntimeRegistration( IIngredientManager ingredientManager, IIngredientVisibility ingredientVisibility, IRecipeTransferManager recipeTransferManager, - IScreenHelper screenHelper, - Executor clientExecutor + IScreenHelper screenHelper ) { this.recipeManager = recipeManager; this.jeiHelpers = jeiHelpers; @@ -51,7 +47,6 @@ public RuntimeRegistration( this.ingredientVisibility = ingredientVisibility; this.recipeTransferManager = recipeTransferManager; this.screenHelper = screenHelper; - this.clientExecutor = clientExecutor; } @Override @@ -124,9 +119,4 @@ public IRecipesGui getRecipesGui() { public IIngredientFilter getIngredientFilter() { return this.ingredientFilter; } - - @Override - public Executor getClientExecutor() { - return this.clientExecutor; - } } diff --git a/Library/src/main/java/mezz/jei/library/runtime/JeiHelpers.java b/Library/src/main/java/mezz/jei/library/runtime/JeiHelpers.java index d3452e9c1..d67103163 100644 --- a/Library/src/main/java/mezz/jei/library/runtime/JeiHelpers.java +++ b/Library/src/main/java/mezz/jei/library/runtime/JeiHelpers.java @@ -18,6 +18,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Optional; +import java.util.concurrent.Executor; public class JeiHelpers implements IJeiHelpers { private final GuiHelper guiHelper; @@ -27,6 +28,7 @@ public class JeiHelpers implements IJeiHelpers { private final IColorHelper colorHelper; private final IIngredientManager ingredientManager; private final IPlatformFluidHelper platformFluidHelper; + private final Executor clientExecutor; private @Nullable Collection> recipeCategories; public JeiHelpers( @@ -35,7 +37,8 @@ public JeiHelpers( IModIdHelper modIdHelper, IFocusFactory focusFactory, IColorHelper colorHelper, - IIngredientManager ingredientManager + IIngredientManager ingredientManager, + Executor clientExecutor ) { this.guiHelper = guiHelper; this.stackHelper = stackHelper; @@ -44,6 +47,7 @@ public JeiHelpers( this.colorHelper = colorHelper; this.ingredientManager = ingredientManager; this.platformFluidHelper = Services.PLATFORM.getFluidHelper(); + this.clientExecutor = clientExecutor; } public void setRecipeCategories(Collection> recipeCategories) { @@ -94,4 +98,9 @@ public Optional> getRecipeType(ResourceLocation uid) { public IIngredientManager getIngredientManager() { return ingredientManager; } + + @Override + public Executor getClientExecutor() { + return clientExecutor; + } } diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index 6f09db5db..9866f5d99 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -140,7 +140,7 @@ private void doActualStart() { IClientToggleState toggleState = Internal.getClientToggleState(); - PluginLoader pluginLoader = new PluginLoader(data, modIdFormatConfig, colorHelper); + PluginLoader pluginLoader = new PluginLoader(data, modIdFormatConfig, colorHelper, taskExecutor); JeiHelpers jeiHelpers = pluginLoader.getJeiHelpers(); IModIdHelper modIdHelper = jeiHelpers.getModIdHelper(); @@ -181,8 +181,7 @@ private void doActualStart() { ingredientManager, ingredientVisibility, recipeTransferManager, - screenHelper, - taskExecutor + screenHelper ); PluginCaller.callOnPlugins("Registering Runtime", plugins, p -> p.registerRuntime(runtimeRegistration)); From 6b33ae24c8c1644b603f0f2300a4747c0cb95e6b Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 14 May 2023 23:47:08 -0700 Subject: [PATCH 07/22] WIP async loading --- .../mezz/jei/common/async/JeiStartTask.java | 40 ++-- .../mezz/jei/common/config/ClientConfig.java | 23 +- .../mezz/jei/common/config/IClientConfig.java | 5 +- .../file/serializers/StringSerializer.java | 37 --- .../java/mezz/jei/common/util/ErrorUtil.java | 14 -- .../java/mezz/jei/api/IAsyncModPlugin.java | 144 ++++++++++++ .../main/java/mezz/jei/api/IModPlugin.java | 23 +- .../java/mezz/jei/api/IRuntimePlugin.java | 46 ++++ .../java/mezz/jei/api/JeiAsyncPlugin.java | 9 + .../java/mezz/jei/api/JeiRuntimePlugin.java | 9 + .../mezz/jei/api/helpers/IJeiHelpers.java | 6 - .../jei/api/runtime/IJeiClientExecutor.java | 11 + .../EffectRenderingInventoryScreenMixin.java | 4 +- .../mezz/jei/fabric/mixin/MinecraftMixin.java | 1 + ...uiPlugin.java => FabricRuntimePlugin.java} | 25 +- .../startup/ClientLifecycleHandler.java | 9 +- .../jei/fabric/startup/EventRegistration.java | 4 + .../fabric/startup/FabricPluginFinder.java | 18 +- Fabric/src/main/resources/fabric.mod.json | 10 +- .../mezz/jei/forge/JustEnoughItemsClient.java | 8 +- ...GuiPlugin.java => ForgeRuntimePlugin.java} | 26 ++- .../jei/forge/startup/ForgePluginFinder.java | 22 +- .../mezz/jei/test/IngredientFilterTest.java | 10 +- .../mezz/jei/test/lib/TestClientConfig.java | 10 + .../jei/gui/ingredients/IListElementInfo.java | 5 - .../jei/gui/ingredients/IngredientFilter.java | 55 +++-- .../jei/gui/ingredients/ListElementInfo.java | 34 +-- .../mezz/jei/gui/startup/JeiGuiStarter.java | 215 +++++++++--------- .../ingredients/IngredientManager.java | 8 +- .../mezz/jei/library/load/PluginCaller.java | 175 +++++++++++--- .../jei/library/load/PluginCallerTimer.java | 42 ++-- .../load/PluginCallerTimerRunnable.java | 3 +- .../mezz/jei/library/load/PluginHelper.java | 20 +- .../mezz/jei/library/load/PluginLoader.java | 99 +++++--- .../library/plugins/debug/JeiDebugPlugin.java | 2 - .../plugins/jei/JeiInternalPlugin.java | 14 +- .../plugins/vanilla/VanillaPlugin.java | 34 ++- .../jei/library/recipes/RecipeManager.java | 30 +-- .../recipes/RecipeManagerInternal.java | 2 +- .../recipes/collect/RecipeTypeDataMap.java | 2 + .../mezz/jei/library/runtime/JeiHelpers.java | 11 +- .../library/startup/ClientTaskExecutor.java | 38 ++++ .../jei/library/startup/IPluginFinder.java | 13 ++ .../library/startup/JeiClientExecutor.java | 38 ++++ .../mezz/jei/library/startup/JeiStarter.java | 135 +++++------ .../mezz/jei/library/startup/StartData.java | 17 ++ gradle.properties | 2 +- 47 files changed, 999 insertions(+), 509 deletions(-) delete mode 100644 Common/src/main/java/mezz/jei/common/config/file/serializers/StringSerializer.java create mode 100644 CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java create mode 100644 CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java create mode 100644 CommonApi/src/main/java/mezz/jei/api/JeiAsyncPlugin.java create mode 100644 CommonApi/src/main/java/mezz/jei/api/JeiRuntimePlugin.java create mode 100644 CommonApi/src/main/java/mezz/jei/api/runtime/IJeiClientExecutor.java rename Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/{FabricGuiPlugin.java => FabricRuntimePlugin.java} (53%) rename Forge/src/main/java/mezz/jei/forge/plugins/forge/{ForgeGuiPlugin.java => ForgeRuntimePlugin.java} (54%) create mode 100644 Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java create mode 100644 Library/src/main/java/mezz/jei/library/startup/IPluginFinder.java create mode 100644 Library/src/main/java/mezz/jei/library/startup/JeiClientExecutor.java diff --git a/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java b/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java index 983c7edb4..23f21a971 100644 --- a/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java +++ b/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java @@ -1,46 +1,42 @@ package mezz.jei.common.async; public class JeiStartTask extends Thread { - private final Runnable startTask; private boolean isCancelled = false; - public JeiStartTask(Runnable startTask) { - this.startTask = startTask; - this.setName("JEI Start"); + public JeiStartTask(Runnable target) { + super(target, "JEI Start"); + setDaemon(true); } - public void interruptStart() { + public void cancelStart() { isCancelled = true; } /** - * Check whether the startup should be interrupted. If this is not running on a JEI startup thread, - * false is returned. + * Check whether the startup should be interrupted. + * If this is not running on a JEI startup thread, false is returned. */ - private static boolean isStartInterrupted() { + private static boolean isCanceled() { Thread t = Thread.currentThread(); - if(t instanceof JeiStartTask) { - return ((JeiStartTask)t).isCancelled; - } else + if (t instanceof JeiStartTask startTask) { + return startTask.isCancelled; + } else { return false; + } } - private static final JeiAsyncStartInterrupt INTERRUPT_START = new JeiAsyncStartInterrupt(); - - public static void checkStartInterruption() { - if(isStartInterrupted()) - forceInterrupt(); - } - - public static void forceInterrupt() { - throw INTERRUPT_START; + public static void interruptIfCanceled() { + if (isCanceled()) { + throw new JeiAsyncStartInterrupt(); + } } @Override public void run() { try { - startTask.run(); - } catch(JeiAsyncStartInterrupt ignored) { + super.run(); + } catch (JeiAsyncStartInterrupt ignored) { + } } } diff --git a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java index 87e834cb5..3dcd68c90 100644 --- a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java @@ -5,7 +5,6 @@ import mezz.jei.common.config.file.IConfigSchemaBuilder; import mezz.jei.common.config.file.serializers.EnumSerializer; import mezz.jei.common.config.file.serializers.ListSerializer; -import mezz.jei.common.config.file.serializers.StringSerializer; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -19,7 +18,7 @@ public final class ClientConfig implements IClientConfig { private final Supplier lowMemorySlowSearchEnabled; private final Supplier cheatToHotbarUsingHotkeysEnabled; private final Supplier asyncLoadingEnabled; - private final Supplier> mainThreadPluginUids; + private final Supplier parallelPluginLoadingEnabled; private final Supplier addBookmarksToFront; private final Supplier lookupFluidContents; private final Supplier giveMode; @@ -68,14 +67,14 @@ public ClientConfig(IConfigSchemaBuilder schema) { "Max. recipe gui height" ); asyncLoadingEnabled = advanced.addBoolean( - "AsyncLoading", - false, - "Whether JEI should load asynchronously" + "asyncLoadingEnabled", + false, + "Whether JEI should load asynchronously, so that it starts some time after world join." ); - mainThreadPluginUids = advanced.addList("AsyncPluginCompat", - List.of("namespace:mod"), - new ListSerializer<>(new StringSerializer()), - "List of plugin UIDs that should be loaded on the main thread" + parallelPluginLoadingEnabled = advanced.addBoolean( + "parallelPluginLoadingEnabled", + false, + "Whether JEI should load plugins in parallel. This may cause plugins to crash." ); IConfigCategoryBuilder sorting = schema.addCategory("sorting"); @@ -112,13 +111,13 @@ public boolean isCheatToHotbarUsingHotkeysEnabled() { } @Override - public boolean isAsyncLoadingEnabled() { + public boolean getAsyncLoadingEnabled() { return asyncLoadingEnabled.get(); } @Override - public List getAsyncCompatPluginUids() { - return mainThreadPluginUids.get(); + public boolean getParallelPluginLoadingEnabled() { + return parallelPluginLoadingEnabled.get(); } @Override diff --git a/Common/src/main/java/mezz/jei/common/config/IClientConfig.java b/Common/src/main/java/mezz/jei/common/config/IClientConfig.java index 55f3e6a1c..4dea26a92 100644 --- a/Common/src/main/java/mezz/jei/common/config/IClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/IClientConfig.java @@ -13,8 +13,9 @@ public interface IClientConfig { boolean isCheatToHotbarUsingHotkeysEnabled(); - boolean isAsyncLoadingEnabled(); - List getAsyncCompatPluginUids(); + boolean getAsyncLoadingEnabled(); + + boolean getParallelPluginLoadingEnabled(); boolean isAddingBookmarksToFront(); diff --git a/Common/src/main/java/mezz/jei/common/config/file/serializers/StringSerializer.java b/Common/src/main/java/mezz/jei/common/config/file/serializers/StringSerializer.java deleted file mode 100644 index 410062947..000000000 --- a/Common/src/main/java/mezz/jei/common/config/file/serializers/StringSerializer.java +++ /dev/null @@ -1,37 +0,0 @@ -package mezz.jei.common.config.file.serializers; - -import mezz.jei.api.runtime.config.IJeiConfigValueSerializer; - -import java.util.Collection; -import java.util.Optional; - -public class StringSerializer implements IJeiConfigValueSerializer { - @Override - public String serialize(String value) { - return value; - } - - @Override - public IDeserializeResult deserialize(String string) { - string = string.trim(); - if (string.startsWith("\"") && string.endsWith("\"")) { - string = string.substring(1, string.length() - 1); - }; - return new DeserializeResult<>(string); - } - - @Override - public boolean isValid(String value) { - return true; - } - - @Override - public Optional> getAllValidValues() { - return Optional.empty(); - } - - @Override - public String getValidValuesDescription() { - return ""; - } -} diff --git a/Common/src/main/java/mezz/jei/common/util/ErrorUtil.java b/Common/src/main/java/mezz/jei/common/util/ErrorUtil.java index 3a205aaec..bcc7610ba 100644 --- a/Common/src/main/java/mezz/jei/common/util/ErrorUtil.java +++ b/Common/src/main/java/mezz/jei/common/util/ErrorUtil.java @@ -10,7 +10,6 @@ import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; -import net.minecraft.client.Minecraft; import net.minecraft.core.NonNullList; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; @@ -135,19 +134,6 @@ public static void checkNotNull(Collection values, String name) { } } - @SuppressWarnings("ConstantConditions") - public static void assertMainThread() { - Minecraft minecraft = Minecraft.getInstance(); - if (minecraft != null && !minecraft.isSameThread()) { - Thread currentThread = Thread.currentThread(); - throw new IllegalStateException( - "A JEI API method is being called by another mod from the wrong thread:\n" + - currentThread + "\n" + - "It must be called on the main thread by using Minecraft.addScheduledTask." - ); - } - } - public static ReportedException createRenderIngredientException(Throwable throwable, final T ingredient, IIngredientManager ingredientManager) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Rendering ingredient"); CrashReportCategory ingredientCategory = crashreport.addCategory("Ingredient being rendered"); diff --git a/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java b/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java new file mode 100644 index 000000000..7bb2e2fe4 --- /dev/null +++ b/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java @@ -0,0 +1,144 @@ +package mezz.jei.api; + +import mezz.jei.api.helpers.IPlatformFluidHelper; +import mezz.jei.api.registration.IAdvancedRegistration; +import mezz.jei.api.registration.IGuiHandlerRegistration; +import mezz.jei.api.registration.IModIngredientRegistration; +import mezz.jei.api.registration.IRecipeCatalystRegistration; +import mezz.jei.api.registration.IRecipeCategoryRegistration; +import mezz.jei.api.registration.IRecipeRegistration; +import mezz.jei.api.registration.IRecipeTransferRegistration; +import mezz.jei.api.registration.ISubtypeRegistration; +import mezz.jei.api.registration.IVanillaCategoryExtensionRegistration; +import mezz.jei.api.runtime.IJeiClientExecutor; +import mezz.jei.api.runtime.IJeiRuntime; +import mezz.jei.api.runtime.config.IJeiConfigManager; +import net.minecraft.resources.ResourceLocation; + +import java.util.concurrent.CompletableFuture; + +/** + * The main async class to implement to create a JEI plugin. + * Everything communicated between a mod and JEI is through this class. + * IAsyncModPlugin must have the {@link JeiPlugin} annotation to get loaded by JEI. + * + * In a Forge environment, IModPlugins must have the {@link JeiAsyncPlugin} annotation to get loaded by JEI. + * + * In a Fabric environment, IModPlugins must be declared under `entrypoints.jei_async_mod_plugin` in `fabric.mod.json`. + * See the Fabric Wiki for more information. + * + * @see IModPlugin for a simpler, synchronous version + * + * @since 13.2.0 + */ +public interface IAsyncModPlugin { + + /** + * The unique ID for this mod plugin. + * The namespace should be your mod's modId. + */ + ResourceLocation getPluginUid(); + + /** + * If your item has subtypes that depend on NBT or capabilities, use this to help JEI identify those subtypes correctly. + */ + default CompletableFuture registerItemSubtypes(ISubtypeRegistration registration, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * If your fluid has subtypes that depend on NBT or capabilities, + * use this to help JEI identify those subtypes correctly. + * + * @since 10.1.0 + */ + default CompletableFuture registerFluidSubtypes(ISubtypeRegistration registration, IPlatformFluidHelper platformFluidHelper, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Register special ingredients, beyond the basic ItemStack and FluidStack. + */ + default CompletableFuture registerIngredients(IModIngredientRegistration registration, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Register the categories handled by this plugin. + * These are registered before recipes so they can be checked for validity. + */ + default CompletableFuture registerCategories(IRecipeCategoryRegistration registration, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Register modded extensions to the vanilla crafting recipe category. + * Custom crafting recipes for your mod should use this to tell JEI how they work. + */ + default CompletableFuture registerVanillaCategoryExtensions(IVanillaCategoryExtensionRegistration registration, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Register modded recipes. + */ + default CompletableFuture registerRecipes(IRecipeRegistration registration, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Register recipe transfer handlers (move ingredients from the inventory into crafting GUIs). + */ + default CompletableFuture registerRecipeTransferHandlers(IRecipeTransferRegistration registration, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Register recipe catalysts. + * Recipe Catalysts are ingredients that are needed in order to craft other things. + * Vanilla examples of Recipe Catalysts are the Crafting Table and Furnace. + */ + default CompletableFuture registerRecipeCatalysts(IRecipeCatalystRegistration registration, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Register various GUI-related things for your mod. + * This includes adding clickable areas in your guis to open JEI, + * and adding areas on the screen that JEI should avoid drawing. + */ + default CompletableFuture registerGuiHandlers(IGuiHandlerRegistration registration, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Register advanced features for your mod plugin. + */ + default CompletableFuture registerAdvanced(IAdvancedRegistration registration, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Called when JEI's runtime features are available, after all mods have registered. + */ + default CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Called when JEI's runtime features are no longer available, after a user quits or logs out of a world. + * @since 11.5.0 + */ + default CompletableFuture onRuntimeUnavailable(IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Called when JEI's configs are available. + * This is called early on, as soon as configs are available. + * @since 12.3.0 + */ + default CompletableFuture onConfigManagerAvailable(IJeiConfigManager configManager, IJeiClientExecutor clientThreadExecutor) { + return CompletableFuture.completedFuture(null); + } +} diff --git a/CommonApi/src/main/java/mezz/jei/api/IModPlugin.java b/CommonApi/src/main/java/mezz/jei/api/IModPlugin.java index f55f0994a..2ef236062 100644 --- a/CommonApi/src/main/java/mezz/jei/api/IModPlugin.java +++ b/CommonApi/src/main/java/mezz/jei/api/IModPlugin.java @@ -16,11 +16,14 @@ import mezz.jei.api.registration.IVanillaCategoryExtensionRegistration; import mezz.jei.api.runtime.IJeiRuntime; -import java.util.EnumSet; /** * The main class to implement to create a JEI plugin. Everything communicated between a mod and JEI is through this class. - * IModPlugins must have the {@link JeiPlugin} annotation to get loaded by JEI. + * + * In a Forge environment, IModPlugins must have the {@link JeiPlugin} annotation to get loaded by JEI. + * + * In a Fabric environment, IModPlugins must be declared under `entrypoints.jei_mod_plugin` in `fabric.mod.json`. + * See the Fabric Wiki for more information. */ public interface IModPlugin { @@ -111,7 +114,11 @@ default void registerAdvanced(IAdvancedRegistration registration) { /** * Override the default JEI runtime. + * + * @since 12.0.2 + * @deprecated this has moved to {@link IRuntimePlugin} */ + @Deprecated(since = "13.2.0", forRemoval = true) default void registerRuntime(IRuntimeRegistration registration) { } @@ -139,16 +146,4 @@ default void onRuntimeUnavailable() { default void onConfigManagerAvailable(IJeiConfigManager configManager) { } - - /** - * Called to find out whether this plugin wants to load on the main thread (legacy behavior), instead of the async - * loading thread. - *

- * Most plugins should use Minecraft.getInstance().executeBlocking() for their purposes, as plugins loading on the - * main thread will cause lag spikes. - * @since TODO - */ - default boolean needsLoadingOnClientThread() { - return false; - } } diff --git a/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java b/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java new file mode 100644 index 000000000..95f6655e6 --- /dev/null +++ b/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java @@ -0,0 +1,46 @@ +package mezz.jei.api; + +import mezz.jei.api.registration.IRuntimeRegistration; +import mezz.jei.api.runtime.IJeiClientExecutor; +import mezz.jei.api.runtime.IJeiRuntime; +import net.minecraft.resources.ResourceLocation; + +import java.util.concurrent.CompletableFuture; + +/** + * In a Forge environment, IRuntimePlugins must have the {@link JeiRuntimePlugin} annotation to get loaded by JEI. + * + * In a Fabric environment, IModPlugins must be declared under `entrypoints.jei_runtime_plugin` in `fabric.mod.json`. + * See the Fabric Wiki for more information. + * + * @since 13.2.0 + */ +public interface IRuntimePlugin { + + /** + * The unique ID for this mod plugin. + * The namespace should be your mod's modId. + */ + ResourceLocation getPluginUid(); + + /** + * Override the default JEI runtime. + */ + default CompletableFuture registerRuntime(IRuntimeRegistration registration, IJeiClientExecutor clientExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Called when JEI's runtime features are available, after all mods have registered. + */ + default CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, IJeiClientExecutor clientExecutor) { + return CompletableFuture.completedFuture(null); + } + + /** + * Called when JEI's runtime features are no longer available, after a user quits or logs out of a world. + */ + default CompletableFuture onRuntimeUnavailable(IJeiClientExecutor clientExecutor) { + return CompletableFuture.completedFuture(null); + } +} diff --git a/CommonApi/src/main/java/mezz/jei/api/JeiAsyncPlugin.java b/CommonApi/src/main/java/mezz/jei/api/JeiAsyncPlugin.java new file mode 100644 index 000000000..d42560224 --- /dev/null +++ b/CommonApi/src/main/java/mezz/jei/api/JeiAsyncPlugin.java @@ -0,0 +1,9 @@ +package mezz.jei.api; + +/** + * This annotation lets JEI detect mod plugins. + * All {@link IAsyncModPlugin} must have this annotation and a constructor with no arguments. + * @since 13.2.0 + */ +public @interface JeiAsyncPlugin { +} diff --git a/CommonApi/src/main/java/mezz/jei/api/JeiRuntimePlugin.java b/CommonApi/src/main/java/mezz/jei/api/JeiRuntimePlugin.java new file mode 100644 index 000000000..562896eee --- /dev/null +++ b/CommonApi/src/main/java/mezz/jei/api/JeiRuntimePlugin.java @@ -0,0 +1,9 @@ +package mezz.jei.api; + +/** + * This annotation lets JEI detect mod plugins. + * All {@link IRuntimePlugin} must have this annotation and a constructor with no arguments. + * @since 13.2.0 + */ +public @interface JeiRuntimePlugin { +} diff --git a/CommonApi/src/main/java/mezz/jei/api/helpers/IJeiHelpers.java b/CommonApi/src/main/java/mezz/jei/api/helpers/IJeiHelpers.java index 5475056f8..d4c6f5899 100644 --- a/CommonApi/src/main/java/mezz/jei/api/helpers/IJeiHelpers.java +++ b/CommonApi/src/main/java/mezz/jei/api/helpers/IJeiHelpers.java @@ -7,7 +7,6 @@ import net.minecraft.resources.ResourceLocation; import java.util.Optional; -import java.util.concurrent.Executor; /** * {@link IJeiHelpers} provides helpers and tools for addon mods. @@ -68,9 +67,4 @@ public interface IJeiHelpers { * @since 11.5.0 */ IIngredientManager getIngredientManager(); - - /** - * Get access to the client executor, which budgets running background tasks on the main thread. - */ - Executor getClientExecutor(); } diff --git a/CommonApi/src/main/java/mezz/jei/api/runtime/IJeiClientExecutor.java b/CommonApi/src/main/java/mezz/jei/api/runtime/IJeiClientExecutor.java new file mode 100644 index 000000000..bb3ab9aec --- /dev/null +++ b/CommonApi/src/main/java/mezz/jei/api/runtime/IJeiClientExecutor.java @@ -0,0 +1,11 @@ +package mezz.jei.api.runtime; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Supplier; + +public interface IJeiClientExecutor { + CompletableFuture runOnClientThread(Runnable runnable); + CompletableFuture runOnClientThread(Supplier supplier); + Executor getExecutor(); +} diff --git a/Fabric/src/main/java/mezz/jei/fabric/mixin/EffectRenderingInventoryScreenMixin.java b/Fabric/src/main/java/mezz/jei/fabric/mixin/EffectRenderingInventoryScreenMixin.java index f0cb49dbb..037a33625 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/mixin/EffectRenderingInventoryScreenMixin.java +++ b/Fabric/src/main/java/mezz/jei/fabric/mixin/EffectRenderingInventoryScreenMixin.java @@ -2,7 +2,7 @@ import mezz.jei.api.runtime.IIngredientListOverlay; import mezz.jei.api.runtime.IJeiRuntime; -import mezz.jei.fabric.plugins.fabric.FabricGuiPlugin; +import mezz.jei.fabric.plugins.fabric.FabricRuntimePlugin; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.gui.screens.inventory.EffectRenderingInventoryScreen; import net.minecraft.network.chat.Component; @@ -25,7 +25,7 @@ public EffectRenderingInventoryScreenMixin(AbstractContainerMenu menu, Inventory at = @At("STORE") ) public boolean modifyHasRoom(boolean bl) { - boolean ingredientListDisplayed = FabricGuiPlugin.getRuntime() + boolean ingredientListDisplayed = FabricRuntimePlugin.getRuntime() .map(IJeiRuntime::getIngredientListOverlay) .map(IIngredientListOverlay::isListDisplayed) .orElse(false); diff --git a/Fabric/src/main/java/mezz/jei/fabric/mixin/MinecraftMixin.java b/Fabric/src/main/java/mezz/jei/fabric/mixin/MinecraftMixin.java index 14c23eeea..3a7086f5a 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/mixin/MinecraftMixin.java +++ b/Fabric/src/main/java/mezz/jei/fabric/mixin/MinecraftMixin.java @@ -48,6 +48,7 @@ public void beforeInitialResourceReload(GameConfig gameConfig, CallbackInfo ci) public void clearLevel(Screen screen, CallbackInfo ci) { JeiLifecycleEvents.GAME_STOP.invoker().run(); } + @Inject( method = "tick", at = @At("TAIL") diff --git a/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricGuiPlugin.java b/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricRuntimePlugin.java similarity index 53% rename from Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricGuiPlugin.java rename to Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricRuntimePlugin.java index 6ae9f2c82..3e5e5ba24 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricGuiPlugin.java +++ b/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricRuntimePlugin.java @@ -1,12 +1,12 @@ package mezz.jei.fabric.plugins.fabric; -import mezz.jei.api.IModPlugin; -import mezz.jei.api.JeiPlugin; +import mezz.jei.api.IRuntimePlugin; +import mezz.jei.api.JeiRuntimePlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.registration.IRuntimeRegistration; +import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.api.runtime.IJeiRuntime; import mezz.jei.fabric.startup.EventRegistration; -import mezz.jei.gui.startup.JeiEventHandlers; import mezz.jei.gui.startup.JeiGuiStarter; import net.minecraft.resources.ResourceLocation; import org.apache.logging.log4j.LogManager; @@ -14,9 +14,10 @@ import org.jetbrains.annotations.Nullable; import java.util.Optional; +import java.util.concurrent.CompletableFuture; -@JeiPlugin -public class FabricGuiPlugin implements IModPlugin { +@JeiRuntimePlugin +public class FabricRuntimePlugin implements IRuntimePlugin { private static final Logger LOGGER = LogManager.getLogger(); private static @Nullable IJeiRuntime runtime; @@ -24,25 +25,27 @@ public class FabricGuiPlugin implements IModPlugin { @Override public ResourceLocation getPluginUid() { - return new ResourceLocation(ModIds.JEI_ID, "fabric_gui"); + return new ResourceLocation(ModIds.JEI_ID, "fabric_runtime"); } @Override - public void registerRuntime(IRuntimeRegistration registration) { - JeiEventHandlers eventHandlers = JeiGuiStarter.start(registration); - eventRegistration.setEventHandlers(eventHandlers); + public CompletableFuture registerRuntime(IRuntimeRegistration registration, IJeiClientExecutor clientExecutor) { + return JeiGuiStarter.start(registration, clientExecutor) + .thenAccept(eventRegistration::setEventHandlers); } @Override - public void onRuntimeAvailable(IJeiRuntime jeiRuntime) { + public CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, IJeiClientExecutor clientExecutor) { runtime = jeiRuntime; + return CompletableFuture.completedFuture(null); } @Override - public void onRuntimeUnavailable() { + public CompletableFuture onRuntimeUnavailable(IJeiClientExecutor clientExecutor) { runtime = null; LOGGER.info("Stopping JEI GUI"); eventRegistration.clear(); + return CompletableFuture.completedFuture(null); } public static Optional getRuntime() { diff --git a/Fabric/src/main/java/mezz/jei/fabric/startup/ClientLifecycleHandler.java b/Fabric/src/main/java/mezz/jei/fabric/startup/ClientLifecycleHandler.java index 83bfc531c..e254d1d45 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/startup/ClientLifecycleHandler.java +++ b/Fabric/src/main/java/mezz/jei/fabric/startup/ClientLifecycleHandler.java @@ -1,6 +1,5 @@ package mezz.jei.fabric.startup; -import mezz.jei.api.IModPlugin; import mezz.jei.common.Internal; import mezz.jei.common.config.IServerConfig; import mezz.jei.common.network.ClientPacketRouter; @@ -16,8 +15,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.List; - public class ClientLifecycleHandler { private static final Logger LOGGER = LogManager.getLogger(); @@ -34,9 +31,9 @@ public ClientLifecycleHandler(IServerConfig serverConfig) { ClientPacketRouter packetRouter = new ClientPacketRouter(serverConnection, serverConfig); ClientNetworkHandler.registerClientPacketHandler(packetRouter); - List plugins = FabricPluginFinder.getModPlugins(); - StartData startData = new StartData( - plugins, + FabricPluginFinder pluginFinder = new FabricPluginFinder(); + StartData startData = StartData.create( + pluginFinder, serverConnection, keyMappings ); diff --git a/Fabric/src/main/java/mezz/jei/fabric/startup/EventRegistration.java b/Fabric/src/main/java/mezz/jei/fabric/startup/EventRegistration.java index 532d74dc5..6bdd0cbba 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/startup/EventRegistration.java +++ b/Fabric/src/main/java/mezz/jei/fabric/startup/EventRegistration.java @@ -38,6 +38,10 @@ private void registerEvents() { ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> registerScreenEvents(screen) ); + Screen currentScreen = Minecraft.getInstance().screen; + if (currentScreen != null) { + registerScreenEvents(currentScreen); + } JeiCharTypedEvents.BEFORE_CHAR_TYPED.register(this::beforeCharTyped); ScreenEvents.AFTER_INIT.register(this::afterInit); JeiScreenEvents.AFTER_RENDER_BACKGROUND.register(this::afterRenderBackground); diff --git a/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java b/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java index fea8151f5..025c17734 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java +++ b/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java @@ -1,19 +1,29 @@ package mezz.jei.fabric.startup; +import mezz.jei.api.IAsyncModPlugin; import mezz.jei.api.IModPlugin; +import mezz.jei.api.IRuntimePlugin; +import mezz.jei.library.startup.IPluginFinder; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.entrypoint.EntrypointContainer; import java.util.List; import java.util.stream.Collectors; -public final class FabricPluginFinder { - private FabricPluginFinder() { +public final class FabricPluginFinder implements IPluginFinder { + @Override + public List getModPlugins() { + return getInstances("jei_mod_plugin", IModPlugin.class); + } + @Override + public List getAsyncModPlugins() { + return getInstances("jei_async_mod_plugin", IAsyncModPlugin.class); } - public static List getModPlugins() { - return getInstances("jei_mod_plugin", IModPlugin.class); + @Override + public List getRuntimePlugins() { + return getInstances("jei_runtime_plugin", IRuntimePlugin.class); } @SuppressWarnings("SameParameterValue") diff --git a/Fabric/src/main/resources/fabric.mod.json b/Fabric/src/main/resources/fabric.mod.json index ac998e5d2..fd092646a 100644 --- a/Fabric/src/main/resources/fabric.mod.json +++ b/Fabric/src/main/resources/fabric.mod.json @@ -25,11 +25,15 @@ "mezz.jei.fabric.JustEnoughItemsClient" ], "jei_mod_plugin": [ - "mezz.jei.library.plugins.vanilla.VanillaPlugin", - "mezz.jei.library.plugins.jei.JeiInternalPlugin", "mezz.jei.library.plugins.debug.JeiDebugPlugin", - "mezz.jei.fabric.plugins.fabric.FabricGuiPlugin", "mezz.jei.gui.plugins.JeiGuiPlugin" + ], + "jei_async_mod_plugin": [ + "mezz.jei.library.plugins.jei.JeiInternalPlugin", + "mezz.jei.library.plugins.vanilla.VanillaPlugin" + ], + "jei_runtime_plugin": [ + "mezz.jei.fabric.plugins.fabric.FabricRuntimePlugin" ] }, "mixins": [ diff --git a/Forge/src/main/java/mezz/jei/forge/JustEnoughItemsClient.java b/Forge/src/main/java/mezz/jei/forge/JustEnoughItemsClient.java index 5ac2a0f50..ebf7a1cd0 100644 --- a/Forge/src/main/java/mezz/jei/forge/JustEnoughItemsClient.java +++ b/Forge/src/main/java/mezz/jei/forge/JustEnoughItemsClient.java @@ -1,6 +1,5 @@ package mezz.jei.forge; -import mezz.jei.api.IModPlugin; import mezz.jei.common.Internal; import mezz.jei.common.config.IServerConfig; import mezz.jei.common.gui.textures.Textures; @@ -18,7 +17,6 @@ import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import java.util.HashSet; -import java.util.List; import java.util.Set; public class JustEnoughItemsClient { @@ -41,9 +39,9 @@ public JustEnoughItemsClient( ClientPacketRouter packetRouter = new ClientPacketRouter(serverConnection, serverConfig); networkHandler.registerClientPacketHandler(packetRouter); - List plugins = ForgePluginFinder.getModPlugins(); - StartData startData = new StartData( - plugins, + ForgePluginFinder forgePluginFinder = new ForgePluginFinder(); + StartData startData = StartData.create( + forgePluginFinder, serverConnection, keyMappings ); diff --git a/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeGuiPlugin.java b/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeRuntimePlugin.java similarity index 54% rename from Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeGuiPlugin.java rename to Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeRuntimePlugin.java index b19fed470..9e4b6d2c9 100644 --- a/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeGuiPlugin.java +++ b/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeRuntimePlugin.java @@ -1,44 +1,48 @@ package mezz.jei.forge.plugins.forge; -import mezz.jei.api.IModPlugin; -import mezz.jei.api.JeiPlugin; +import mezz.jei.api.IRuntimePlugin; +import mezz.jei.api.JeiRuntimePlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.registration.IRuntimeRegistration; +import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.forge.events.RuntimeEventSubscriptions; import mezz.jei.forge.startup.EventRegistration; -import mezz.jei.gui.startup.JeiEventHandlers; import mezz.jei.gui.startup.JeiGuiStarter; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.common.MinecraftForge; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -@JeiPlugin -public class ForgeGuiPlugin implements IModPlugin { +import java.util.concurrent.CompletableFuture; + +@JeiRuntimePlugin +public class ForgeRuntimePlugin implements IRuntimePlugin { private static final Logger LOGGER = LogManager.getLogger(); private final RuntimeEventSubscriptions runtimeSubscriptions = new RuntimeEventSubscriptions(MinecraftForge.EVENT_BUS); @Override public ResourceLocation getPluginUid() { - return new ResourceLocation(ModIds.JEI_ID, "forge_gui"); + return new ResourceLocation(ModIds.JEI_ID, "forge_runtime"); } @Override - public void registerRuntime(IRuntimeRegistration registration) { + public CompletableFuture registerRuntime(IRuntimeRegistration registration, IJeiClientExecutor clientExecutor) { if (!runtimeSubscriptions.isEmpty()) { LOGGER.error("JEI GUI is already running."); runtimeSubscriptions.clear(); } - JeiEventHandlers eventHandlers = JeiGuiStarter.start(registration); - - EventRegistration.registerEvents(runtimeSubscriptions, eventHandlers); + return JeiGuiStarter.start(registration, clientExecutor) + .thenAcceptAsync(eventHandlers -> { + EventRegistration.registerEvents(runtimeSubscriptions, eventHandlers); + }, clientExecutor.getExecutor()); } @Override - public void onRuntimeUnavailable() { + public CompletableFuture onRuntimeUnavailable(IJeiClientExecutor clientExecuto) { LOGGER.info("Stopping JEI GUI"); runtimeSubscriptions.clear(); + return CompletableFuture.completedFuture(null); } } diff --git a/Forge/src/main/java/mezz/jei/forge/startup/ForgePluginFinder.java b/Forge/src/main/java/mezz/jei/forge/startup/ForgePluginFinder.java index 32e7a6182..5a387ba7e 100644 --- a/Forge/src/main/java/mezz/jei/forge/startup/ForgePluginFinder.java +++ b/Forge/src/main/java/mezz/jei/forge/startup/ForgePluginFinder.java @@ -7,6 +7,11 @@ import java.util.Objects; import java.util.Set; +import mezz.jei.api.IAsyncModPlugin; +import mezz.jei.api.IRuntimePlugin; +import mezz.jei.api.JeiAsyncPlugin; +import mezz.jei.api.JeiRuntimePlugin; +import mezz.jei.library.startup.IPluginFinder; import net.minecraftforge.fml.ModList; import net.minecraftforge.forgespi.language.ModFileScanData; @@ -16,15 +21,22 @@ import org.apache.logging.log4j.Logger; import org.objectweb.asm.Type; -public final class ForgePluginFinder { +public final class ForgePluginFinder implements IPluginFinder { private static final Logger LOGGER = LogManager.getLogger(); - private ForgePluginFinder() { + @Override + public List getModPlugins() { + return getInstances(JeiPlugin.class, IModPlugin.class); + } + @Override + public List getAsyncModPlugins() { + return getInstances(JeiAsyncPlugin.class, IAsyncModPlugin.class); } - public static List getModPlugins() { - return getInstances(JeiPlugin.class, IModPlugin.class); + @Override + public List getRuntimePlugins() { + return getInstances(JeiRuntimePlugin.class, IRuntimePlugin.class); } @SuppressWarnings("SameParameterValue") @@ -49,7 +61,7 @@ private static List getInstances(Class annotationClass, Class insta Constructor constructor = asmInstanceClass.getDeclaredConstructor(); T instance = constructor.newInstance(); instances.add(instance); - } catch (ReflectiveOperationException | LinkageError e) { + } catch (ReflectiveOperationException | ClassCastException | LinkageError e) { LOGGER.error("Failed to load: {}", className, e); } } diff --git a/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java b/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java index 59f62969f..8879778bc 100644 --- a/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java +++ b/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java @@ -24,6 +24,7 @@ import mezz.jei.library.ingredients.subtypes.SubtypeInterpreters; import mezz.jei.library.ingredients.subtypes.SubtypeManager; import mezz.jei.library.load.registration.IngredientManagerBuilder; +import mezz.jei.library.startup.JeiClientExecutor; import mezz.jei.test.lib.TestClientConfig; import mezz.jei.test.lib.TestColorHelper; import mezz.jei.test.lib.TestIngredient; @@ -45,6 +46,7 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; +import java.util.concurrent.ExecutionException; public class IngredientFilterTest { private static final int EXTRA_INGREDIENT_COUNT = 5; @@ -62,8 +64,9 @@ public class IngredientFilterTest { private FilterTextSource filterTextSource; @BeforeEach - public void setup() { + public void setup() throws ExecutionException, InterruptedException { TestPlugin testPlugin = new TestPlugin(); + JeiClientExecutor clientExecutor = new JeiClientExecutor(MoreExecutors.directExecutor()); SubtypeInterpreters subtypeInterpreters = new SubtypeInterpreters(); SubtypeManager subtypeManager = new SubtypeManager(subtypeInterpreters); @@ -93,12 +96,11 @@ public void setup() { ingredientFilterConfig, ingredientManager, ingredientListSorter, - baseList, modIdHelper, ingredientVisibility, - colorHelper, - MoreExecutors.directExecutor() + colorHelper ); + this.ingredientFilter.addIngredientsAsync(baseList, clientExecutor).get(); this.ingredientManager.registerIngredientListener(blacklist); this.ingredientManager.registerIngredientListener(ingredientFilter); diff --git a/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java b/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java index fbb39cc60..2191ea185 100644 --- a/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java +++ b/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java @@ -28,6 +28,16 @@ public boolean isCheatToHotbarUsingHotkeysEnabled() { return false; } + @Override + public boolean getParallelPluginLoadingEnabled() { + return false; + } + + @Override + public List getAsyncCompatPluginUids() { + return List.of(); + } + @Override public boolean isAddingBookmarksToFront() { return false; diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java index 1e59bdcb8..1d3e67864 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IListElementInfo.java @@ -3,8 +3,6 @@ import java.util.Collection; import java.util.List; import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; import java.util.stream.Stream; import mezz.jei.api.ingredients.ITypedIngredient; @@ -39,7 +37,4 @@ public interface IListElementInfo { void setSortedIndex(int sortIndex); int getSortedIndex(); - - CompletableFuture cacheTooltips(IIngredientFilterConfig config, IIngredientManager ingredientManager, Executor clientExecutor); - } diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java index 5de77e468..431c91ea0 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java @@ -1,5 +1,6 @@ package mezz.jei.gui.ingredients; +import com.google.common.collect.Lists; import mezz.jei.api.helpers.IColorHelper; import mezz.jei.api.helpers.IModIdHelper; import mezz.jei.api.ingredients.IIngredientHelper; @@ -8,6 +9,7 @@ import mezz.jei.api.ingredients.subtypes.UidContext; import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.api.runtime.IIngredientVisibility; +import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.common.async.JeiStartTask; import mezz.jei.common.config.DebugConfig; import mezz.jei.common.util.Translator; @@ -36,7 +38,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -68,11 +70,9 @@ public IngredientFilter( IIngredientFilterConfig config, IIngredientManager ingredientManager, IIngredientSorter sorter, - NonNullList> ingredients, IModIdHelper modIdHelper, IIngredientVisibility ingredientVisibility, - IColorHelper colorHelper, - Executor clientExecutor + IColorHelper colorHelper ) { this.filterTextSource = filterTextSource; this.ingredientManager = ingredientManager; @@ -87,33 +87,42 @@ public IngredientFilter( this.elementSearch = new ElementSearch(this.elementPrefixParser); } - LOGGER.info("Adding {} ingredients", ingredients.size()); - List> elementInfos = ingredients.stream() - .map(i -> ListElementInfo.create(i, ingredientManager, modIdHelper)) - .flatMap(Optional::stream) - .collect(Collectors.toList()); - List> futures = new ArrayList<>(); - for(IListElementInfo elementInfo : elementInfos) { - futures.add(elementInfo.cacheTooltips(config, ingredientManager, clientExecutor)); - } - CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); - for(IListElementInfo elementInfo : elementInfos) { - this.addIngredient(elementInfo); - } - LOGGER.info("Added {} ingredients", ingredients.size()); - this.filterTextSource.addListener(filterText -> { ingredientListCached = null; notifyListenersOfChange(); }); } - /* used to check for interruption periodically */ - private int ingredientNum = 0; + public CompletableFuture addIngredientsAsync( + NonNullList> ingredients, + IJeiClientExecutor clientExecutor + ) { + int ingredientCount = ingredients.size(); + LOGGER.info("Adding {} ingredients", ingredientCount); + List> elementInfos = ingredients.stream() + .map(i -> ListElementInfo.create(i, ingredientManager, modIdHelper)) + .flatMap(Optional::stream) + .collect(Collectors.toList()); + + int batchSize = 100; + AtomicInteger addedTotal = new AtomicInteger(0); + Stream> futures = Lists.partition(elementInfos, batchSize) + .stream() + .map(batch -> clientExecutor.runOnClientThread(() -> { + JeiStartTask.interruptIfCanceled(); + for (IListElementInfo elementInfo : batch) { + this.addIngredient(elementInfo); + } + int added = addedTotal.addAndGet(batch.size()); + if (added % (10 * batchSize) == 0 || added == ingredientCount) { + LOGGER.info("Added {}/{} ingredients", added, ingredientCount); + } + })); + + return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)); + } public void addIngredient(IListElementInfo info) { - if(((ingredientNum++) % 100) == 0) - JeiStartTask.checkStartInterruption(); IListElement element = info.getElement(); updateHiddenState(element); diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java b/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java index 1ed48e5d4..9b6a69ef7 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/ListElementInfo.java @@ -1,14 +1,13 @@ package mezz.jei.gui.ingredients; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import mezz.jei.api.helpers.IModIdHelper; import mezz.jei.api.ingredients.IIngredientHelper; import mezz.jei.api.ingredients.IIngredientRenderer; import mezz.jei.api.ingredients.ITypedIngredient; import mezz.jei.api.runtime.IIngredientManager; -import mezz.jei.common.util.Translator; import mezz.jei.common.config.IIngredientFilterConfig; +import mezz.jei.common.util.Translator; import net.minecraft.resources.ResourceLocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -20,8 +19,6 @@ import java.util.Locale; import java.util.Optional; import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -35,7 +32,6 @@ public class ListElementInfo implements IListElementInfo { private final List modNames; private final ResourceLocation resourceLocation; private int sortedIndex = Integer.MAX_VALUE; - private List tooltipCache = null; public static Optional> create(IListElement element, IIngredientManager ingredientManager, IModIdHelper modIdHelper) { ITypedIngredient value = element.getTypedIngredient(); @@ -105,18 +101,13 @@ private static void addModNameStrings(Set modNames, String modId, String @Override public final List getTooltipStrings(IIngredientFilterConfig config, IIngredientManager ingredientManager) { - if(this.tooltipCache == null) { - String modName = this.modNames.get(0); - String modId = this.modIds.get(0); - String modNameLowercase = modName.toLowerCase(Locale.ENGLISH); - ITypedIngredient value = element.getTypedIngredient(); - IIngredientRenderer ingredientRenderer = ingredientManager.getIngredientRenderer(value.getType()); - ImmutableSet toRemove = ImmutableSet.of(modId, modNameLowercase, displayNameLowercase, resourceLocation.getPath()); - // use ImmutableList to automatically deduplicate empty lists to the singleton empty list - this.tooltipCache = ImmutableList.copyOf(IngredientInformationUtil.getTooltipStrings(value.getIngredient(), - ingredientRenderer, toRemove, config)); - } - return this.tooltipCache; + String modName = this.modNames.get(0); + String modId = this.modIds.get(0); + String modNameLowercase = modName.toLowerCase(Locale.ENGLISH); + ITypedIngredient value = element.getTypedIngredient(); + IIngredientRenderer ingredientRenderer = ingredientManager.getIngredientRenderer(value.getType()); + ImmutableSet toRemove = ImmutableSet.of(modId, modNameLowercase, displayNameLowercase, resourceLocation.getPath()); + return IngredientInformationUtil.getTooltipStrings(value.getIngredient(), ingredientRenderer, toRemove, config); } @Override @@ -167,13 +158,4 @@ public void setSortedIndex(int sortIndex) { public int getSortedIndex() { return sortedIndex; } - - @Override - public CompletableFuture cacheTooltips(IIngredientFilterConfig config, IIngredientManager ingredientManager, - Executor clientExecutor) { - if(this.tooltipCache == null) { - return CompletableFuture.runAsync(() -> this.getTooltipStrings(config, ingredientManager), clientExecutor); - } else - return CompletableFuture.completedFuture(null); - } } diff --git a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java index f760690d4..e1e7949ca 100644 --- a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java +++ b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java @@ -11,6 +11,7 @@ import mezz.jei.api.runtime.IIngredientFilter; import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.api.runtime.IIngredientVisibility; +import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.api.runtime.IScreenHelper; import mezz.jei.common.Internal; import mezz.jei.common.config.IClientConfig; @@ -55,11 +56,12 @@ import org.apache.logging.log4j.Logger; import java.util.List; +import java.util.concurrent.CompletableFuture; public class JeiGuiStarter { private static final Logger LOGGER = LogManager.getLogger(); - public static JeiEventHandlers start(IRuntimeRegistration registration) { + public static CompletableFuture start(IRuntimeRegistration registration, IJeiClientExecutor clientExecutor) { LOGGER.info("Starting JEI GUI"); LoggedTimer timer = new LoggedTimer(); @@ -111,113 +113,118 @@ public static JeiEventHandlers start(IRuntimeRegistration registration) { ingredientFilterConfig, ingredientManager, ingredientSorter, - ingredientList, modIdHelper, ingredientVisibility, - colorHelper, - jeiHelpers.getClientExecutor() + colorHelper ); - ingredientManager.registerIngredientListener(ingredientFilter); - ingredientVisibility.registerListener(ingredientFilter::onIngredientVisibilityChanged); timer.stop(); - IIngredientFilter ingredientFilterApi = new IngredientFilterApi(ingredientFilter, filterTextSource); - registration.setIngredientFilter(ingredientFilterApi); - - CheatUtil cheatUtil = new CheatUtil(ingredientManager); - IngredientListOverlay ingredientListOverlay = OverlayHelper.createIngredientListOverlay( - ingredientManager, - screenHelper, - ingredientFilter, - filterTextSource, - modIdHelper, - keyMappings, - ingredientListConfig, - clientConfig, - toggleState, - editModeConfig, - serverConnection, - ingredientFilterConfig, - textures, - colorHelper, - cheatUtil - ); - registration.setIngredientListOverlay(ingredientListOverlay); - - BookmarkList bookmarkList = new BookmarkList(ingredientManager, bookmarkConfig, clientConfig); - bookmarkConfig.loadBookmarks(ingredientManager, bookmarkList); - - BookmarkOverlay bookmarkOverlay = OverlayHelper.createBookmarkOverlay( - ingredientManager, - screenHelper, - bookmarkList, - modIdHelper, - keyMappings, - bookmarkListConfig, - editModeConfig, - ingredientFilterConfig, - clientConfig, - toggleState, - serverConnection, - textures, - colorHelper, - cheatUtil - ); - registration.setBookmarkOverlay(bookmarkOverlay); - - GuiEventHandler guiEventHandler = new GuiEventHandler( - screenHelper, - bookmarkOverlay, - ingredientListOverlay - ); - - RecipesGui recipesGui = new RecipesGui( - recipeManager, - recipeTransferManager, - ingredientManager, - modIdHelper, - clientConfig, - textures, - keyMappings, - focusFactory - ); - registration.setRecipesGui(recipesGui); - - CombinedRecipeFocusSource recipeFocusSource = new CombinedRecipeFocusSource( - recipesGui, - ingredientListOverlay, - bookmarkOverlay, - new GuiContainerWrapper(screenHelper) - ); - - List charTypedHandlers = List.of( - ingredientListOverlay - ); - - UserInputRouter userInputRouter = new UserInputRouter( - new EditInputHandler(recipeFocusSource, toggleState, editModeConfig), - ingredientListOverlay.createInputHandler(), - bookmarkOverlay.createInputHandler(), - new FocusInputHandler(recipeFocusSource, recipesGui, focusFactory, clientConfig, ingredientManager), - new BookmarkInputHandler(recipeFocusSource, bookmarkList), - new GlobalInputHandler(toggleState), - new GuiAreaInputHandler(screenHelper, recipesGui, focusFactory) - ); - - DragRouter dragRouter = new DragRouter( - ingredientListOverlay.createDragHandler(), - bookmarkOverlay.createDragHandler() - ); - ClientInputHandler clientInputHandler = new ClientInputHandler( - charTypedHandlers, - userInputRouter, - dragRouter, - keyMappings - ); - - return new JeiEventHandlers( - guiEventHandler, - clientInputHandler - ); + timer.start("Adding ingredients"); + return ingredientFilter.addIngredientsAsync(ingredientList, clientExecutor) + .thenApplyAsync((v) -> { + timer.stop(); + + ingredientManager.registerIngredientListener(ingredientFilter); + ingredientVisibility.registerListener(ingredientFilter::onIngredientVisibilityChanged); + + IIngredientFilter ingredientFilterApi = new IngredientFilterApi(ingredientFilter, filterTextSource); + registration.setIngredientFilter(ingredientFilterApi); + + CheatUtil cheatUtil = new CheatUtil(ingredientManager); + IngredientListOverlay ingredientListOverlay = OverlayHelper.createIngredientListOverlay( + ingredientManager, + screenHelper, + ingredientFilter, + filterTextSource, + modIdHelper, + keyMappings, + ingredientListConfig, + clientConfig, + toggleState, + editModeConfig, + serverConnection, + ingredientFilterConfig, + textures, + colorHelper, + cheatUtil + ); + registration.setIngredientListOverlay(ingredientListOverlay); + + BookmarkList bookmarkList = new BookmarkList(ingredientManager, bookmarkConfig, clientConfig); + bookmarkConfig.loadBookmarks(ingredientManager, bookmarkList); + + BookmarkOverlay bookmarkOverlay = OverlayHelper.createBookmarkOverlay( + ingredientManager, + screenHelper, + bookmarkList, + modIdHelper, + keyMappings, + bookmarkListConfig, + editModeConfig, + ingredientFilterConfig, + clientConfig, + toggleState, + serverConnection, + textures, + colorHelper, + cheatUtil + ); + registration.setBookmarkOverlay(bookmarkOverlay); + + GuiEventHandler guiEventHandler = new GuiEventHandler( + screenHelper, + bookmarkOverlay, + ingredientListOverlay + ); + + RecipesGui recipesGui = new RecipesGui( + recipeManager, + recipeTransferManager, + ingredientManager, + modIdHelper, + clientConfig, + textures, + keyMappings, + focusFactory + ); + registration.setRecipesGui(recipesGui); + + CombinedRecipeFocusSource recipeFocusSource = new CombinedRecipeFocusSource( + recipesGui, + ingredientListOverlay, + bookmarkOverlay, + new GuiContainerWrapper(screenHelper) + ); + + List charTypedHandlers = List.of( + ingredientListOverlay + ); + + UserInputRouter userInputRouter = new UserInputRouter( + new EditInputHandler(recipeFocusSource, toggleState, editModeConfig), + ingredientListOverlay.createInputHandler(), + bookmarkOverlay.createInputHandler(), + new FocusInputHandler(recipeFocusSource, recipesGui, focusFactory, clientConfig, ingredientManager), + new BookmarkInputHandler(recipeFocusSource, bookmarkList), + new GlobalInputHandler(toggleState), + new GuiAreaInputHandler(screenHelper, recipesGui, focusFactory) + ); + + DragRouter dragRouter = new DragRouter( + ingredientListOverlay.createDragHandler(), + bookmarkOverlay.createDragHandler() + ); + ClientInputHandler clientInputHandler = new ClientInputHandler( + charTypedHandlers, + userInputRouter, + dragRouter, + keyMappings + ); + + return new JeiEventHandlers( + guiEventHandler, + clientInputHandler + ); + }); } } diff --git a/Library/src/main/java/mezz/jei/library/ingredients/IngredientManager.java b/Library/src/main/java/mezz/jei/library/ingredients/IngredientManager.java index 7670e9c1f..789aa1b10 100644 --- a/Library/src/main/java/mezz/jei/library/ingredients/IngredientManager.java +++ b/Library/src/main/java/mezz/jei/library/ingredients/IngredientManager.java @@ -73,11 +73,9 @@ public Collection> getRegisteredIngredientTypes() { } @Override - public void addIngredientsAtRuntime(IIngredientType ingredientType, Collection ingredients) { - ErrorUtil.assertMainThread(); + public synchronized void addIngredientsAtRuntime(IIngredientType ingredientType, Collection ingredients) { ErrorUtil.checkNotNull(ingredientType, "ingredientType"); ErrorUtil.checkNotEmpty(ingredients, "ingredients"); - IngredientInfo ingredientInfo = this.registeredIngredients.getIngredientInfo(ingredientType); LOGGER.info("Ingredients are being added at runtime: {} {}", ingredients.size(), ingredientType.getIngredientClass().getName()); @@ -119,11 +117,9 @@ public Optional> getIngredientTypeChecked(Class void removeIngredientsAtRuntime(IIngredientType ingredientType, Collection ingredients) { - ErrorUtil.assertMainThread(); + public synchronized void removeIngredientsAtRuntime(IIngredientType ingredientType, Collection ingredients) { ErrorUtil.checkNotNull(ingredientType, "ingredientType"); ErrorUtil.checkNotEmpty(ingredients, "ingredients"); - IngredientInfo ingredientInfo = this.registeredIngredients.getIngredientInfo(ingredientType); LOGGER.info("Ingredients are being removed at runtime: {} {}", ingredients.size(), ingredientType.getIngredientClass().getName()); diff --git a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java index 761155181..07cb8b970 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java @@ -1,57 +1,176 @@ package mezz.jei.library.load; import com.google.common.base.Stopwatch; +import mezz.jei.api.IAsyncModPlugin; import mezz.jei.api.IModPlugin; -import mezz.jei.common.Internal; +import mezz.jei.api.IRuntimePlugin; import mezz.jei.common.async.JeiStartTask; -import mezz.jei.library.startup.JeiStarter; +import mezz.jei.common.config.IClientConfig; +import mezz.jei.library.startup.JeiClientExecutor; import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Stream; public class PluginCaller { private static final Logger LOGGER = LogManager.getLogger(); + private final List plugins; + private final List asyncPlugins; + private final List runtimePlugins; + private final JeiClientExecutor clientExecutor; + private final IClientConfig clientConfig; - public static void callOnPlugins(String title, List plugins, Consumer func) { + public PluginCaller( + List plugins, + List asyncPlugins, + List runtimePlugins, + JeiClientExecutor clientExecutor, + IClientConfig clientConfig + ) { + this.plugins = plugins; + this.asyncPlugins = asyncPlugins; + this.runtimePlugins = runtimePlugins; + this.clientExecutor = clientExecutor; + this.clientConfig = clientConfig; + } + + private void callAsync( + String title, + PluginCallerTimer timer, + List plugins, + Function uidFunc, + Function> func + ) { + Set erroredPlugins = ConcurrentHashMap.newKeySet(); + + Stream> futures = plugins.stream() + .map(plugin -> + CompletableFuture.>supplyAsync(() -> { + JeiStartTask.interruptIfCanceled(); + ResourceLocation pluginUid = uidFunc.apply(plugin); + var t = timer.begin(title, pluginUid); + try { + return func.apply(plugin) + .handle((v, e) -> { + if (e != null) { + LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); + } + t.close(); + return null; + }); + } catch (RuntimeException | LinkageError e) { + LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); + erroredPlugins.add(plugin); + t.close(); + return CompletableFuture.completedFuture(null); + } + }) + .thenCompose(f -> f) + ); + + CompletableFuture allFutures = CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)); + + Minecraft minecraft = Minecraft.getInstance(); + if (minecraft.isSameThread()) { + minecraft.managedBlock(() -> { + if (allFutures.isDone()) { + return true; + } + clientExecutor.tick(); + return false; + }); + } + allFutures.join(); + + plugins.removeAll(erroredPlugins); + } + + private void callSync( + String title, + PluginCallerTimer timer, + List plugins, + Function uidFunc, + Consumer func + ) { + Set erroredPlugins = ConcurrentHashMap.newKeySet(); + for (T plugin : plugins) { + ResourceLocation pluginUid = uidFunc.apply(plugin); + try (var ignored = timer.begin(title, pluginUid)) { + func.accept(plugin); + } catch (RuntimeException | LinkageError e) { + LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); + erroredPlugins.add(plugin); + } + } + plugins.removeAll(erroredPlugins); + } + + public void callOnPlugins( + String title, + Consumer func, + Function> asyncFun + ) { LOGGER.info("{}...", title); Stopwatch stopwatch = Stopwatch.createStarted(); try (PluginCallerTimer timer = new PluginCallerTimer()) { - List erroredPlugins = new ArrayList<>(); - - for (IModPlugin plugin : plugins) { - JeiStartTask.checkStartInterruption(); - try { - ResourceLocation pluginUid = plugin.getPluginUid(); - timer.begin(title, pluginUid); - if(plugin.needsLoadingOnClientThread() || isLegacyPlugin(plugin.getPluginUid())) { - Minecraft.getInstance().executeBlocking(() -> func.accept(plugin)); - } else - func.accept(plugin); - timer.end(); - } catch (RuntimeException | LinkageError e) { - LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), plugin.getPluginUid(), e); - erroredPlugins.add(plugin); - } + callAsync( + title, + timer, + asyncPlugins, + IAsyncModPlugin::getPluginUid, + asyncFun + ); + + if (clientConfig.getParallelPluginLoadingEnabled()) { + callAsync( + title, + timer, + plugins, + IModPlugin::getPluginUid, + p -> CompletableFuture.runAsync(() -> func.accept(p)) + ); + } else { + clientExecutor.runOnClientThread(() -> { + callSync( + title, + timer, + plugins, + IModPlugin::getPluginUid, + func + ); + }).join(); } - plugins.removeAll(erroredPlugins); } LOGGER.info("{} took {}", title, stopwatch); } - private static boolean isLegacyPlugin(ResourceLocation uid) { - String uidString = uid.toString(); - // scales poorly if there are many plugins, but there shouldn't be as modders should support this mode - // in the worst case, this can be cached - return Internal.getJeiClientConfigs().getClientConfig().getAsyncCompatPluginUids().contains(uidString); + public void callOnRuntimePlugins( + String title, + Function> asyncFun + ) { + LOGGER.info("{}...", title); + Stopwatch stopwatch = Stopwatch.createStarted(); + + try (PluginCallerTimer timer = new PluginCallerTimer()) { + callAsync( + title, + timer, + runtimePlugins, + IRuntimePlugin::getPluginUid, + asyncFun + ); + } + + LOGGER.info("{} took {}", title, stopwatch); } } diff --git a/Library/src/main/java/mezz/jei/library/load/PluginCallerTimer.java b/Library/src/main/java/mezz/jei/library/load/PluginCallerTimer.java index 1d71247af..cc70e2b38 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginCallerTimer.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginCallerTimer.java @@ -1,15 +1,16 @@ package mezz.jei.library.load; import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.Nullable; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class PluginCallerTimer implements AutoCloseable { private final ScheduledExecutorService executor; - private @Nullable PluginCallerTimerRunnable runnable; + private final Set refs = ConcurrentHashMap.newKeySet(); public PluginCallerTimer() { this.executor = Executors.newSingleThreadScheduledExecutor(); @@ -17,24 +18,39 @@ public PluginCallerTimer() { } private synchronized void run() { - if (this.runnable != null) { - this.runnable.check(); - } + refs.stream() + .map(r -> r.runnable) + .forEach(PluginCallerTimerRunnable::check); } - public synchronized void begin(String title, ResourceLocation pluginUid) { - this.runnable = new PluginCallerTimerRunnable(title, pluginUid); + public synchronized Ref begin(String title, ResourceLocation pluginUid) { + PluginCallerTimerRunnable runnable = new PluginCallerTimerRunnable(title, pluginUid); + Ref ref = new Ref(runnable); + refs.add(ref); + return ref; } - public synchronized void end() { - if (this.runnable != null) { - this.runnable.stop(); - this.runnable = null; - } + private synchronized boolean end(Ref ref) { + return refs.remove(ref); } @Override - public void close() { + public synchronized void close() { this.executor.shutdown(); } + + public final class Ref implements AutoCloseable { + public final PluginCallerTimerRunnable runnable; + + public Ref(PluginCallerTimerRunnable runnable) { + this.runnable = runnable; + } + + @Override + public void close() { + if (end(this)) { + this.runnable.stop(); + } + } + } } diff --git a/Library/src/main/java/mezz/jei/library/load/PluginCallerTimerRunnable.java b/Library/src/main/java/mezz/jei/library/load/PluginCallerTimerRunnable.java index 34ab9a35e..219a817c9 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginCallerTimerRunnable.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginCallerTimerRunnable.java @@ -1,5 +1,6 @@ package mezz.jei.library.load; +import mezz.jei.common.config.DebugConfig; import net.minecraft.resources.ResourceLocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -40,7 +41,7 @@ public void check() { public void stop() { Duration elapsed = Duration.ofNanos(System.nanoTime() - this.startTime); - if (elapsed.toMillis() > startReportDurationMs) { + if (elapsed.toMillis() > startReportDurationMs || DebugConfig.isDebugModeEnabled()) { LOGGER.info("{}: {} took {}", title, pluginUid, toHumanString(elapsed)); } } diff --git a/Library/src/main/java/mezz/jei/library/load/PluginHelper.java b/Library/src/main/java/mezz/jei/library/load/PluginHelper.java index 97381fe20..7b5f192a9 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginHelper.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginHelper.java @@ -1,15 +1,17 @@ package mezz.jei.library.load; +import mezz.jei.api.IAsyncModPlugin; import mezz.jei.library.plugins.jei.JeiInternalPlugin; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; import mezz.jei.api.IModPlugin; import mezz.jei.library.plugins.vanilla.VanillaPlugin; public class PluginHelper { - public static void sortPlugins(List plugins, VanillaPlugin vanillaPlugin, @Nullable JeiInternalPlugin jeiInternalPlugin) { + public static void sortPlugins(List plugins, VanillaPlugin vanillaPlugin, @Nullable JeiInternalPlugin jeiInternalPlugin) { plugins.remove(vanillaPlugin); plugins.add(0, vanillaPlugin); @@ -19,13 +21,13 @@ public static void sortPlugins(List plugins, VanillaPlugin vanillaPl } } - public static Optional getPluginWithClass(Class pluginClass, List modPlugins) { - for (IModPlugin modPlugin : modPlugins) { - if (pluginClass.isInstance(modPlugin)) { - T cast = pluginClass.cast(modPlugin); - return Optional.of(cast); - } - } - return Optional.empty(); + public static Optional getPluginWithClass(Class pluginClass, List modPlugins, List asyncModPlugins) { + return Stream.concat( + modPlugins.stream(), + asyncModPlugins.stream() + ) + .filter(pluginClass::isInstance) + .map(pluginClass::cast) + .findFirst(); } } diff --git a/Library/src/main/java/mezz/jei/library/load/PluginLoader.java b/Library/src/main/java/mezz/jei/library/load/PluginLoader.java index 671c8f26e..f3d06086e 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginLoader.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginLoader.java @@ -2,7 +2,6 @@ import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableTable; -import mezz.jei.api.IModPlugin; import mezz.jei.api.helpers.IColorHelper; import mezz.jei.api.helpers.IJeiHelpers; import mezz.jei.api.helpers.IModIdHelper; @@ -18,10 +17,11 @@ import mezz.jei.api.runtime.IScreenHelper; import mezz.jei.common.Internal; import mezz.jei.common.gui.textures.Textures; +import mezz.jei.common.network.IConnectionToServer; import mezz.jei.common.platform.IPlatformFluidHelperInternal; import mezz.jei.common.platform.Services; -import mezz.jei.core.util.LoggedTimer; import mezz.jei.common.util.StackHelper; +import mezz.jei.core.util.LoggedTimer; import mezz.jei.library.config.IModIdFormatConfig; import mezz.jei.library.config.RecipeCategorySortingConfig; import mezz.jei.library.focus.FocusFactory; @@ -44,86 +44,127 @@ import mezz.jei.library.recipes.RecipeManager; import mezz.jei.library.recipes.RecipeManagerInternal; import mezz.jei.library.runtime.JeiHelpers; -import mezz.jei.library.startup.StartData; +import mezz.jei.library.startup.JeiClientExecutor; import mezz.jei.library.transfer.RecipeTransferHandlerHelper; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.AbstractContainerMenu; import org.jetbrains.annotations.Unmodifiable; import java.util.List; -import java.util.concurrent.Executor; public class PluginLoader { - private final StartData data; + private final IConnectionToServer serverConnection; + private final PluginCaller pluginCaller; + private final JeiClientExecutor clientExecutor; private final LoggedTimer timer; private final IIngredientManager ingredientManager; private final JeiHelpers jeiHelpers; - public PluginLoader(StartData data, IModIdFormatConfig modIdFormatConfig, IColorHelper colorHelper, Executor clientExecutor) { - this.data = data; + public PluginLoader( + IConnectionToServer serverConnection, + PluginCaller pluginCaller, + IModIdFormatConfig modIdFormatConfig, + IColorHelper colorHelper, + JeiClientExecutor clientExecutor + ) { + this.serverConnection = serverConnection; + this.pluginCaller = pluginCaller; + this.clientExecutor = clientExecutor; this.timer = new LoggedTimer(); IPlatformFluidHelperInternal fluidHelper = Services.PLATFORM.getFluidHelper(); - List plugins = data.plugins(); SubtypeRegistration subtypeRegistration = new SubtypeRegistration(); - PluginCaller.callOnPlugins("Registering item subtypes", plugins, p -> p.registerItemSubtypes(subtypeRegistration)); - PluginCaller.callOnPlugins("Registering fluid subtypes", plugins, p -> - p.registerFluidSubtypes(subtypeRegistration, fluidHelper) + pluginCaller.callOnPlugins( + "Registering item subtypes", + p -> p.registerItemSubtypes(subtypeRegistration), + p -> p.registerItemSubtypes(subtypeRegistration, clientExecutor) + ); + pluginCaller.callOnPlugins( + "Registering fluid subtypes", + p -> p.registerFluidSubtypes(subtypeRegistration, fluidHelper), + p -> p.registerFluidSubtypes(subtypeRegistration, fluidHelper, clientExecutor) ); SubtypeInterpreters subtypeInterpreters = subtypeRegistration.getInterpreters(); SubtypeManager subtypeManager = new SubtypeManager(subtypeInterpreters); IngredientManagerBuilder ingredientManagerBuilder = new IngredientManagerBuilder(subtypeManager, colorHelper); - PluginCaller.callOnPlugins("Registering ingredients", plugins, p -> p.registerIngredients(ingredientManagerBuilder)); + pluginCaller.callOnPlugins( + "Registering ingredients", + p -> p.registerIngredients(ingredientManagerBuilder), + p -> p.registerIngredients(ingredientManagerBuilder, clientExecutor) + ); this.ingredientManager = ingredientManagerBuilder.build(); StackHelper stackHelper = new StackHelper(subtypeManager); GuiHelper guiHelper = new GuiHelper(ingredientManager); FocusFactory focusFactory = new FocusFactory(ingredientManager); IModIdHelper modIdHelper = new ModIdHelper(modIdFormatConfig, ingredientManager); - this.jeiHelpers = new JeiHelpers(guiHelper, stackHelper, modIdHelper, focusFactory, colorHelper, ingredientManager, clientExecutor); + this.jeiHelpers = new JeiHelpers(guiHelper, stackHelper, modIdHelper, focusFactory, colorHelper, ingredientManager); } @Unmodifiable - private List> createRecipeCategories(List plugins, VanillaPlugin vanillaPlugin) { + private List> createRecipeCategories(VanillaPlugin vanillaPlugin) { RecipeCategoryRegistration recipeCategoryRegistration = new RecipeCategoryRegistration(jeiHelpers); - PluginCaller.callOnPlugins("Registering categories", plugins, p -> p.registerCategories(recipeCategoryRegistration)); + pluginCaller.callOnPlugins( + "Registering categories", + p -> p.registerCategories(recipeCategoryRegistration), + p ->p.registerCategories(recipeCategoryRegistration, clientExecutor) + ); CraftingRecipeCategory craftingCategory = vanillaPlugin.getCraftingCategory() .orElseThrow(() -> new NullPointerException("vanilla crafting category")); VanillaCategoryExtensionRegistration vanillaCategoryExtensionRegistration = new VanillaCategoryExtensionRegistration(craftingCategory, jeiHelpers); - PluginCaller.callOnPlugins("Registering vanilla category extensions", plugins, p -> p.registerVanillaCategoryExtensions(vanillaCategoryExtensionRegistration)); + pluginCaller.callOnPlugins( + "Registering vanilla category extensions", + p -> p.registerVanillaCategoryExtensions(vanillaCategoryExtensionRegistration), + p -> p.registerVanillaCategoryExtensions(vanillaCategoryExtensionRegistration, clientExecutor) + ); return recipeCategoryRegistration.getRecipeCategories(); } - public IScreenHelper createGuiScreenHelper(List plugins, IJeiHelpers jeiHelpers) { + public IScreenHelper createGuiScreenHelper(IJeiHelpers jeiHelpers) { GuiHandlerRegistration guiHandlerRegistration = new GuiHandlerRegistration(jeiHelpers); - PluginCaller.callOnPlugins("Registering gui handlers", plugins, p -> p.registerGuiHandlers(guiHandlerRegistration)); + pluginCaller.callOnPlugins( + "Registering gui handlers", + p -> p.registerGuiHandlers(guiHandlerRegistration), + p -> p.registerGuiHandlers(guiHandlerRegistration, clientExecutor) + ); return guiHandlerRegistration.createGuiScreenHelper(ingredientManager); } - public ImmutableTable, RecipeType, IRecipeTransferHandler> createRecipeTransferHandlers(List plugins) { + public ImmutableTable, RecipeType, IRecipeTransferHandler> createRecipeTransferHandlers() { IStackHelper stackHelper = jeiHelpers.getStackHelper(); IRecipeTransferHandlerHelper handlerHelper = new RecipeTransferHandlerHelper(stackHelper); - RecipeTransferRegistration recipeTransferRegistration = new RecipeTransferRegistration(stackHelper, handlerHelper, this.jeiHelpers, data.serverConnection()); - PluginCaller.callOnPlugins("Registering recipes transfer handlers", plugins, p -> p.registerRecipeTransferHandlers(recipeTransferRegistration)); + RecipeTransferRegistration recipeTransferRegistration = new RecipeTransferRegistration(stackHelper, handlerHelper, jeiHelpers, serverConnection); + pluginCaller.callOnPlugins( + "Registering recipes transfer handlers", + p -> p.registerRecipeTransferHandlers(recipeTransferRegistration), + p -> p.registerRecipeTransferHandlers(recipeTransferRegistration, clientExecutor) + ); return recipeTransferRegistration.getRecipeTransferHandlers(); } public RecipeManager createRecipeManager( - List plugins, VanillaPlugin vanillaPlugin, RecipeCategorySortingConfig recipeCategorySortingConfig, IModIdHelper modIdHelper, IIngredientVisibility ingredientVisibility ) { - List> recipeCategories = createRecipeCategories(plugins, vanillaPlugin); + List> recipeCategories = createRecipeCategories(vanillaPlugin); RecipeCatalystRegistration recipeCatalystRegistration = new RecipeCatalystRegistration(ingredientManager, jeiHelpers); - PluginCaller.callOnPlugins("Registering recipe catalysts", plugins, p -> p.registerRecipeCatalysts(recipeCatalystRegistration)); + pluginCaller.callOnPlugins( + "Registering recipe catalysts", + p -> p.registerRecipeCatalysts(recipeCatalystRegistration), + p -> p.registerRecipeCatalysts(recipeCatalystRegistration, clientExecutor) + ); ImmutableListMultimap> recipeCatalysts = recipeCatalystRegistration.getRecipeCatalysts(); AdvancedRegistration advancedRegistration = new AdvancedRegistration(jeiHelpers); - PluginCaller.callOnPlugins("Registering advanced plugins", plugins, p -> p.registerAdvanced(advancedRegistration)); + pluginCaller.callOnPlugins( + "Registering advanced plugins", + p -> p.registerAdvanced(advancedRegistration), + p -> p.registerAdvanced(advancedRegistration, clientExecutor) + ); List recipeManagerPlugins = advancedRegistration.getRecipeManagerPlugins(); timer.start("Building recipe registry"); @@ -139,10 +180,14 @@ public RecipeManager createRecipeManager( VanillaRecipeFactory vanillaRecipeFactory = new VanillaRecipeFactory(ingredientManager); RecipeRegistration recipeRegistration = new RecipeRegistration(jeiHelpers, ingredientManager, ingredientVisibility, vanillaRecipeFactory, recipeManagerInternal); - PluginCaller.callOnPlugins("Registering recipes", plugins, p -> p.registerRecipes(recipeRegistration)); + pluginCaller.callOnPlugins( + "Registering recipes", + p -> p.registerRecipes(recipeRegistration), + p -> p.registerRecipes(recipeRegistration, clientExecutor) + ); Textures textures = Internal.getTextures(); - return new RecipeManager(recipeManagerInternal, modIdHelper, ingredientManager, textures, ingredientVisibility); + return new RecipeManager(recipeManagerInternal, modIdHelper, ingredientManager, textures, ingredientVisibility, clientExecutor); } public IIngredientManager getIngredientManager() { diff --git a/Library/src/main/java/mezz/jei/library/plugins/debug/JeiDebugPlugin.java b/Library/src/main/java/mezz/jei/library/plugins/debug/JeiDebugPlugin.java index 1f02f1c7a..d1df62074 100644 --- a/Library/src/main/java/mezz/jei/library/plugins/debug/JeiDebugPlugin.java +++ b/Library/src/main/java/mezz/jei/library/plugins/debug/JeiDebugPlugin.java @@ -23,7 +23,6 @@ import mezz.jei.common.platform.IPlatformRegistry; import mezz.jei.common.platform.IPlatformScreenHelper; import mezz.jei.common.platform.Services; -import mezz.jei.common.util.ErrorUtil; import mezz.jei.common.util.MathUtil; import mezz.jei.library.plugins.jei.ingredients.DebugIngredient; import mezz.jei.library.plugins.jei.ingredients.DebugIngredientHelper; @@ -236,7 +235,6 @@ private void registerRecipeCatalysts(IRecipeCatalystRegistration registratio @Override public void onRuntimeAvailable(IJeiRuntime jeiRuntime) { if (DebugConfig.isDebugModeEnabled()) { - ErrorUtil.assertMainThread(); if (debugRecipeCategory != null) { debugRecipeCategory.setRuntime(jeiRuntime); } diff --git a/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java b/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java index b5d62db57..65fc5581c 100644 --- a/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java +++ b/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java @@ -1,25 +1,28 @@ package mezz.jei.library.plugins.jei; -import mezz.jei.api.IModPlugin; -import mezz.jei.api.JeiPlugin; +import mezz.jei.api.IAsyncModPlugin; +import mezz.jei.api.JeiAsyncPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.helpers.IGuiHelper; import mezz.jei.api.helpers.IJeiHelpers; import mezz.jei.api.registration.IRecipeCategoryRegistration; +import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.common.Internal; import mezz.jei.common.gui.textures.Textures; import mezz.jei.library.plugins.jei.info.IngredientInfoRecipeCategory; import net.minecraft.resources.ResourceLocation; -@JeiPlugin -public class JeiInternalPlugin implements IModPlugin { +import java.util.concurrent.CompletableFuture; + +@JeiAsyncPlugin +public class JeiInternalPlugin implements IAsyncModPlugin { @Override public ResourceLocation getPluginUid() { return new ResourceLocation(ModIds.JEI_ID, "internal"); } @Override - public void registerCategories(IRecipeCategoryRegistration registration) { + public CompletableFuture registerCategories(IRecipeCategoryRegistration registration, IJeiClientExecutor clientExecutor) { IJeiHelpers jeiHelpers = registration.getJeiHelpers(); IGuiHelper guiHelper = jeiHelpers.getGuiHelper(); Textures textures = Internal.getTextures(); @@ -27,5 +30,6 @@ public void registerCategories(IRecipeCategoryRegistration registration) { registration.addRecipeCategories( new IngredientInfoRecipeCategory(guiHelper, textures) ); + return CompletableFuture.completedFuture(null); } } diff --git a/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java b/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java index 6ca9de3c5..3f335925a 100644 --- a/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java +++ b/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java @@ -1,7 +1,7 @@ package mezz.jei.library.plugins.vanilla; -import mezz.jei.api.IModPlugin; -import mezz.jei.api.JeiPlugin; +import mezz.jei.api.IAsyncModPlugin; +import mezz.jei.api.JeiAsyncPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.constants.RecipeTypes; import mezz.jei.api.constants.VanillaTypes; @@ -26,6 +26,7 @@ import mezz.jei.api.registration.ISubtypeRegistration; import mezz.jei.api.registration.IVanillaCategoryExtensionRegistration; import mezz.jei.api.runtime.IIngredientManager; +import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.common.Internal; import mezz.jei.common.gui.textures.Textures; import mezz.jei.common.platform.IPlatformFluidHelperInternal; @@ -111,11 +112,12 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import java.util.stream.Stream; -@JeiPlugin -public class VanillaPlugin implements IModPlugin { +@JeiAsyncPlugin +public class VanillaPlugin implements IAsyncModPlugin { private static final Logger LOGGER = LogManager.getLogger(); @Nullable @@ -139,7 +141,7 @@ public ResourceLocation getPluginUid() { } @Override - public void registerItemSubtypes(ISubtypeRegistration registration) { + public CompletableFuture registerItemSubtypes(ISubtypeRegistration registration, IJeiClientExecutor clientExecutor) { registration.registerSubtypeInterpreter(Items.TIPPED_ARROW, PotionSubtypeInterpreter.INSTANCE); registration.registerSubtypeInterpreter(Items.POTION, PotionSubtypeInterpreter.INSTANCE); registration.registerSubtypeInterpreter(Items.SPLASH_POTION, PotionSubtypeInterpreter.INSTANCE); @@ -161,10 +163,11 @@ public void registerItemSubtypes(ISubtypeRegistration registration) { enchantmentNames.sort(null); return enchantmentNames.toString(); }); + return CompletableFuture.completedFuture(null); } @Override - public void registerIngredients(IModIngredientRegistration registration) { + public CompletableFuture registerIngredients(IModIngredientRegistration registration, IJeiClientExecutor clientExecutor) { ISubtypeManager subtypeManager = registration.getSubtypeManager(); StackHelper stackHelper = new StackHelper(subtypeManager); @@ -176,6 +179,7 @@ public void registerIngredients(IModIngredientRegistration registration) { IPlatformFluidHelperInternal platformFluidHelper = Services.PLATFORM.getFluidHelper(); registerFluidIngredients(registration, platformFluidHelper); + return CompletableFuture.completedFuture(null); } private void registerFluidIngredients(IModIngredientRegistration registration, IPlatformFluidHelperInternal platformFluidHelper) { @@ -191,7 +195,7 @@ private void registerFluidIngredients(IModIngredientRegistration registratio } @Override - public void registerCategories(IRecipeCategoryRegistration registration) { + public CompletableFuture registerCategories(IRecipeCategoryRegistration registration, IJeiClientExecutor clientExecutor) { Textures textures = Internal.getTextures(); IJeiHelpers jeiHelpers = registration.getJeiHelpers(); IGuiHelper guiHelper = jeiHelpers.getGuiHelper(); @@ -208,16 +212,18 @@ public void registerCategories(IRecipeCategoryRegistration registration) { new BrewingRecipeCategory(guiHelper), new AnvilRecipeCategory(guiHelper) ); + return CompletableFuture.completedFuture(null); } @Override - public void registerVanillaCategoryExtensions(IVanillaCategoryExtensionRegistration registration) { + public CompletableFuture registerVanillaCategoryExtensions(IVanillaCategoryExtensionRegistration registration, IJeiClientExecutor clientExecutor) { IExtendableRecipeCategory craftingCategory = registration.getCraftingCategory(); craftingCategory.addCategoryExtension(CraftingRecipe.class, r -> !r.isSpecial(), CraftingCategoryExtension::new); + return CompletableFuture.completedFuture(null); } @Override - public void registerRecipes(IRecipeRegistration registration) { + public CompletableFuture registerRecipes(IRecipeRegistration registration, IJeiClientExecutor clientExecutor) { ErrorUtil.checkNotNull(craftingCategory, "craftingCategory"); ErrorUtil.checkNotNull(stonecuttingCategory, "stonecuttingCategory"); ErrorUtil.checkNotNull(furnaceCategory, "furnaceCategory"); @@ -254,10 +260,11 @@ public void registerRecipes(IRecipeRegistration registration) { List brewingRecipes = recipeHelper.getBrewingRecipes(ingredientManager, vanillaRecipeFactory); brewingRecipes.sort(Comparator.comparingInt(IJeiBrewingRecipe::getBrewingSteps)); registration.addRecipes(RecipeTypes.BREWING, brewingRecipes); + return CompletableFuture.completedFuture(null); } @Override - public void registerGuiHandlers(IGuiHandlerRegistration registration) { + public CompletableFuture registerGuiHandlers(IGuiHandlerRegistration registration, IJeiClientExecutor clientExecutor) { registration.addRecipeClickArea(CraftingScreen.class, 88, 32, 28, 23, RecipeTypes.CRAFTING); registration.addRecipeClickArea(InventoryScreen.class, 137, 29, 10, 13, RecipeTypes.CRAFTING); registration.addRecipeClickArea(BrewingStandScreen.class, 97, 16, 14, 30, RecipeTypes.BREWING); @@ -271,10 +278,11 @@ public void registerGuiHandlers(IGuiHandlerRegistration registration) { registration.addGuiContainerHandler(CraftingScreen.class, new RecipeBookGuiHandler<>()); registration.addGuiContainerHandler(InventoryScreen.class, new RecipeBookGuiHandler<>()); registration.addGuiContainerHandler(AbstractFurnaceScreen.class, new RecipeBookGuiHandler<>()); + return CompletableFuture.completedFuture(null); } @Override - public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) { + public CompletableFuture registerRecipeTransferHandlers(IRecipeTransferRegistration registration, IJeiClientExecutor clientExecutor) { registration.addRecipeTransferHandler(CraftingMenu.class, MenuType.CRAFTING, RecipeTypes.CRAFTING, 1, 9, 10, 36); registration.addRecipeTransferHandler(FurnaceMenu.class, MenuType.FURNACE, RecipeTypes.SMELTING, 0, 1, 3, 36); registration.addRecipeTransferHandler(FurnaceMenu.class, MenuType.FURNACE, RecipeTypes.FUELING, 1, 1, 3, 36); @@ -289,10 +297,11 @@ public void registerRecipeTransferHandlers(IRecipeTransferRegistration registrat IRecipeTransferHandlerHelper transferHelper = registration.getTransferHelper(); PlayerRecipeTransferHandler recipeTransferHandler = new PlayerRecipeTransferHandler(transferHelper); registration.addRecipeTransferHandler(recipeTransferHandler, RecipeTypes.CRAFTING); + return CompletableFuture.completedFuture(null); } @Override - public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { + public CompletableFuture registerRecipeCatalysts(IRecipeCatalystRegistration registration, IJeiClientExecutor clientExecutor) { registration.addRecipeCatalyst(new ItemStack(Blocks.CRAFTING_TABLE), RecipeTypes.CRAFTING); registration.addRecipeCatalyst(new ItemStack(Blocks.STONECUTTER), RecipeTypes.STONECUTTING); registration.addRecipeCatalyst(new ItemStack(Blocks.FURNACE), RecipeTypes.SMELTING, RecipeTypes.FUELING); @@ -305,6 +314,7 @@ public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { registration.addRecipeCatalyst(new ItemStack(Blocks.ANVIL), RecipeTypes.ANVIL); registration.addRecipeCatalyst(new ItemStack(Blocks.SMITHING_TABLE), RecipeTypes.SMITHING); registration.addRecipeCatalyst(new ItemStack(Blocks.COMPOSTER), RecipeTypes.COMPOSTING); + return CompletableFuture.completedFuture(null); } public Optional getCraftingCategory() { diff --git a/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java b/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java index da27011c7..146aa5819 100644 --- a/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java +++ b/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java @@ -14,6 +14,7 @@ import mezz.jei.api.recipe.category.IRecipeCategory; import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.api.runtime.IIngredientVisibility; +import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.common.gui.textures.Textures; import mezz.jei.common.util.ErrorUtil; import mezz.jei.library.gui.ingredients.RecipeSlot; @@ -31,13 +32,22 @@ public class RecipeManager implements IRecipeManager { private final IIngredientManager ingredientManager; private final Textures textures; private final IIngredientVisibility ingredientVisibility; - - public RecipeManager(RecipeManagerInternal internal, IModIdHelper modIdHelper, IIngredientManager ingredientManager, Textures textures, IIngredientVisibility ingredientVisibility) { + private final IJeiClientExecutor clientExecutor; + + public RecipeManager( + RecipeManagerInternal internal, + IModIdHelper modIdHelper, + IIngredientManager ingredientManager, + Textures textures, + IIngredientVisibility ingredientVisibility, + IJeiClientExecutor clientExecutor + ) { this.internal = internal; this.modIdHelper = modIdHelper; this.ingredientManager = ingredientManager; this.textures = textures; this.ingredientVisibility = ingredientVisibility; + this.clientExecutor = clientExecutor; } @Override @@ -60,9 +70,7 @@ public IRecipeCatalystLookup createRecipeCatalystLookup(RecipeType recipeType public void addRecipes(RecipeType recipeType, List recipes) { ErrorUtil.checkNotNull(recipeType, "recipeType"); ErrorUtil.checkNotNull(recipes, "recipes"); - ErrorUtil.assertMainThread(); - - internal.addRecipes(recipeType, recipes); + clientExecutor.runOnClientThread(() -> internal.addRecipes(recipeType, recipes)); } @Override @@ -92,30 +100,26 @@ public IRecipeSlotDrawable createRecipeSlotDrawable(RecipeIngredientRole role, L public void hideRecipes(RecipeType recipeType, Collection recipes) { ErrorUtil.checkNotNull(recipes, "recipe"); ErrorUtil.checkNotNull(recipeType, "recipeType"); - ErrorUtil.assertMainThread(); - internal.hideRecipes(recipeType, recipes); + clientExecutor.runOnClientThread(() -> internal.hideRecipes(recipeType, recipes)); } @Override public void unhideRecipes(RecipeType recipeType, Collection recipes) { ErrorUtil.checkNotNull(recipes, "recipe"); ErrorUtil.checkNotNull(recipeType, "recipeType"); - ErrorUtil.assertMainThread(); - internal.unhideRecipes(recipeType, recipes); + clientExecutor.runOnClientThread(() -> internal.unhideRecipes(recipeType, recipes)); } @Override public void hideRecipeCategory(RecipeType recipeType) { ErrorUtil.checkNotNull(recipeType, "recipeType"); - ErrorUtil.assertMainThread(); - internal.hideRecipeCategory(recipeType); + clientExecutor.runOnClientThread(() -> internal.hideRecipeCategory(recipeType)); } @Override public void unhideRecipeCategory(RecipeType recipeType) { ErrorUtil.checkNotNull(recipeType, "recipeType"); - ErrorUtil.assertMainThread(); - internal.unhideRecipeCategory(recipeType); + clientExecutor.runOnClientThread(() -> internal.unhideRecipeCategory(recipeType)); } @Override diff --git a/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java b/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java index dcef66de2..5e5ed0f03 100644 --- a/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java +++ b/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java @@ -117,7 +117,7 @@ private void addRecipes(RecipeTypeData recipeTypeData, Collection reci if (ingredientSupplier == null) { return false; } - JeiStartTask.checkStartInterruption(); + JeiStartTask.interruptIfCanceled(); return addRecipe(recipeCategory, recipe, ingredientSupplier); }) .toList(); diff --git a/Library/src/main/java/mezz/jei/library/recipes/collect/RecipeTypeDataMap.java b/Library/src/main/java/mezz/jei/library/recipes/collect/RecipeTypeDataMap.java index 164f2ea07..7a49dacb1 100644 --- a/Library/src/main/java/mezz/jei/library/recipes/collect/RecipeTypeDataMap.java +++ b/Library/src/main/java/mezz/jei/library/recipes/collect/RecipeTypeDataMap.java @@ -7,11 +7,13 @@ import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.Unmodifiable; +import javax.annotation.concurrent.ThreadSafe; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +@ThreadSafe public class RecipeTypeDataMap { @Unmodifiable private final Map> uidMap; diff --git a/Library/src/main/java/mezz/jei/library/runtime/JeiHelpers.java b/Library/src/main/java/mezz/jei/library/runtime/JeiHelpers.java index d67103163..d3452e9c1 100644 --- a/Library/src/main/java/mezz/jei/library/runtime/JeiHelpers.java +++ b/Library/src/main/java/mezz/jei/library/runtime/JeiHelpers.java @@ -18,7 +18,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Optional; -import java.util.concurrent.Executor; public class JeiHelpers implements IJeiHelpers { private final GuiHelper guiHelper; @@ -28,7 +27,6 @@ public class JeiHelpers implements IJeiHelpers { private final IColorHelper colorHelper; private final IIngredientManager ingredientManager; private final IPlatformFluidHelper platformFluidHelper; - private final Executor clientExecutor; private @Nullable Collection> recipeCategories; public JeiHelpers( @@ -37,8 +35,7 @@ public JeiHelpers( IModIdHelper modIdHelper, IFocusFactory focusFactory, IColorHelper colorHelper, - IIngredientManager ingredientManager, - Executor clientExecutor + IIngredientManager ingredientManager ) { this.guiHelper = guiHelper; this.stackHelper = stackHelper; @@ -47,7 +44,6 @@ public JeiHelpers( this.colorHelper = colorHelper; this.ingredientManager = ingredientManager; this.platformFluidHelper = Services.PLATFORM.getFluidHelper(); - this.clientExecutor = clientExecutor; } public void setRecipeCategories(Collection> recipeCategories) { @@ -98,9 +94,4 @@ public Optional> getRecipeType(ResourceLocation uid) { public IIngredientManager getIngredientManager() { return ingredientManager; } - - @Override - public Executor getClientExecutor() { - return clientExecutor; - } } diff --git a/Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java b/Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java new file mode 100644 index 000000000..65e9633bb --- /dev/null +++ b/Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java @@ -0,0 +1,38 @@ +package mezz.jei.library.startup; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.renderer.texture.Tickable; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +public final class ClientTaskExecutor implements Executor, Tickable { + private static final long TICK_BUDGET = TimeUnit.MILLISECONDS.toNanos(2); + + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Override + public void tick() { + final long startTime = System.nanoTime(); + do { + Runnable r = this.taskQueue.poll(); + if (r != null) { + r.run(); + } else { + return; + } + } while ((System.nanoTime() - startTime) < TICK_BUDGET); + } + + @Override + public void execute(Runnable runnable) { + if (RenderSystem.isOnRenderThreadOrInit()) { + // we can't queue on the client render thread, + // it would block forever waiting for the next tick to happen + runnable.run(); + } else { + this.taskQueue.add(runnable); + } + } +} diff --git a/Library/src/main/java/mezz/jei/library/startup/IPluginFinder.java b/Library/src/main/java/mezz/jei/library/startup/IPluginFinder.java new file mode 100644 index 000000000..f7ca0da75 --- /dev/null +++ b/Library/src/main/java/mezz/jei/library/startup/IPluginFinder.java @@ -0,0 +1,13 @@ +package mezz.jei.library.startup; + +import mezz.jei.api.IAsyncModPlugin; +import mezz.jei.api.IModPlugin; +import mezz.jei.api.IRuntimePlugin; + +import java.util.List; + +public interface IPluginFinder { + List getModPlugins(); + List getAsyncModPlugins(); + List getRuntimePlugins(); +} diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiClientExecutor.java b/Library/src/main/java/mezz/jei/library/startup/JeiClientExecutor.java new file mode 100644 index 000000000..c13e114df --- /dev/null +++ b/Library/src/main/java/mezz/jei/library/startup/JeiClientExecutor.java @@ -0,0 +1,38 @@ +package mezz.jei.library.startup; + +import mezz.jei.api.runtime.IJeiClientExecutor; +import net.minecraft.client.renderer.texture.Tickable; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Supplier; + +public final class JeiClientExecutor implements IJeiClientExecutor, Tickable { + private final Executor executor; + + public JeiClientExecutor(Executor executor) { + this.executor = executor; + } + + @Override + public void tick() { + if (executor instanceof Tickable tickable) { + tickable.tick(); + } + } + + @Override + public Executor getExecutor() { + return executor; + } + + @Override + public CompletableFuture runOnClientThread(Runnable runnable) { + return CompletableFuture.runAsync(runnable, executor); + } + + @Override + public CompletableFuture runOnClientThread(Supplier supplier) { + return CompletableFuture.supplyAsync(supplier, executor); + } +} diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index 9866f5d99..66fa21b7d 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -1,7 +1,7 @@ package mezz.jei.library.startup; import com.google.common.collect.ImmutableTable; -import com.google.common.util.concurrent.MoreExecutors; +import mezz.jei.api.IAsyncModPlugin; import mezz.jei.api.IModPlugin; import mezz.jei.api.helpers.IColorHelper; import mezz.jei.api.helpers.IModIdHelper; @@ -13,16 +13,17 @@ import mezz.jei.api.runtime.IScreenHelper; import mezz.jei.common.Internal; import mezz.jei.common.async.JeiStartTask; +import mezz.jei.common.config.ClientConfig; import mezz.jei.common.config.ConfigManager; import mezz.jei.common.config.DebugConfig; import mezz.jei.common.config.IClientToggleState; import mezz.jei.common.config.JeiClientConfigs; +import mezz.jei.common.config.file.ConfigSchemaBuilder; import mezz.jei.common.config.file.FileWatcher; +import mezz.jei.common.config.file.IConfigSchemaBuilder; import mezz.jei.common.platform.Services; import mezz.jei.common.util.ErrorUtil; import mezz.jei.core.util.LoggedTimer; -import mezz.jei.common.config.file.ConfigSchemaBuilder; -import mezz.jei.common.config.file.IConfigSchemaBuilder; import mezz.jei.library.color.ColorHelper; import mezz.jei.library.config.ColorNameConfig; import mezz.jei.library.config.EditModeConfig; @@ -41,24 +42,18 @@ import mezz.jei.library.runtime.JeiHelpers; import mezz.jei.library.runtime.JeiRuntime; import net.minecraft.client.Minecraft; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.AbstractContainerMenu; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import java.nio.file.Path; import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.CompletableFuture; public final class JeiStarter { private static final Logger LOGGER = LogManager.getLogger(); private final StartData data; - private final List plugins; private final VanillaPlugin vanillaPlugin; private final ModIdFormatConfig modIdFormatConfig; private final ColorNameConfig colorNameConfig; @@ -66,19 +61,22 @@ public final class JeiStarter { @SuppressWarnings("FieldCanBeLocal") private final FileWatcher fileWatcher = new FileWatcher("JEI Config File Watcher"); private final ConfigManager configManager; - private Executor taskExecutor; + private final JeiClientExecutor clientExecutor; + private final PluginCaller pluginCaller; + private final JeiClientConfigs jeiClientConfigs; private JeiStartTask currentStartTask = null; public JeiStarter(StartData data) { ErrorUtil.checkNotEmpty(data.plugins(), "plugins"); this.data = data; - this.plugins = data.plugins(); - this.vanillaPlugin = PluginHelper.getPluginWithClass(VanillaPlugin.class, plugins) + List plugins = data.plugins(); + List asyncModPlugins = data.asyncPlugins(); + this.vanillaPlugin = PluginHelper.getPluginWithClass(VanillaPlugin.class, plugins, asyncModPlugins) .orElseThrow(() -> new IllegalStateException("vanilla plugin not found")); - JeiInternalPlugin jeiInternalPlugin = PluginHelper.getPluginWithClass(JeiInternalPlugin.class, plugins) + JeiInternalPlugin jeiInternalPlugin = PluginHelper.getPluginWithClass(JeiInternalPlugin.class, plugins, asyncModPlugins) .orElse(null); - PluginHelper.sortPlugins(plugins, vanillaPlugin, jeiInternalPlugin); + PluginHelper.sortPlugins(asyncModPlugins, vanillaPlugin, jeiInternalPlugin); Path configDir = Services.PLATFORM.getConfigHelper().createJeiConfigDir(); @@ -96,15 +94,28 @@ public JeiStarter(StartData data) { this.colorNameConfig = new ColorNameConfig(colorFileBuilder); colorFileBuilder.build().register(fileWatcher, configManager); - JeiClientConfigs jeiClientConfigs = new JeiClientConfigs(configDir.resolve("jei-client.ini")); - jeiClientConfigs.register(fileWatcher, configManager); + this.jeiClientConfigs = new JeiClientConfigs(configDir.resolve("jei-client.ini")); + this.jeiClientConfigs.register(fileWatcher, configManager); Internal.setJeiClientConfigs(jeiClientConfigs); fileWatcher.start(); this.recipeCategorySortingConfig = new RecipeCategorySortingConfig(configDir.resolve("recipe-category-sort-order.ini")); - PluginCaller.callOnPlugins("Sending ConfigManager", plugins, p -> p.onConfigManagerAvailable(configManager)); + this.clientExecutor = new JeiClientExecutor(new ClientTaskExecutor()); + this.pluginCaller = new PluginCaller( + data.plugins(), + data.asyncPlugins(), + data.runtimePlugins(), + clientExecutor, + jeiClientConfigs.getClientConfig() + ); + + pluginCaller.callOnPlugins( + "Sending ConfigManager", + p -> p.onConfigManagerAvailable(configManager), + p -> p.onConfigManagerAvailable(configManager, clientExecutor) + ); } /** @@ -112,7 +123,7 @@ public JeiStarter(StartData data) { * the main thread. */ public void start() { - if(currentStartTask != null) { + if (currentStartTask != null) { LOGGER.error("JEI start requested but it is already starting."); return; } @@ -121,26 +132,28 @@ public void start() { LOGGER.error("Failed to start JEI, there is no Minecraft client level."); return; } - JeiStartTask task = new JeiStartTask(this::doActualStart); - if(Internal.getJeiClientConfigs().getClientConfig().isAsyncLoadingEnabled()) { - currentStartTask = task; - this.taskExecutor = new ClientTaskExecutor(); - task.start(); + + if (jeiClientConfigs.getClientConfig().getAsyncLoadingEnabled()) { + currentStartTask = new JeiStartTask(this::doActualStart); + currentStartTask.start(); } else { - this.taskExecutor = MoreExecutors.directExecutor(); - task.run(); + doActualStart(); } } private void doActualStart() { LoggedTimer totalTime = new LoggedTimer(); - totalTime.start("Starting JEI" + ((Thread.currentThread() instanceof JeiStartTask) ? " (asynchronously)" : "")); + if (Thread.currentThread() instanceof JeiStartTask) { + totalTime.start("Starting JEI asynchronously"); + } else { + totalTime.start("Starting JEI synchronously"); + } IColorHelper colorHelper = new ColorHelper(colorNameConfig); IClientToggleState toggleState = Internal.getClientToggleState(); - PluginLoader pluginLoader = new PluginLoader(data, modIdFormatConfig, colorHelper, taskExecutor); + PluginLoader pluginLoader = new PluginLoader(data.serverConnection(), pluginCaller, modIdFormatConfig, colorHelper, clientExecutor); JeiHelpers jeiHelpers = pluginLoader.getJeiHelpers(); IModIdHelper modIdHelper = jeiHelpers.getModIdHelper(); @@ -160,19 +173,18 @@ private void doActualStart() { ); RecipeManager recipeManager = pluginLoader.createRecipeManager( - plugins, vanillaPlugin, recipeCategorySortingConfig, modIdHelper, ingredientVisibility ); ImmutableTable, RecipeType, IRecipeTransferHandler> recipeTransferHandlers = - pluginLoader.createRecipeTransferHandlers(plugins); + pluginLoader.createRecipeTransferHandlers(); IRecipeTransferManager recipeTransferManager = new RecipeTransferManager(recipeTransferHandlers); LoggedTimer timer = new LoggedTimer(); timer.start("Building runtime"); - IScreenHelper screenHelper = pluginLoader.createGuiScreenHelper(plugins, jeiHelpers); + IScreenHelper screenHelper = pluginLoader.createGuiScreenHelper(jeiHelpers); RuntimeRegistration runtimeRegistration = new RuntimeRegistration( recipeManager, @@ -183,7 +195,16 @@ private void doActualStart() { recipeTransferManager, screenHelper ); - PluginCaller.callOnPlugins("Registering Runtime", plugins, p -> p.registerRuntime(runtimeRegistration)); + //noinspection removal + pluginCaller.callOnPlugins( + "Registering Runtime (legacy)", + p -> p.registerRuntime(runtimeRegistration), + p -> CompletableFuture.completedFuture(null) + ); + pluginCaller.callOnRuntimePlugins( + "Registering Runtime", + p -> p.registerRuntime(runtimeRegistration, clientExecutor) + ); JeiRuntime jeiRuntime = new JeiRuntime( recipeManager, @@ -202,50 +223,34 @@ private void doActualStart() { ); timer.stop(); - PluginCaller.callOnPlugins("Sending Runtime", plugins, p -> p.onRuntimeAvailable(jeiRuntime)); + pluginCaller.callOnPlugins( + "Sending Runtime", + p -> p.onRuntimeAvailable(jeiRuntime), + p -> p.onRuntimeAvailable(jeiRuntime, clientExecutor) + ); + pluginCaller.callOnRuntimePlugins( + "Registering Runtime", + p -> p.onRuntimeAvailable(jeiRuntime, clientExecutor) + ); totalTime.stop(); } public void stop() { LOGGER.info("Stopping JEI"); - if(currentStartTask != null) { - currentStartTask.interruptStart(); + if (currentStartTask != null) { + currentStartTask.cancelStart(); Minecraft.getInstance().managedBlock(() -> !currentStartTask.isAlive()); currentStartTask = null; } - List plugins = data.plugins(); - PluginCaller.callOnPlugins("Sending Runtime Unavailable", plugins, IModPlugin::onRuntimeUnavailable); - } - - static final class ClientTaskExecutor implements Executor { - private static final long TICK_BUDGET = TimeUnit.MILLISECONDS.toNanos(2); - - final ConcurrentLinkedQueue startTasks = new ConcurrentLinkedQueue<>(); - - public void tick() { - long startTime = System.nanoTime(); - do { - Runnable r = this.startTasks.poll(); - if(r != null) - r.run(); - else - break; - } while((System.nanoTime() - startTime) < TICK_BUDGET); - } - - @Override - public void execute(@NotNull Runnable runnable) { - // sanity check, in case a task is submitted from the main thread to the main thread - if(Minecraft.getInstance().isSameThread()) - runnable.run(); - else - this.startTasks.add(runnable); - } + pluginCaller.callOnPlugins( + "Sending Runtime Unavailable", + IModPlugin::onRuntimeUnavailable, + p -> p.onRuntimeUnavailable(clientExecutor) + ); } public void tick() { - if(this.taskExecutor instanceof ClientTaskExecutor) - ((ClientTaskExecutor)this.taskExecutor).tick(); + this.clientExecutor.tick(); } } diff --git a/Library/src/main/java/mezz/jei/library/startup/StartData.java b/Library/src/main/java/mezz/jei/library/startup/StartData.java index 64e6e16f6..666a4c97f 100644 --- a/Library/src/main/java/mezz/jei/library/startup/StartData.java +++ b/Library/src/main/java/mezz/jei/library/startup/StartData.java @@ -1,6 +1,8 @@ package mezz.jei.library.startup; +import mezz.jei.api.IAsyncModPlugin; import mezz.jei.api.IModPlugin; +import mezz.jei.api.IRuntimePlugin; import mezz.jei.common.input.IInternalKeyMappings; import mezz.jei.common.network.IConnectionToServer; @@ -8,7 +10,22 @@ public record StartData( List plugins, + List asyncPlugins, + List runtimePlugins, IConnectionToServer serverConnection, IInternalKeyMappings keyBindings ) { + public static StartData create( + IPluginFinder pluginFinder, + IConnectionToServer connectionToServer, + IInternalKeyMappings keyBindings + ) { + return new StartData( + pluginFinder.getModPlugins(), + pluginFinder.getAsyncModPlugins(), + pluginFinder.getRuntimePlugins(), + connectionToServer, + keyBindings + ); + } } diff --git a/gradle.properties b/gradle.properties index be2cfcbcf..ed16a989a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -38,7 +38,7 @@ curseHomepageUrl=https://www.curseforge.com/minecraft/mc-mods/jei jUnitVersion=5.8.2 # Version -specificationVersion=13.1.0 +specificationVersion=13.2.0 # Workaround for Spotless bug # https://github.com/diffplug/spotless/issues/834 From 0f1c7252b608e42fe71075219244733aca13a4a7 Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 14 May 2023 23:52:00 -0700 Subject: [PATCH 08/22] cleanup --- .../java/mezz/jei/common/async/JeiAsyncStartInterrupt.java | 6 ------ .../src/main/java/mezz/jei/common/async/JeiStartTask.java | 5 +++++ .../src/main/java/mezz/jei/library/startup/JeiStarter.java | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) delete mode 100644 Common/src/main/java/mezz/jei/common/async/JeiAsyncStartInterrupt.java diff --git a/Common/src/main/java/mezz/jei/common/async/JeiAsyncStartInterrupt.java b/Common/src/main/java/mezz/jei/common/async/JeiAsyncStartInterrupt.java deleted file mode 100644 index 5911c1990..000000000 --- a/Common/src/main/java/mezz/jei/common/async/JeiAsyncStartInterrupt.java +++ /dev/null @@ -1,6 +0,0 @@ -package mezz.jei.common.async; - -final class JeiAsyncStartInterrupt extends Error { - public JeiAsyncStartInterrupt() { - } -} diff --git a/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java b/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java index 23f21a971..912a1ce25 100644 --- a/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java +++ b/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java @@ -39,4 +39,9 @@ public void run() { } } + + private static final class JeiAsyncStartInterrupt extends Error { + public JeiAsyncStartInterrupt() { + } + } } diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index 66fa21b7d..92adf985e 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -13,7 +13,6 @@ import mezz.jei.api.runtime.IScreenHelper; import mezz.jei.common.Internal; import mezz.jei.common.async.JeiStartTask; -import mezz.jei.common.config.ClientConfig; import mezz.jei.common.config.ConfigManager; import mezz.jei.common.config.DebugConfig; import mezz.jei.common.config.IClientToggleState; From 1cd6d35efbceb21d39edc42a364a6c508b53bf1b Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 14 May 2023 23:52:57 -0700 Subject: [PATCH 09/22] fix calls to runtime plugins --- .../src/main/java/mezz/jei/library/startup/JeiStarter.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index 92adf985e..158b9d846 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -228,7 +228,7 @@ private void doActualStart() { p -> p.onRuntimeAvailable(jeiRuntime, clientExecutor) ); pluginCaller.callOnRuntimePlugins( - "Registering Runtime", + "Sending Runtime", p -> p.onRuntimeAvailable(jeiRuntime, clientExecutor) ); @@ -247,6 +247,10 @@ public void stop() { IModPlugin::onRuntimeUnavailable, p -> p.onRuntimeUnavailable(clientExecutor) ); + pluginCaller.callOnRuntimePlugins( + "Sending Runtime Unavailable", + p -> p.onRuntimeUnavailable(clientExecutor) + ); } public void tick() { From 411316a1210182e435abf159a7b42fc1b75ff27f Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 14 May 2023 23:54:21 -0700 Subject: [PATCH 10/22] fix 'since' tags in IAsyncModPlugin and IRuntimePlugin --- .../java/mezz/jei/api/IAsyncModPlugin.java | 30 +++++++++++++++++-- .../java/mezz/jei/api/IRuntimePlugin.java | 8 +++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java b/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java index 7bb2e2fe4..d0954c3be 100644 --- a/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java +++ b/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java @@ -36,11 +36,15 @@ public interface IAsyncModPlugin { /** * The unique ID for this mod plugin. * The namespace should be your mod's modId. + * + * @since 13.2.0 */ ResourceLocation getPluginUid(); /** * If your item has subtypes that depend on NBT or capabilities, use this to help JEI identify those subtypes correctly. + * + * @since 13.2.0 */ default CompletableFuture registerItemSubtypes(ISubtypeRegistration registration, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -50,7 +54,7 @@ default CompletableFuture registerItemSubtypes(ISubtypeRegistration regist * If your fluid has subtypes that depend on NBT or capabilities, * use this to help JEI identify those subtypes correctly. * - * @since 10.1.0 + * @since 13.2.0 */ default CompletableFuture registerFluidSubtypes(ISubtypeRegistration registration, IPlatformFluidHelper platformFluidHelper, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -58,6 +62,8 @@ default CompletableFuture registerFluidSubtypes(ISubtypeRegistration r /** * Register special ingredients, beyond the basic ItemStack and FluidStack. + * + * @since 13.2.0 */ default CompletableFuture registerIngredients(IModIngredientRegistration registration, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -66,6 +72,8 @@ default CompletableFuture registerIngredients(IModIngredientRegistration r /** * Register the categories handled by this plugin. * These are registered before recipes so they can be checked for validity. + * + * @since 13.2.0 */ default CompletableFuture registerCategories(IRecipeCategoryRegistration registration, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -74,6 +82,8 @@ default CompletableFuture registerCategories(IRecipeCategoryRegistration r /** * Register modded extensions to the vanilla crafting recipe category. * Custom crafting recipes for your mod should use this to tell JEI how they work. + * + * @since 13.2.0 */ default CompletableFuture registerVanillaCategoryExtensions(IVanillaCategoryExtensionRegistration registration, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -81,6 +91,8 @@ default CompletableFuture registerVanillaCategoryExtensions(IVanillaCatego /** * Register modded recipes. + * + * @since 13.2.0 */ default CompletableFuture registerRecipes(IRecipeRegistration registration, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -88,6 +100,8 @@ default CompletableFuture registerRecipes(IRecipeRegistration registration /** * Register recipe transfer handlers (move ingredients from the inventory into crafting GUIs). + * + * @since 13.2.0 */ default CompletableFuture registerRecipeTransferHandlers(IRecipeTransferRegistration registration, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -97,6 +111,8 @@ default CompletableFuture registerRecipeTransferHandlers(IRecipeTransferRe * Register recipe catalysts. * Recipe Catalysts are ingredients that are needed in order to craft other things. * Vanilla examples of Recipe Catalysts are the Crafting Table and Furnace. + * + * @since 13.2.0 */ default CompletableFuture registerRecipeCatalysts(IRecipeCatalystRegistration registration, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -106,6 +122,8 @@ default CompletableFuture registerRecipeCatalysts(IRecipeCatalystRegistrat * Register various GUI-related things for your mod. * This includes adding clickable areas in your guis to open JEI, * and adding areas on the screen that JEI should avoid drawing. + * + * @since 13.2.0 */ default CompletableFuture registerGuiHandlers(IGuiHandlerRegistration registration, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -113,6 +131,8 @@ default CompletableFuture registerGuiHandlers(IGuiHandlerRegistration regi /** * Register advanced features for your mod plugin. + * + * @since 13.2.0 */ default CompletableFuture registerAdvanced(IAdvancedRegistration registration, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -120,6 +140,8 @@ default CompletableFuture registerAdvanced(IAdvancedRegistration registrat /** * Called when JEI's runtime features are available, after all mods have registered. + * + * @since 13.2.0 */ default CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -127,7 +149,8 @@ default CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, IJeiC /** * Called when JEI's runtime features are no longer available, after a user quits or logs out of a world. - * @since 11.5.0 + * + * @since 13.2.0 */ default CompletableFuture onRuntimeUnavailable(IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); @@ -136,7 +159,8 @@ default CompletableFuture onRuntimeUnavailable(IJeiClientExecutor clientTh /** * Called when JEI's configs are available. * This is called early on, as soon as configs are available. - * @since 12.3.0 + * + * @since 13.2.0 */ default CompletableFuture onConfigManagerAvailable(IJeiConfigManager configManager, IJeiClientExecutor clientThreadExecutor) { return CompletableFuture.completedFuture(null); diff --git a/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java b/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java index 95f6655e6..f41a2d30a 100644 --- a/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java +++ b/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java @@ -20,11 +20,15 @@ public interface IRuntimePlugin { /** * The unique ID for this mod plugin. * The namespace should be your mod's modId. + * + * @since 13.2.0 */ ResourceLocation getPluginUid(); /** * Override the default JEI runtime. + * + * @since 13.2.0 */ default CompletableFuture registerRuntime(IRuntimeRegistration registration, IJeiClientExecutor clientExecutor) { return CompletableFuture.completedFuture(null); @@ -32,6 +36,8 @@ default CompletableFuture registerRuntime(IRuntimeRegistration registratio /** * Called when JEI's runtime features are available, after all mods have registered. + * + * @since 13.2.0 */ default CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, IJeiClientExecutor clientExecutor) { return CompletableFuture.completedFuture(null); @@ -39,6 +45,8 @@ default CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, IJeiC /** * Called when JEI's runtime features are no longer available, after a user quits or logs out of a world. + * + * @since 13.2.0 */ default CompletableFuture onRuntimeUnavailable(IJeiClientExecutor clientExecutor) { return CompletableFuture.completedFuture(null); From a33ee6a771a7bd5f592847151c8e014718c8d926 Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 14 May 2023 23:58:08 -0700 Subject: [PATCH 11/22] fix tests --- Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java b/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java index 2191ea185..d886356b8 100644 --- a/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java +++ b/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java @@ -29,13 +29,13 @@ public boolean isCheatToHotbarUsingHotkeysEnabled() { } @Override - public boolean getParallelPluginLoadingEnabled() { + public boolean getAsyncLoadingEnabled() { return false; } @Override - public List getAsyncCompatPluginUids() { - return List.of(); + public boolean getParallelPluginLoadingEnabled() { + return false; } @Override From 31a6863006ba3aab5f7ae55b2781f90da696b916 Mon Sep 17 00:00:00 2001 From: mezz Date: Mon, 15 May 2023 21:33:10 -0700 Subject: [PATCH 12/22] Simplify use of JeiPlugin --- .../java/mezz/jei/api/IRuntimePlugin.java | 2 +- .../plugins/fabric/FabricRuntimePlugin.java | 4 +- .../fabric/startup/FabricPluginFinder.java | 28 +++++---- .../plugins/forge/ForgeRuntimePlugin.java | 4 +- .../jei/forge/startup/ForgePluginFinder.java | 57 +++++++------------ .../plugins/jei/JeiInternalPlugin.java | 3 +- .../plugins/vanilla/VanillaPlugin.java | 3 +- .../jei/library/startup/IPluginFinder.java | 8 +-- .../mezz/jei/library/startup/JeiStarter.java | 2 - .../mezz/jei/library/startup/StartData.java | 6 +- 10 files changed, 47 insertions(+), 70 deletions(-) diff --git a/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java b/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java index f41a2d30a..c6dcfaeab 100644 --- a/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java +++ b/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java @@ -8,7 +8,7 @@ import java.util.concurrent.CompletableFuture; /** - * In a Forge environment, IRuntimePlugins must have the {@link JeiRuntimePlugin} annotation to get loaded by JEI. + * In a Forge environment, IRuntimePlugins must have the {@link JeiPlugin} annotation to get loaded by JEI. * * In a Fabric environment, IModPlugins must be declared under `entrypoints.jei_runtime_plugin` in `fabric.mod.json`. * See the Fabric Wiki for more information. diff --git a/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricRuntimePlugin.java b/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricRuntimePlugin.java index 3e5e5ba24..3b1c44bfa 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricRuntimePlugin.java +++ b/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricRuntimePlugin.java @@ -1,7 +1,7 @@ package mezz.jei.fabric.plugins.fabric; import mezz.jei.api.IRuntimePlugin; -import mezz.jei.api.JeiRuntimePlugin; +import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.registration.IRuntimeRegistration; import mezz.jei.api.runtime.IJeiClientExecutor; @@ -16,7 +16,7 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; -@JeiRuntimePlugin +@JeiPlugin public class FabricRuntimePlugin implements IRuntimePlugin { private static final Logger LOGGER = LogManager.getLogger(); private static @Nullable IJeiRuntime runtime; diff --git a/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java b/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java index 025c17734..cd9ec2cfe 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java +++ b/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java @@ -8,30 +8,28 @@ import net.fabricmc.loader.api.entrypoint.EntrypointContainer; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; public final class FabricPluginFinder implements IPluginFinder { - @Override - public List getModPlugins() { - return getInstances("jei_mod_plugin", IModPlugin.class); - } + private static final Map, String> entryPointKeys = Map.of( + IModPlugin.class, "jei_mod_plugin", + IAsyncModPlugin.class, "jei_async_mod_plugin", + IRuntimePlugin.class, "jei_runtime_plugin" + ); @Override - public List getAsyncModPlugins() { - return getInstances("jei_async_mod_plugin", IAsyncModPlugin.class); - } + public List getPlugins(Class pluginClass) { + String entryPointKey = entryPointKeys.get(pluginClass); + if (entryPointKey == null) { + throw new IllegalArgumentException("FabricPluginFinder does not support " + pluginClass); + } - @Override - public List getRuntimePlugins() { - return getInstances("jei_runtime_plugin", IRuntimePlugin.class); - } - - @SuppressWarnings("SameParameterValue") - private static List getInstances(String entrypointContainerKey, Class instanceClass) { FabricLoader fabricLoader = FabricLoader.getInstance(); - List> pluginContainers = fabricLoader.getEntrypointContainers(entrypointContainerKey, instanceClass); + List> pluginContainers = fabricLoader.getEntrypointContainers(entryPointKey, pluginClass); return pluginContainers.stream() .map(EntrypointContainer::getEntrypoint) .collect(Collectors.toList()); } + } diff --git a/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeRuntimePlugin.java b/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeRuntimePlugin.java index 9e4b6d2c9..b2cc46d7c 100644 --- a/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeRuntimePlugin.java +++ b/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeRuntimePlugin.java @@ -1,7 +1,7 @@ package mezz.jei.forge.plugins.forge; import mezz.jei.api.IRuntimePlugin; -import mezz.jei.api.JeiRuntimePlugin; +import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.registration.IRuntimeRegistration; import mezz.jei.api.runtime.IJeiClientExecutor; @@ -15,7 +15,7 @@ import java.util.concurrent.CompletableFuture; -@JeiRuntimePlugin +@JeiPlugin public class ForgeRuntimePlugin implements IRuntimePlugin { private static final Logger LOGGER = LogManager.getLogger(); diff --git a/Forge/src/main/java/mezz/jei/forge/startup/ForgePluginFinder.java b/Forge/src/main/java/mezz/jei/forge/startup/ForgePluginFinder.java index 5a387ba7e..3cbf34595 100644 --- a/Forge/src/main/java/mezz/jei/forge/startup/ForgePluginFinder.java +++ b/Forge/src/main/java/mezz/jei/forge/startup/ForgePluginFinder.java @@ -1,49 +1,28 @@ package mezz.jei.forge.startup; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -import mezz.jei.api.IAsyncModPlugin; -import mezz.jei.api.IRuntimePlugin; -import mezz.jei.api.JeiAsyncPlugin; -import mezz.jei.api.JeiRuntimePlugin; +import mezz.jei.api.JeiPlugin; import mezz.jei.library.startup.IPluginFinder; import net.minecraftforge.fml.ModList; import net.minecraftforge.forgespi.language.ModFileScanData; - -import mezz.jei.api.IModPlugin; -import mezz.jei.api.JeiPlugin; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.objectweb.asm.Type; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; + public final class ForgePluginFinder implements IPluginFinder { private static final Logger LOGGER = LogManager.getLogger(); - @Override - public List getModPlugins() { - return getInstances(JeiPlugin.class, IModPlugin.class); - } + private final LinkedHashSet pluginClassNames; - @Override - public List getAsyncModPlugins() { - return getInstances(JeiAsyncPlugin.class, IAsyncModPlugin.class); - } - - @Override - public List getRuntimePlugins() { - return getInstances(JeiRuntimePlugin.class, IRuntimePlugin.class); - } - - @SuppressWarnings("SameParameterValue") - private static List getInstances(Class annotationClass, Class instanceClass) { - Type annotationType = Type.getType(annotationClass); + public ForgePluginFinder() { + Type annotationType = Type.getType(JeiPlugin.class); List allScanData = ModList.get().getAllScanData(); - Set pluginClassNames = new LinkedHashSet<>(); + this.pluginClassNames = new LinkedHashSet<>(); for (ModFileScanData scanData : allScanData) { Iterable annotations = scanData.getAnnotations(); for (ModFileScanData.AnnotationData a : annotations) { @@ -53,14 +32,20 @@ private static List getInstances(Class annotationClass, Class insta } } } + } + + @Override + public List getPlugins(Class pluginClass) { List instances = new ArrayList<>(); for (String className : pluginClassNames) { try { Class asmClass = Class.forName(className); - Class asmInstanceClass = asmClass.asSubclass(instanceClass); - Constructor constructor = asmInstanceClass.getDeclaredConstructor(); - T instance = constructor.newInstance(); - instances.add(instance); + if (pluginClass.isAssignableFrom(asmClass)) { + Class asmInstanceClass = asmClass.asSubclass(pluginClass); + Constructor constructor = asmInstanceClass.getDeclaredConstructor(); + T instance = constructor.newInstance(); + instances.add(instance); + } } catch (ReflectiveOperationException | ClassCastException | LinkageError e) { LOGGER.error("Failed to load: {}", className, e); } diff --git a/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java b/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java index 65fc5581c..08f3d1fd9 100644 --- a/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java +++ b/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java @@ -2,6 +2,7 @@ import mezz.jei.api.IAsyncModPlugin; import mezz.jei.api.JeiAsyncPlugin; +import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.helpers.IGuiHelper; import mezz.jei.api.helpers.IJeiHelpers; @@ -14,7 +15,7 @@ import java.util.concurrent.CompletableFuture; -@JeiAsyncPlugin +@JeiPlugin public class JeiInternalPlugin implements IAsyncModPlugin { @Override public ResourceLocation getPluginUid() { diff --git a/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java b/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java index 3f335925a..f3e9e56af 100644 --- a/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java +++ b/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java @@ -2,6 +2,7 @@ import mezz.jei.api.IAsyncModPlugin; import mezz.jei.api.JeiAsyncPlugin; +import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.constants.RecipeTypes; import mezz.jei.api.constants.VanillaTypes; @@ -116,7 +117,7 @@ import java.util.function.Supplier; import java.util.stream.Stream; -@JeiAsyncPlugin +@JeiPlugin public class VanillaPlugin implements IAsyncModPlugin { private static final Logger LOGGER = LogManager.getLogger(); diff --git a/Library/src/main/java/mezz/jei/library/startup/IPluginFinder.java b/Library/src/main/java/mezz/jei/library/startup/IPluginFinder.java index f7ca0da75..a501fbabd 100644 --- a/Library/src/main/java/mezz/jei/library/startup/IPluginFinder.java +++ b/Library/src/main/java/mezz/jei/library/startup/IPluginFinder.java @@ -1,13 +1,7 @@ package mezz.jei.library.startup; -import mezz.jei.api.IAsyncModPlugin; -import mezz.jei.api.IModPlugin; -import mezz.jei.api.IRuntimePlugin; - import java.util.List; public interface IPluginFinder { - List getModPlugins(); - List getAsyncModPlugins(); - List getRuntimePlugins(); + List getPlugins(Class pluginClass); } diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index 158b9d846..30a9661a9 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -21,7 +21,6 @@ import mezz.jei.common.config.file.FileWatcher; import mezz.jei.common.config.file.IConfigSchemaBuilder; import mezz.jei.common.platform.Services; -import mezz.jei.common.util.ErrorUtil; import mezz.jei.core.util.LoggedTimer; import mezz.jei.library.color.ColorHelper; import mezz.jei.library.config.ColorNameConfig; @@ -67,7 +66,6 @@ public final class JeiStarter { private JeiStartTask currentStartTask = null; public JeiStarter(StartData data) { - ErrorUtil.checkNotEmpty(data.plugins(), "plugins"); this.data = data; List plugins = data.plugins(); List asyncModPlugins = data.asyncPlugins(); diff --git a/Library/src/main/java/mezz/jei/library/startup/StartData.java b/Library/src/main/java/mezz/jei/library/startup/StartData.java index 666a4c97f..3a5dc4346 100644 --- a/Library/src/main/java/mezz/jei/library/startup/StartData.java +++ b/Library/src/main/java/mezz/jei/library/startup/StartData.java @@ -21,9 +21,9 @@ public static StartData create( IInternalKeyMappings keyBindings ) { return new StartData( - pluginFinder.getModPlugins(), - pluginFinder.getAsyncModPlugins(), - pluginFinder.getRuntimePlugins(), + pluginFinder.getPlugins(IModPlugin.class), + pluginFinder.getPlugins(IAsyncModPlugin.class), + pluginFinder.getPlugins(IRuntimePlugin.class), connectionToServer, keyBindings ); From e96d72613248d91e1c4054a980e12bc8caa061f9 Mon Sep 17 00:00:00 2001 From: mezz Date: Mon, 15 May 2023 21:33:26 -0700 Subject: [PATCH 13/22] remove useless interrupt check on client thread --- Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java index 431c91ea0..a221d2f62 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java @@ -109,7 +109,6 @@ public CompletableFuture addIngredientsAsync( Stream> futures = Lists.partition(elementInfos, batchSize) .stream() .map(batch -> clientExecutor.runOnClientThread(() -> { - JeiStartTask.interruptIfCanceled(); for (IListElementInfo elementInfo : batch) { this.addIngredient(elementInfo); } From 6f03577f10ed2b771cb8f9c9fdc80fe783526faf Mon Sep 17 00:00:00 2001 From: mezz Date: Sat, 20 May 2023 12:23:26 -0700 Subject: [PATCH 14/22] Only allow one runtime plugin at a time. Use JeiPlugin annotation for all types of plugins --- .../java/mezz/jei/api/IAsyncModPlugin.java | 3 +- .../java/mezz/jei/api/IRuntimePlugin.java | 4 ++ .../java/mezz/jei/api/JeiAsyncPlugin.java | 9 ----- .../java/mezz/jei/api/JeiRuntimePlugin.java | 9 ----- .../jei/gui/ingredients/IngredientFilter.java | 1 - .../mezz/jei/library/load/PluginCaller.java | 11 ++++-- .../plugins/jei/JeiInternalPlugin.java | 1 - .../plugins/vanilla/VanillaPlugin.java | 1 - .../mezz/jei/library/startup/JeiStarter.java | 12 +++--- .../mezz/jei/library/startup/StartData.java | 38 ++++++++++++++++++- 10 files changed, 54 insertions(+), 35 deletions(-) delete mode 100644 CommonApi/src/main/java/mezz/jei/api/JeiAsyncPlugin.java delete mode 100644 CommonApi/src/main/java/mezz/jei/api/JeiRuntimePlugin.java diff --git a/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java b/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java index d0954c3be..aea81165b 100644 --- a/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java +++ b/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java @@ -20,9 +20,8 @@ /** * The main async class to implement to create a JEI plugin. * Everything communicated between a mod and JEI is through this class. - * IAsyncModPlugin must have the {@link JeiPlugin} annotation to get loaded by JEI. * - * In a Forge environment, IModPlugins must have the {@link JeiAsyncPlugin} annotation to get loaded by JEI. + * In a Forge environment, IModPlugins must have the {@link JeiPlugin} annotation to get loaded by JEI. * * In a Fabric environment, IModPlugins must be declared under `entrypoints.jei_async_mod_plugin` in `fabric.mod.json`. * See the Fabric Wiki for more information. diff --git a/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java b/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java index c6dcfaeab..411003517 100644 --- a/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java +++ b/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java @@ -8,6 +8,10 @@ import java.util.concurrent.CompletableFuture; /** + * A runtime plugin is used to override the default JEI runtime. + * Only one runtime plugin will be used, so if you create one then JEI's will be deactivated. + * This is intended for mods that implement a GUI that completely replaces JEI's. + * * In a Forge environment, IRuntimePlugins must have the {@link JeiPlugin} annotation to get loaded by JEI. * * In a Fabric environment, IModPlugins must be declared under `entrypoints.jei_runtime_plugin` in `fabric.mod.json`. diff --git a/CommonApi/src/main/java/mezz/jei/api/JeiAsyncPlugin.java b/CommonApi/src/main/java/mezz/jei/api/JeiAsyncPlugin.java deleted file mode 100644 index d42560224..000000000 --- a/CommonApi/src/main/java/mezz/jei/api/JeiAsyncPlugin.java +++ /dev/null @@ -1,9 +0,0 @@ -package mezz.jei.api; - -/** - * This annotation lets JEI detect mod plugins. - * All {@link IAsyncModPlugin} must have this annotation and a constructor with no arguments. - * @since 13.2.0 - */ -public @interface JeiAsyncPlugin { -} diff --git a/CommonApi/src/main/java/mezz/jei/api/JeiRuntimePlugin.java b/CommonApi/src/main/java/mezz/jei/api/JeiRuntimePlugin.java deleted file mode 100644 index 562896eee..000000000 --- a/CommonApi/src/main/java/mezz/jei/api/JeiRuntimePlugin.java +++ /dev/null @@ -1,9 +0,0 @@ -package mezz.jei.api; - -/** - * This annotation lets JEI detect mod plugins. - * All {@link IRuntimePlugin} must have this annotation and a constructor with no arguments. - * @since 13.2.0 - */ -public @interface JeiRuntimePlugin { -} diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java index a221d2f62..b2c1fc527 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java @@ -10,7 +10,6 @@ import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.api.runtime.IIngredientVisibility; import mezz.jei.api.runtime.IJeiClientExecutor; -import mezz.jei.common.async.JeiStartTask; import mezz.jei.common.config.DebugConfig; import mezz.jei.common.util.Translator; import mezz.jei.common.config.IClientConfig; diff --git a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java index 07cb8b970..e129d3dad 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java @@ -12,6 +12,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -24,20 +25,20 @@ public class PluginCaller { private static final Logger LOGGER = LogManager.getLogger(); private final List plugins; private final List asyncPlugins; - private final List runtimePlugins; + private final IRuntimePlugin runtimePlugin; private final JeiClientExecutor clientExecutor; private final IClientConfig clientConfig; public PluginCaller( List plugins, List asyncPlugins, - List runtimePlugins, + IRuntimePlugin runtimePlugin, JeiClientExecutor clientExecutor, IClientConfig clientConfig ) { this.plugins = plugins; this.asyncPlugins = asyncPlugins; - this.runtimePlugins = runtimePlugins; + this.runtimePlugin = runtimePlugin; this.clientExecutor = clientExecutor; this.clientConfig = clientConfig; } @@ -154,13 +155,15 @@ public void callOnPlugins( LOGGER.info("{} took {}", title, stopwatch); } - public void callOnRuntimePlugins( + public void callOnRuntimePlugin( String title, Function> asyncFun ) { LOGGER.info("{}...", title); Stopwatch stopwatch = Stopwatch.createStarted(); + List runtimePlugins = new ArrayList<>(); + runtimePlugins.add(runtimePlugin); try (PluginCallerTimer timer = new PluginCallerTimer()) { callAsync( title, diff --git a/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java b/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java index 08f3d1fd9..772729015 100644 --- a/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java +++ b/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java @@ -1,7 +1,6 @@ package mezz.jei.library.plugins.jei; import mezz.jei.api.IAsyncModPlugin; -import mezz.jei.api.JeiAsyncPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.helpers.IGuiHelper; diff --git a/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java b/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java index f3e9e56af..45cc443e9 100644 --- a/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java +++ b/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java @@ -1,7 +1,6 @@ package mezz.jei.library.plugins.vanilla; import mezz.jei.api.IAsyncModPlugin; -import mezz.jei.api.JeiAsyncPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.constants.RecipeTypes; diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index 30a9661a9..ca7f75bf9 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -103,7 +103,7 @@ public JeiStarter(StartData data) { this.pluginCaller = new PluginCaller( data.plugins(), data.asyncPlugins(), - data.runtimePlugins(), + data.runtimePlugin(), clientExecutor, jeiClientConfigs.getClientConfig() ); @@ -198,7 +198,7 @@ private void doActualStart() { p -> p.registerRuntime(runtimeRegistration), p -> CompletableFuture.completedFuture(null) ); - pluginCaller.callOnRuntimePlugins( + pluginCaller.callOnRuntimePlugin( "Registering Runtime", p -> p.registerRuntime(runtimeRegistration, clientExecutor) ); @@ -225,8 +225,8 @@ private void doActualStart() { p -> p.onRuntimeAvailable(jeiRuntime), p -> p.onRuntimeAvailable(jeiRuntime, clientExecutor) ); - pluginCaller.callOnRuntimePlugins( - "Sending Runtime", + pluginCaller.callOnRuntimePlugin( + "Sending Runtime to Runtime Plugin", p -> p.onRuntimeAvailable(jeiRuntime, clientExecutor) ); @@ -245,8 +245,8 @@ public void stop() { IModPlugin::onRuntimeUnavailable, p -> p.onRuntimeUnavailable(clientExecutor) ); - pluginCaller.callOnRuntimePlugins( - "Sending Runtime Unavailable", + pluginCaller.callOnRuntimePlugin( + "Sending Runtime Unavailable to Runtime Plugin", p -> p.onRuntimeUnavailable(clientExecutor) ); } diff --git a/Library/src/main/java/mezz/jei/library/startup/StartData.java b/Library/src/main/java/mezz/jei/library/startup/StartData.java index 3a5dc4346..6759d93ab 100644 --- a/Library/src/main/java/mezz/jei/library/startup/StartData.java +++ b/Library/src/main/java/mezz/jei/library/startup/StartData.java @@ -3,27 +3,61 @@ import mezz.jei.api.IAsyncModPlugin; import mezz.jei.api.IModPlugin; import mezz.jei.api.IRuntimePlugin; +import mezz.jei.api.constants.ModIds; import mezz.jei.common.input.IInternalKeyMappings; import mezz.jei.common.network.IConnectionToServer; +import net.minecraft.resources.ResourceLocation; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; public record StartData( List plugins, List asyncPlugins, - List runtimePlugins, + IRuntimePlugin runtimePlugin, IConnectionToServer serverConnection, IInternalKeyMappings keyBindings ) { + private static final Logger LOGGER = LogManager.getLogger(); + public static StartData create( IPluginFinder pluginFinder, IConnectionToServer connectionToServer, IInternalKeyMappings keyBindings ) { + List runtimePlugins = pluginFinder.getPlugins(IRuntimePlugin.class); + if (runtimePlugins.size() > 1) { + // Only one runtime plugin should be active at a time. + // If a mod has registered one, it gets priority over JEI's. + runtimePlugins = runtimePlugins.stream() + .filter(r -> !r.getPluginUid().getNamespace().equals(ModIds.JEI_ID)) + .sorted() + .toList(); + } + + IRuntimePlugin runtimePlugin = runtimePlugins.get(0); + if (runtimePlugins.size() > 1) { + LOGGER.warn( + """ + Multiple runtime plugins have been registered but only one can be used. + Chosen runtime plugin: {} + Ignored runtime plugins: [{}]""", + runtimePlugin.getPluginUid(), + runtimePlugins.stream() + .filter(r -> !Objects.equals(r, runtimePlugin)) + .map(r -> r.getPluginUid()) + .map(ResourceLocation::toString) + .collect(Collectors.joining(", ")) + ); + } + return new StartData( pluginFinder.getPlugins(IModPlugin.class), pluginFinder.getPlugins(IAsyncModPlugin.class), - pluginFinder.getPlugins(IRuntimePlugin.class), + runtimePlugin, connectionToServer, keyBindings ); From d598989b8f779287b934dc2131f0fd84bf2a347c Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 21 May 2023 11:23:56 -0700 Subject: [PATCH 15/22] simplify JeiStartTask#interruptIfCanceled --- .../mezz/jei/common/async/JeiStartTask.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java b/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java index 912a1ce25..2d85a49ad 100644 --- a/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java +++ b/Common/src/main/java/mezz/jei/common/async/JeiStartTask.java @@ -12,22 +12,12 @@ public void cancelStart() { isCancelled = true; } - /** - * Check whether the startup should be interrupted. - * If this is not running on a JEI startup thread, false is returned. - */ - private static boolean isCanceled() { + public static void interruptIfCanceled() { Thread t = Thread.currentThread(); if (t instanceof JeiStartTask startTask) { - return startTask.isCancelled; - } else { - return false; - } - } - - public static void interruptIfCanceled() { - if (isCanceled()) { - throw new JeiAsyncStartInterrupt(); + if (startTask.isCancelled) { + throw new JeiAsyncStartInterrupt(); + } } } From 43d81e7d7e425128b7a61fbb19899258ce32ede6 Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 21 May 2023 14:33:25 -0700 Subject: [PATCH 16/22] move configs to their own subcategory --- .../main/java/mezz/jei/common/config/ClientConfig.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java index 3dcd68c90..53acea9fc 100644 --- a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java @@ -66,13 +66,15 @@ public ClientConfig(IConfigSchemaBuilder schema) { Integer.MAX_VALUE, "Max. recipe gui height" ); - asyncLoadingEnabled = advanced.addBoolean( + + IConfigCategoryBuilder loading = schema.addCategory("loading"); + asyncLoadingEnabled = loading.addBoolean( "asyncLoadingEnabled", false, - "Whether JEI should load asynchronously, so that it starts some time after world join." + "Whether JEI should load asynchronously, so that it starts finishes loading after world join." ); - parallelPluginLoadingEnabled = advanced.addBoolean( - "parallelPluginLoadingEnabled", + parallelPluginLoadingEnabled = loading.addBoolean( + "experimentalParallelPluginLoadingEnabled", false, "Whether JEI should load plugins in parallel. This may cause plugins to crash." ); From 2ecf7e615abfac3ec258ad3087c99ca40d1f0486 Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 21 May 2023 14:38:21 -0700 Subject: [PATCH 17/22] fix crash when plugin uid crashes --- .../mezz/jei/library/load/PluginCaller.java | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java index e129d3dad..795108c8a 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java @@ -56,21 +56,27 @@ private void callAsync( .map(plugin -> CompletableFuture.>supplyAsync(() -> { JeiStartTask.interruptIfCanceled(); - ResourceLocation pluginUid = uidFunc.apply(plugin); - var t = timer.begin(title, pluginUid); try { - return func.apply(plugin) - .handle((v, e) -> { - if (e != null) { - LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); - } - t.close(); - return null; - }); - } catch (RuntimeException | LinkageError e) { - LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); + ResourceLocation pluginUid = uidFunc.apply(plugin); + var t = timer.begin(title, pluginUid); + try { + return func.apply(plugin) + .handle((v, e) -> { + if (e != null) { + LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); + } + t.close(); + return null; + }); + } catch (RuntimeException | LinkageError e) { + LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); + erroredPlugins.add(plugin); + t.close(); + return CompletableFuture.completedFuture(null); + } + } catch (RuntimeException e) { + LOGGER.error("Caught an error from mod plugin: {}", plugin.getClass(), e); erroredPlugins.add(plugin); - t.close(); return CompletableFuture.completedFuture(null); } }) @@ -103,11 +109,16 @@ private void callSync( ) { Set erroredPlugins = ConcurrentHashMap.newKeySet(); for (T plugin : plugins) { - ResourceLocation pluginUid = uidFunc.apply(plugin); - try (var ignored = timer.begin(title, pluginUid)) { - func.accept(plugin); - } catch (RuntimeException | LinkageError e) { - LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); + try { + ResourceLocation pluginUid = uidFunc.apply(plugin); + try (var ignored = timer.begin(title, pluginUid)) { + func.accept(plugin); + } catch (RuntimeException | LinkageError e) { + LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); + erroredPlugins.add(plugin); + } + } catch (RuntimeException e) { + LOGGER.error("Caught an error from mod plugin: {}", plugin.getClass(), e); erroredPlugins.add(plugin); } } From 442074dc3db22d8ecbe91e5554d2770b35fb7917 Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 21 May 2023 15:37:43 -0700 Subject: [PATCH 18/22] remove parallel loading --- .../mezz/jei/common/config/ClientConfig.java | 11 -- .../mezz/jei/common/config/IClientConfig.java | 2 - .../java/mezz/jei/api/IAsyncModPlugin.java | 167 ------------------ .../java/mezz/jei/api/IRuntimePlugin.java | 8 +- .../jei/api/runtime/IJeiClientExecutor.java | 11 -- .../plugins/fabric/FabricRuntimePlugin.java | 8 +- .../fabric/startup/FabricPluginFinder.java | 2 - .../plugins/forge/ForgeRuntimePlugin.java | 8 +- .../mezz/jei/test/IngredientFilterTest.java | 10 +- .../mezz/jei/test/lib/TestClientConfig.java | 5 - .../jei/gui/ingredients/IngredientFilter.java | 28 +-- .../mezz/jei/gui/startup/JeiGuiStarter.java | 6 +- .../mezz/jei/library/load/PluginCaller.java | 129 +++----------- .../mezz/jei/library/load/PluginHelper.java | 10 +- .../mezz/jei/library/load/PluginLoader.java | 36 ++-- .../plugins/jei/JeiInternalPlugin.java | 10 +- .../plugins/vanilla/VanillaPlugin.java | 30 ++-- .../jei/library/recipes/RecipeManager.java | 16 +- .../recipes/RecipeManagerInternal.java | 2 - .../library/startup/ClientTaskExecutor.java | 3 +- .../library/startup/JeiClientExecutor.java | 38 ---- .../mezz/jei/library/startup/JeiStarter.java | 29 ++- .../mezz/jei/library/startup/StartData.java | 3 - 23 files changed, 109 insertions(+), 463 deletions(-) delete mode 100644 CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java delete mode 100644 CommonApi/src/main/java/mezz/jei/api/runtime/IJeiClientExecutor.java delete mode 100644 Library/src/main/java/mezz/jei/library/startup/JeiClientExecutor.java diff --git a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java index 53acea9fc..2749123e7 100644 --- a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java @@ -18,7 +18,6 @@ public final class ClientConfig implements IClientConfig { private final Supplier lowMemorySlowSearchEnabled; private final Supplier cheatToHotbarUsingHotkeysEnabled; private final Supplier asyncLoadingEnabled; - private final Supplier parallelPluginLoadingEnabled; private final Supplier addBookmarksToFront; private final Supplier lookupFluidContents; private final Supplier giveMode; @@ -73,11 +72,6 @@ public ClientConfig(IConfigSchemaBuilder schema) { false, "Whether JEI should load asynchronously, so that it starts finishes loading after world join." ); - parallelPluginLoadingEnabled = loading.addBoolean( - "experimentalParallelPluginLoadingEnabled", - false, - "Whether JEI should load plugins in parallel. This may cause plugins to crash." - ); IConfigCategoryBuilder sorting = schema.addCategory("sorting"); ingredientSorterStages = sorting.addList( @@ -117,11 +111,6 @@ public boolean getAsyncLoadingEnabled() { return asyncLoadingEnabled.get(); } - @Override - public boolean getParallelPluginLoadingEnabled() { - return parallelPluginLoadingEnabled.get(); - } - @Override public boolean isAddingBookmarksToFront() { return addBookmarksToFront.get(); diff --git a/Common/src/main/java/mezz/jei/common/config/IClientConfig.java b/Common/src/main/java/mezz/jei/common/config/IClientConfig.java index 4dea26a92..b6cc8f550 100644 --- a/Common/src/main/java/mezz/jei/common/config/IClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/IClientConfig.java @@ -15,8 +15,6 @@ public interface IClientConfig { boolean getAsyncLoadingEnabled(); - boolean getParallelPluginLoadingEnabled(); - boolean isAddingBookmarksToFront(); boolean isLookupFluidContents(); diff --git a/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java b/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java deleted file mode 100644 index aea81165b..000000000 --- a/CommonApi/src/main/java/mezz/jei/api/IAsyncModPlugin.java +++ /dev/null @@ -1,167 +0,0 @@ -package mezz.jei.api; - -import mezz.jei.api.helpers.IPlatformFluidHelper; -import mezz.jei.api.registration.IAdvancedRegistration; -import mezz.jei.api.registration.IGuiHandlerRegistration; -import mezz.jei.api.registration.IModIngredientRegistration; -import mezz.jei.api.registration.IRecipeCatalystRegistration; -import mezz.jei.api.registration.IRecipeCategoryRegistration; -import mezz.jei.api.registration.IRecipeRegistration; -import mezz.jei.api.registration.IRecipeTransferRegistration; -import mezz.jei.api.registration.ISubtypeRegistration; -import mezz.jei.api.registration.IVanillaCategoryExtensionRegistration; -import mezz.jei.api.runtime.IJeiClientExecutor; -import mezz.jei.api.runtime.IJeiRuntime; -import mezz.jei.api.runtime.config.IJeiConfigManager; -import net.minecraft.resources.ResourceLocation; - -import java.util.concurrent.CompletableFuture; - -/** - * The main async class to implement to create a JEI plugin. - * Everything communicated between a mod and JEI is through this class. - * - * In a Forge environment, IModPlugins must have the {@link JeiPlugin} annotation to get loaded by JEI. - * - * In a Fabric environment, IModPlugins must be declared under `entrypoints.jei_async_mod_plugin` in `fabric.mod.json`. - * See the Fabric Wiki for more information. - * - * @see IModPlugin for a simpler, synchronous version - * - * @since 13.2.0 - */ -public interface IAsyncModPlugin { - - /** - * The unique ID for this mod plugin. - * The namespace should be your mod's modId. - * - * @since 13.2.0 - */ - ResourceLocation getPluginUid(); - - /** - * If your item has subtypes that depend on NBT or capabilities, use this to help JEI identify those subtypes correctly. - * - * @since 13.2.0 - */ - default CompletableFuture registerItemSubtypes(ISubtypeRegistration registration, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * If your fluid has subtypes that depend on NBT or capabilities, - * use this to help JEI identify those subtypes correctly. - * - * @since 13.2.0 - */ - default CompletableFuture registerFluidSubtypes(ISubtypeRegistration registration, IPlatformFluidHelper platformFluidHelper, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * Register special ingredients, beyond the basic ItemStack and FluidStack. - * - * @since 13.2.0 - */ - default CompletableFuture registerIngredients(IModIngredientRegistration registration, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * Register the categories handled by this plugin. - * These are registered before recipes so they can be checked for validity. - * - * @since 13.2.0 - */ - default CompletableFuture registerCategories(IRecipeCategoryRegistration registration, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * Register modded extensions to the vanilla crafting recipe category. - * Custom crafting recipes for your mod should use this to tell JEI how they work. - * - * @since 13.2.0 - */ - default CompletableFuture registerVanillaCategoryExtensions(IVanillaCategoryExtensionRegistration registration, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * Register modded recipes. - * - * @since 13.2.0 - */ - default CompletableFuture registerRecipes(IRecipeRegistration registration, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * Register recipe transfer handlers (move ingredients from the inventory into crafting GUIs). - * - * @since 13.2.0 - */ - default CompletableFuture registerRecipeTransferHandlers(IRecipeTransferRegistration registration, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * Register recipe catalysts. - * Recipe Catalysts are ingredients that are needed in order to craft other things. - * Vanilla examples of Recipe Catalysts are the Crafting Table and Furnace. - * - * @since 13.2.0 - */ - default CompletableFuture registerRecipeCatalysts(IRecipeCatalystRegistration registration, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * Register various GUI-related things for your mod. - * This includes adding clickable areas in your guis to open JEI, - * and adding areas on the screen that JEI should avoid drawing. - * - * @since 13.2.0 - */ - default CompletableFuture registerGuiHandlers(IGuiHandlerRegistration registration, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * Register advanced features for your mod plugin. - * - * @since 13.2.0 - */ - default CompletableFuture registerAdvanced(IAdvancedRegistration registration, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * Called when JEI's runtime features are available, after all mods have registered. - * - * @since 13.2.0 - */ - default CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * Called when JEI's runtime features are no longer available, after a user quits or logs out of a world. - * - * @since 13.2.0 - */ - default CompletableFuture onRuntimeUnavailable(IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } - - /** - * Called when JEI's configs are available. - * This is called early on, as soon as configs are available. - * - * @since 13.2.0 - */ - default CompletableFuture onConfigManagerAvailable(IJeiConfigManager configManager, IJeiClientExecutor clientThreadExecutor) { - return CompletableFuture.completedFuture(null); - } -} diff --git a/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java b/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java index 411003517..2db299a41 100644 --- a/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java +++ b/CommonApi/src/main/java/mezz/jei/api/IRuntimePlugin.java @@ -1,11 +1,11 @@ package mezz.jei.api; import mezz.jei.api.registration.IRuntimeRegistration; -import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.api.runtime.IJeiRuntime; import net.minecraft.resources.ResourceLocation; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; /** * A runtime plugin is used to override the default JEI runtime. @@ -34,7 +34,7 @@ public interface IRuntimePlugin { * * @since 13.2.0 */ - default CompletableFuture registerRuntime(IRuntimeRegistration registration, IJeiClientExecutor clientExecutor) { + default CompletableFuture registerRuntime(IRuntimeRegistration registration, Executor clientExecutor) { return CompletableFuture.completedFuture(null); } @@ -43,7 +43,7 @@ default CompletableFuture registerRuntime(IRuntimeRegistration registratio * * @since 13.2.0 */ - default CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, IJeiClientExecutor clientExecutor) { + default CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, Executor clientExecutor) { return CompletableFuture.completedFuture(null); } @@ -52,7 +52,7 @@ default CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, IJeiC * * @since 13.2.0 */ - default CompletableFuture onRuntimeUnavailable(IJeiClientExecutor clientExecutor) { + default CompletableFuture onRuntimeUnavailable(Executor clientExecutor) { return CompletableFuture.completedFuture(null); } } diff --git a/CommonApi/src/main/java/mezz/jei/api/runtime/IJeiClientExecutor.java b/CommonApi/src/main/java/mezz/jei/api/runtime/IJeiClientExecutor.java deleted file mode 100644 index bb3ab9aec..000000000 --- a/CommonApi/src/main/java/mezz/jei/api/runtime/IJeiClientExecutor.java +++ /dev/null @@ -1,11 +0,0 @@ -package mezz.jei.api.runtime; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.Supplier; - -public interface IJeiClientExecutor { - CompletableFuture runOnClientThread(Runnable runnable); - CompletableFuture runOnClientThread(Supplier supplier); - Executor getExecutor(); -} diff --git a/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricRuntimePlugin.java b/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricRuntimePlugin.java index 3b1c44bfa..f472cfc20 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricRuntimePlugin.java +++ b/Fabric/src/main/java/mezz/jei/fabric/plugins/fabric/FabricRuntimePlugin.java @@ -4,7 +4,6 @@ import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.registration.IRuntimeRegistration; -import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.api.runtime.IJeiRuntime; import mezz.jei.fabric.startup.EventRegistration; import mezz.jei.gui.startup.JeiGuiStarter; @@ -15,6 +14,7 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; @JeiPlugin public class FabricRuntimePlugin implements IRuntimePlugin { @@ -29,19 +29,19 @@ public ResourceLocation getPluginUid() { } @Override - public CompletableFuture registerRuntime(IRuntimeRegistration registration, IJeiClientExecutor clientExecutor) { + public CompletableFuture registerRuntime(IRuntimeRegistration registration, Executor clientExecutor) { return JeiGuiStarter.start(registration, clientExecutor) .thenAccept(eventRegistration::setEventHandlers); } @Override - public CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, IJeiClientExecutor clientExecutor) { + public CompletableFuture onRuntimeAvailable(IJeiRuntime jeiRuntime, Executor clientExecutor) { runtime = jeiRuntime; return CompletableFuture.completedFuture(null); } @Override - public CompletableFuture onRuntimeUnavailable(IJeiClientExecutor clientExecutor) { + public CompletableFuture onRuntimeUnavailable(Executor clientExecutor) { runtime = null; LOGGER.info("Stopping JEI GUI"); eventRegistration.clear(); diff --git a/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java b/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java index cd9ec2cfe..4c0ca4c6f 100644 --- a/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java +++ b/Fabric/src/main/java/mezz/jei/fabric/startup/FabricPluginFinder.java @@ -1,6 +1,5 @@ package mezz.jei.fabric.startup; -import mezz.jei.api.IAsyncModPlugin; import mezz.jei.api.IModPlugin; import mezz.jei.api.IRuntimePlugin; import mezz.jei.library.startup.IPluginFinder; @@ -14,7 +13,6 @@ public final class FabricPluginFinder implements IPluginFinder { private static final Map, String> entryPointKeys = Map.of( IModPlugin.class, "jei_mod_plugin", - IAsyncModPlugin.class, "jei_async_mod_plugin", IRuntimePlugin.class, "jei_runtime_plugin" ); diff --git a/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeRuntimePlugin.java b/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeRuntimePlugin.java index b2cc46d7c..5593267d3 100644 --- a/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeRuntimePlugin.java +++ b/Forge/src/main/java/mezz/jei/forge/plugins/forge/ForgeRuntimePlugin.java @@ -4,7 +4,6 @@ import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.registration.IRuntimeRegistration; -import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.forge.events.RuntimeEventSubscriptions; import mezz.jei.forge.startup.EventRegistration; import mezz.jei.gui.startup.JeiGuiStarter; @@ -14,6 +13,7 @@ import org.apache.logging.log4j.Logger; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; @JeiPlugin public class ForgeRuntimePlugin implements IRuntimePlugin { @@ -27,7 +27,7 @@ public ResourceLocation getPluginUid() { } @Override - public CompletableFuture registerRuntime(IRuntimeRegistration registration, IJeiClientExecutor clientExecutor) { + public CompletableFuture registerRuntime(IRuntimeRegistration registration, Executor clientExecutor) { if (!runtimeSubscriptions.isEmpty()) { LOGGER.error("JEI GUI is already running."); runtimeSubscriptions.clear(); @@ -36,11 +36,11 @@ public CompletableFuture registerRuntime(IRuntimeRegistration registration return JeiGuiStarter.start(registration, clientExecutor) .thenAcceptAsync(eventHandlers -> { EventRegistration.registerEvents(runtimeSubscriptions, eventHandlers); - }, clientExecutor.getExecutor()); + }, clientExecutor); } @Override - public CompletableFuture onRuntimeUnavailable(IJeiClientExecutor clientExecuto) { + public CompletableFuture onRuntimeUnavailable(Executor clientExecutor) { LOGGER.info("Stopping JEI GUI"); runtimeSubscriptions.clear(); return CompletableFuture.completedFuture(null); diff --git a/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java b/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java index 8879778bc..51aedfb0c 100644 --- a/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java +++ b/Forge/src/test/java/mezz/jei/test/IngredientFilterTest.java @@ -8,9 +8,9 @@ import mezz.jei.api.runtime.IEditModeConfig; import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.api.runtime.IIngredientVisibility; -import mezz.jei.common.util.Translator; -import mezz.jei.common.config.IClientToggleState; import mezz.jei.common.config.IClientConfig; +import mezz.jei.common.config.IClientToggleState; +import mezz.jei.common.util.Translator; import mezz.jei.gui.filter.FilterTextSource; import mezz.jei.gui.filter.IFilterTextSource; import mezz.jei.gui.ingredients.IIngredientSorter; @@ -24,15 +24,14 @@ import mezz.jei.library.ingredients.subtypes.SubtypeInterpreters; import mezz.jei.library.ingredients.subtypes.SubtypeManager; import mezz.jei.library.load.registration.IngredientManagerBuilder; -import mezz.jei.library.startup.JeiClientExecutor; import mezz.jei.test.lib.TestClientConfig; +import mezz.jei.test.lib.TestClientToggleState; import mezz.jei.test.lib.TestColorHelper; import mezz.jei.test.lib.TestIngredient; import mezz.jei.test.lib.TestIngredientFilterConfig; import mezz.jei.test.lib.TestIngredientHelper; import mezz.jei.test.lib.TestModIdHelper; import mezz.jei.test.lib.TestPlugin; -import mezz.jei.test.lib.TestClientToggleState; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; import net.minecraft.util.StringUtil; @@ -47,6 +46,7 @@ import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; public class IngredientFilterTest { private static final int EXTRA_INGREDIENT_COUNT = 5; @@ -66,7 +66,7 @@ public class IngredientFilterTest { @BeforeEach public void setup() throws ExecutionException, InterruptedException { TestPlugin testPlugin = new TestPlugin(); - JeiClientExecutor clientExecutor = new JeiClientExecutor(MoreExecutors.directExecutor()); + Executor clientExecutor = MoreExecutors.directExecutor(); SubtypeInterpreters subtypeInterpreters = new SubtypeInterpreters(); SubtypeManager subtypeManager = new SubtypeManager(subtypeInterpreters); diff --git a/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java b/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java index d886356b8..e03b84b9b 100644 --- a/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java +++ b/Forge/src/test/java/mezz/jei/test/lib/TestClientConfig.java @@ -33,11 +33,6 @@ public boolean getAsyncLoadingEnabled() { return false; } - @Override - public boolean getParallelPluginLoadingEnabled() { - return false; - } - @Override public boolean isAddingBookmarksToFront() { return false; diff --git a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java index b2c1fc527..402e02be2 100644 --- a/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java +++ b/Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java @@ -9,11 +9,10 @@ import mezz.jei.api.ingredients.subtypes.UidContext; import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.api.runtime.IIngredientVisibility; -import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.common.config.DebugConfig; -import mezz.jei.common.util.Translator; import mezz.jei.common.config.IClientConfig; import mezz.jei.common.config.IIngredientFilterConfig; +import mezz.jei.common.util.Translator; import mezz.jei.gui.filter.IFilterTextSource; import mezz.jei.gui.overlay.IIngredientGridSource; import mezz.jei.gui.search.ElementPrefixParser; @@ -37,6 +36,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.regex.Matcher; @@ -94,7 +94,7 @@ public IngredientFilter( public CompletableFuture addIngredientsAsync( NonNullList> ingredients, - IJeiClientExecutor clientExecutor + Executor clientExecutor ) { int ingredientCount = ingredients.size(); LOGGER.info("Adding {} ingredients", ingredientCount); @@ -103,19 +103,21 @@ public CompletableFuture addIngredientsAsync( .flatMap(Optional::stream) .collect(Collectors.toList()); - int batchSize = 100; + int batchSize = 1000; AtomicInteger addedTotal = new AtomicInteger(0); Stream> futures = Lists.partition(elementInfos, batchSize) .stream() - .map(batch -> clientExecutor.runOnClientThread(() -> { - for (IListElementInfo elementInfo : batch) { - this.addIngredient(elementInfo); - } - int added = addedTotal.addAndGet(batch.size()); - if (added % (10 * batchSize) == 0 || added == ingredientCount) { - LOGGER.info("Added {}/{} ingredients", added, ingredientCount); - } - })); + .map(batch -> + CompletableFuture.runAsync(() -> { + for (IListElementInfo elementInfo : batch) { + this.addIngredient(elementInfo); + } + int added = addedTotal.addAndGet(batch.size()); + if (added % (10 * batchSize) == 0 || added == ingredientCount) { + LOGGER.info("Added {}/{} ingredients", added, ingredientCount); + } + }, clientExecutor) + ); return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)); } diff --git a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java index e1e7949ca..594c9e5c9 100644 --- a/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java +++ b/Gui/src/main/java/mezz/jei/gui/startup/JeiGuiStarter.java @@ -11,7 +11,6 @@ import mezz.jei.api.runtime.IIngredientFilter; import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.api.runtime.IIngredientVisibility; -import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.api.runtime.IScreenHelper; import mezz.jei.common.Internal; import mezz.jei.common.config.IClientConfig; @@ -57,11 +56,12 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; public class JeiGuiStarter { private static final Logger LOGGER = LogManager.getLogger(); - public static CompletableFuture start(IRuntimeRegistration registration, IJeiClientExecutor clientExecutor) { + public static CompletableFuture start(IRuntimeRegistration registration, Executor clientExecutor) { LOGGER.info("Starting JEI GUI"); LoggedTimer timer = new LoggedTimer(); @@ -225,6 +225,6 @@ public static CompletableFuture start(IRuntimeRegistration reg guiEventHandler, clientInputHandler ); - }); + }, clientExecutor); } } diff --git a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java index 795108c8a..6d2c525c4 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java @@ -1,103 +1,36 @@ package mezz.jei.library.load; import com.google.common.base.Stopwatch; -import mezz.jei.api.IAsyncModPlugin; import mezz.jei.api.IModPlugin; import mezz.jei.api.IRuntimePlugin; import mezz.jei.common.async.JeiStartTask; -import mezz.jei.common.config.IClientConfig; -import mezz.jei.library.startup.JeiClientExecutor; +import mezz.jei.library.startup.ClientTaskExecutor; import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Function; -import java.util.stream.Stream; public class PluginCaller { private static final Logger LOGGER = LogManager.getLogger(); private final List plugins; - private final List asyncPlugins; private final IRuntimePlugin runtimePlugin; - private final JeiClientExecutor clientExecutor; - private final IClientConfig clientConfig; + private final ClientTaskExecutor clientExecutor; public PluginCaller( List plugins, - List asyncPlugins, IRuntimePlugin runtimePlugin, - JeiClientExecutor clientExecutor, - IClientConfig clientConfig + ClientTaskExecutor clientExecutor ) { this.plugins = plugins; - this.asyncPlugins = asyncPlugins; this.runtimePlugin = runtimePlugin; this.clientExecutor = clientExecutor; - this.clientConfig = clientConfig; - } - - private void callAsync( - String title, - PluginCallerTimer timer, - List plugins, - Function uidFunc, - Function> func - ) { - Set erroredPlugins = ConcurrentHashMap.newKeySet(); - - Stream> futures = plugins.stream() - .map(plugin -> - CompletableFuture.>supplyAsync(() -> { - JeiStartTask.interruptIfCanceled(); - try { - ResourceLocation pluginUid = uidFunc.apply(plugin); - var t = timer.begin(title, pluginUid); - try { - return func.apply(plugin) - .handle((v, e) -> { - if (e != null) { - LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); - } - t.close(); - return null; - }); - } catch (RuntimeException | LinkageError e) { - LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); - erroredPlugins.add(plugin); - t.close(); - return CompletableFuture.completedFuture(null); - } - } catch (RuntimeException e) { - LOGGER.error("Caught an error from mod plugin: {}", plugin.getClass(), e); - erroredPlugins.add(plugin); - return CompletableFuture.completedFuture(null); - } - }) - .thenCompose(f -> f) - ); - - CompletableFuture allFutures = CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)); - - Minecraft minecraft = Minecraft.getInstance(); - if (minecraft.isSameThread()) { - minecraft.managedBlock(() -> { - if (allFutures.isDone()) { - return true; - } - clientExecutor.tick(); - return false; - }); - } - allFutures.join(); - - plugins.removeAll(erroredPlugins); } private void callSync( @@ -127,40 +60,22 @@ private void callSync( public void callOnPlugins( String title, - Consumer func, - Function> asyncFun + Consumer func ) { + JeiStartTask.interruptIfCanceled(); LOGGER.info("{}...", title); Stopwatch stopwatch = Stopwatch.createStarted(); try (PluginCallerTimer timer = new PluginCallerTimer()) { - callAsync( - title, - timer, - asyncPlugins, - IAsyncModPlugin::getPluginUid, - asyncFun - ); - - if (clientConfig.getParallelPluginLoadingEnabled()) { - callAsync( + CompletableFuture.runAsync(() -> { + callSync( title, timer, plugins, IModPlugin::getPluginUid, - p -> CompletableFuture.runAsync(() -> func.accept(p)) + func ); - } else { - clientExecutor.runOnClientThread(() -> { - callSync( - title, - timer, - plugins, - IModPlugin::getPluginUid, - func - ); - }).join(); - } + }, clientExecutor).join(); } LOGGER.info("{} took {}", title, stopwatch); @@ -173,16 +88,24 @@ public void callOnRuntimePlugin( LOGGER.info("{}...", title); Stopwatch stopwatch = Stopwatch.createStarted(); - List runtimePlugins = new ArrayList<>(); - runtimePlugins.add(runtimePlugin); try (PluginCallerTimer timer = new PluginCallerTimer()) { - callAsync( - title, - timer, - runtimePlugins, - IRuntimePlugin::getPluginUid, - asyncFun - ); + ResourceLocation pluginUid = runtimePlugin.getPluginUid(); + try (var ignored = timer.begin(title, pluginUid)) { + CompletableFuture future = CompletableFuture.supplyAsync(() -> asyncFun.apply(runtimePlugin)) + .thenCompose(f -> f); + + Minecraft minecraft = Minecraft.getInstance(); + if (minecraft.isSameThread()) { + minecraft.managedBlock(() -> { + if (future.isDone()) { + return true; + } + clientExecutor.tick(); + return false; + }); + } + future.join(); + } } LOGGER.info("{} took {}", title, stopwatch); diff --git a/Library/src/main/java/mezz/jei/library/load/PluginHelper.java b/Library/src/main/java/mezz/jei/library/load/PluginHelper.java index 7b5f192a9..0f2e59e47 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginHelper.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginHelper.java @@ -1,6 +1,5 @@ package mezz.jei.library.load; -import mezz.jei.api.IAsyncModPlugin; import mezz.jei.library.plugins.jei.JeiInternalPlugin; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -11,7 +10,7 @@ import mezz.jei.library.plugins.vanilla.VanillaPlugin; public class PluginHelper { - public static void sortPlugins(List plugins, VanillaPlugin vanillaPlugin, @Nullable JeiInternalPlugin jeiInternalPlugin) { + public static void sortPlugins(List plugins, VanillaPlugin vanillaPlugin, @Nullable JeiInternalPlugin jeiInternalPlugin) { plugins.remove(vanillaPlugin); plugins.add(0, vanillaPlugin); @@ -21,11 +20,8 @@ public static void sortPlugins(List plugins, VanillaPlugin vani } } - public static Optional getPluginWithClass(Class pluginClass, List modPlugins, List asyncModPlugins) { - return Stream.concat( - modPlugins.stream(), - asyncModPlugins.stream() - ) + public static Optional getPluginWithClass(Class pluginClass, List modPlugins) { + return modPlugins.stream() .filter(pluginClass::isInstance) .map(pluginClass::cast) .findFirst(); diff --git a/Library/src/main/java/mezz/jei/library/load/PluginLoader.java b/Library/src/main/java/mezz/jei/library/load/PluginLoader.java index f3d06086e..8346f09e1 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginLoader.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginLoader.java @@ -44,7 +44,7 @@ import mezz.jei.library.recipes.RecipeManager; import mezz.jei.library.recipes.RecipeManagerInternal; import mezz.jei.library.runtime.JeiHelpers; -import mezz.jei.library.startup.JeiClientExecutor; +import mezz.jei.library.startup.ClientTaskExecutor; import mezz.jei.library.transfer.RecipeTransferHandlerHelper; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -55,7 +55,7 @@ public class PluginLoader { private final IConnectionToServer serverConnection; private final PluginCaller pluginCaller; - private final JeiClientExecutor clientExecutor; + private final ClientTaskExecutor clientExecutor; private final LoggedTimer timer; private final IIngredientManager ingredientManager; private final JeiHelpers jeiHelpers; @@ -65,7 +65,7 @@ public PluginLoader( PluginCaller pluginCaller, IModIdFormatConfig modIdFormatConfig, IColorHelper colorHelper, - JeiClientExecutor clientExecutor + ClientTaskExecutor clientExecutor ) { this.serverConnection = serverConnection; this.pluginCaller = pluginCaller; @@ -76,13 +76,11 @@ public PluginLoader( SubtypeRegistration subtypeRegistration = new SubtypeRegistration(); pluginCaller.callOnPlugins( "Registering item subtypes", - p -> p.registerItemSubtypes(subtypeRegistration), - p -> p.registerItemSubtypes(subtypeRegistration, clientExecutor) + p -> p.registerItemSubtypes(subtypeRegistration) ); pluginCaller.callOnPlugins( "Registering fluid subtypes", - p -> p.registerFluidSubtypes(subtypeRegistration, fluidHelper), - p -> p.registerFluidSubtypes(subtypeRegistration, fluidHelper, clientExecutor) + p -> p.registerFluidSubtypes(subtypeRegistration, fluidHelper) ); SubtypeInterpreters subtypeInterpreters = subtypeRegistration.getInterpreters(); SubtypeManager subtypeManager = new SubtypeManager(subtypeInterpreters); @@ -90,8 +88,7 @@ public PluginLoader( IngredientManagerBuilder ingredientManagerBuilder = new IngredientManagerBuilder(subtypeManager, colorHelper); pluginCaller.callOnPlugins( "Registering ingredients", - p -> p.registerIngredients(ingredientManagerBuilder), - p -> p.registerIngredients(ingredientManagerBuilder, clientExecutor) + p -> p.registerIngredients(ingredientManagerBuilder) ); this.ingredientManager = ingredientManagerBuilder.build(); @@ -107,16 +104,14 @@ private List> createRecipeCategories(VanillaPlugin vanillaPlu RecipeCategoryRegistration recipeCategoryRegistration = new RecipeCategoryRegistration(jeiHelpers); pluginCaller.callOnPlugins( "Registering categories", - p -> p.registerCategories(recipeCategoryRegistration), - p ->p.registerCategories(recipeCategoryRegistration, clientExecutor) + p -> p.registerCategories(recipeCategoryRegistration) ); CraftingRecipeCategory craftingCategory = vanillaPlugin.getCraftingCategory() .orElseThrow(() -> new NullPointerException("vanilla crafting category")); VanillaCategoryExtensionRegistration vanillaCategoryExtensionRegistration = new VanillaCategoryExtensionRegistration(craftingCategory, jeiHelpers); pluginCaller.callOnPlugins( "Registering vanilla category extensions", - p -> p.registerVanillaCategoryExtensions(vanillaCategoryExtensionRegistration), - p -> p.registerVanillaCategoryExtensions(vanillaCategoryExtensionRegistration, clientExecutor) + p -> p.registerVanillaCategoryExtensions(vanillaCategoryExtensionRegistration) ); return recipeCategoryRegistration.getRecipeCategories(); } @@ -125,8 +120,7 @@ public IScreenHelper createGuiScreenHelper(IJeiHelpers jeiHelpers) { GuiHandlerRegistration guiHandlerRegistration = new GuiHandlerRegistration(jeiHelpers); pluginCaller.callOnPlugins( "Registering gui handlers", - p -> p.registerGuiHandlers(guiHandlerRegistration), - p -> p.registerGuiHandlers(guiHandlerRegistration, clientExecutor) + p -> p.registerGuiHandlers(guiHandlerRegistration) ); return guiHandlerRegistration.createGuiScreenHelper(ingredientManager); } @@ -137,8 +131,7 @@ public IScreenHelper createGuiScreenHelper(IJeiHelpers jeiHelpers) { RecipeTransferRegistration recipeTransferRegistration = new RecipeTransferRegistration(stackHelper, handlerHelper, jeiHelpers, serverConnection); pluginCaller.callOnPlugins( "Registering recipes transfer handlers", - p -> p.registerRecipeTransferHandlers(recipeTransferRegistration), - p -> p.registerRecipeTransferHandlers(recipeTransferRegistration, clientExecutor) + p -> p.registerRecipeTransferHandlers(recipeTransferRegistration) ); return recipeTransferRegistration.getRecipeTransferHandlers(); } @@ -154,16 +147,14 @@ public RecipeManager createRecipeManager( RecipeCatalystRegistration recipeCatalystRegistration = new RecipeCatalystRegistration(ingredientManager, jeiHelpers); pluginCaller.callOnPlugins( "Registering recipe catalysts", - p -> p.registerRecipeCatalysts(recipeCatalystRegistration), - p -> p.registerRecipeCatalysts(recipeCatalystRegistration, clientExecutor) + p -> p.registerRecipeCatalysts(recipeCatalystRegistration) ); ImmutableListMultimap> recipeCatalysts = recipeCatalystRegistration.getRecipeCatalysts(); AdvancedRegistration advancedRegistration = new AdvancedRegistration(jeiHelpers); pluginCaller.callOnPlugins( "Registering advanced plugins", - p -> p.registerAdvanced(advancedRegistration), - p -> p.registerAdvanced(advancedRegistration, clientExecutor) + p -> p.registerAdvanced(advancedRegistration) ); List recipeManagerPlugins = advancedRegistration.getRecipeManagerPlugins(); @@ -182,8 +173,7 @@ public RecipeManager createRecipeManager( RecipeRegistration recipeRegistration = new RecipeRegistration(jeiHelpers, ingredientManager, ingredientVisibility, vanillaRecipeFactory, recipeManagerInternal); pluginCaller.callOnPlugins( "Registering recipes", - p -> p.registerRecipes(recipeRegistration), - p -> p.registerRecipes(recipeRegistration, clientExecutor) + p -> p.registerRecipes(recipeRegistration) ); Textures textures = Internal.getTextures(); diff --git a/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java b/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java index 772729015..b5d62db57 100644 --- a/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java +++ b/Library/src/main/java/mezz/jei/library/plugins/jei/JeiInternalPlugin.java @@ -1,28 +1,25 @@ package mezz.jei.library.plugins.jei; -import mezz.jei.api.IAsyncModPlugin; +import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.helpers.IGuiHelper; import mezz.jei.api.helpers.IJeiHelpers; import mezz.jei.api.registration.IRecipeCategoryRegistration; -import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.common.Internal; import mezz.jei.common.gui.textures.Textures; import mezz.jei.library.plugins.jei.info.IngredientInfoRecipeCategory; import net.minecraft.resources.ResourceLocation; -import java.util.concurrent.CompletableFuture; - @JeiPlugin -public class JeiInternalPlugin implements IAsyncModPlugin { +public class JeiInternalPlugin implements IModPlugin { @Override public ResourceLocation getPluginUid() { return new ResourceLocation(ModIds.JEI_ID, "internal"); } @Override - public CompletableFuture registerCategories(IRecipeCategoryRegistration registration, IJeiClientExecutor clientExecutor) { + public void registerCategories(IRecipeCategoryRegistration registration) { IJeiHelpers jeiHelpers = registration.getJeiHelpers(); IGuiHelper guiHelper = jeiHelpers.getGuiHelper(); Textures textures = Internal.getTextures(); @@ -30,6 +27,5 @@ public CompletableFuture registerCategories(IRecipeCategoryRegistration re registration.addRecipeCategories( new IngredientInfoRecipeCategory(guiHelper, textures) ); - return CompletableFuture.completedFuture(null); } } diff --git a/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java b/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java index 45cc443e9..6ca9de3c5 100644 --- a/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java +++ b/Library/src/main/java/mezz/jei/library/plugins/vanilla/VanillaPlugin.java @@ -1,6 +1,6 @@ package mezz.jei.library.plugins.vanilla; -import mezz.jei.api.IAsyncModPlugin; +import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.ModIds; import mezz.jei.api.constants.RecipeTypes; @@ -26,7 +26,6 @@ import mezz.jei.api.registration.ISubtypeRegistration; import mezz.jei.api.registration.IVanillaCategoryExtensionRegistration; import mezz.jei.api.runtime.IIngredientManager; -import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.common.Internal; import mezz.jei.common.gui.textures.Textures; import mezz.jei.common.platform.IPlatformFluidHelperInternal; @@ -112,12 +111,11 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import java.util.stream.Stream; @JeiPlugin -public class VanillaPlugin implements IAsyncModPlugin { +public class VanillaPlugin implements IModPlugin { private static final Logger LOGGER = LogManager.getLogger(); @Nullable @@ -141,7 +139,7 @@ public ResourceLocation getPluginUid() { } @Override - public CompletableFuture registerItemSubtypes(ISubtypeRegistration registration, IJeiClientExecutor clientExecutor) { + public void registerItemSubtypes(ISubtypeRegistration registration) { registration.registerSubtypeInterpreter(Items.TIPPED_ARROW, PotionSubtypeInterpreter.INSTANCE); registration.registerSubtypeInterpreter(Items.POTION, PotionSubtypeInterpreter.INSTANCE); registration.registerSubtypeInterpreter(Items.SPLASH_POTION, PotionSubtypeInterpreter.INSTANCE); @@ -163,11 +161,10 @@ public CompletableFuture registerItemSubtypes(ISubtypeRegistration registr enchantmentNames.sort(null); return enchantmentNames.toString(); }); - return CompletableFuture.completedFuture(null); } @Override - public CompletableFuture registerIngredients(IModIngredientRegistration registration, IJeiClientExecutor clientExecutor) { + public void registerIngredients(IModIngredientRegistration registration) { ISubtypeManager subtypeManager = registration.getSubtypeManager(); StackHelper stackHelper = new StackHelper(subtypeManager); @@ -179,7 +176,6 @@ public CompletableFuture registerIngredients(IModIngredientRegistration re IPlatformFluidHelperInternal platformFluidHelper = Services.PLATFORM.getFluidHelper(); registerFluidIngredients(registration, platformFluidHelper); - return CompletableFuture.completedFuture(null); } private void registerFluidIngredients(IModIngredientRegistration registration, IPlatformFluidHelperInternal platformFluidHelper) { @@ -195,7 +191,7 @@ private void registerFluidIngredients(IModIngredientRegistration registratio } @Override - public CompletableFuture registerCategories(IRecipeCategoryRegistration registration, IJeiClientExecutor clientExecutor) { + public void registerCategories(IRecipeCategoryRegistration registration) { Textures textures = Internal.getTextures(); IJeiHelpers jeiHelpers = registration.getJeiHelpers(); IGuiHelper guiHelper = jeiHelpers.getGuiHelper(); @@ -212,18 +208,16 @@ public CompletableFuture registerCategories(IRecipeCategoryRegistration re new BrewingRecipeCategory(guiHelper), new AnvilRecipeCategory(guiHelper) ); - return CompletableFuture.completedFuture(null); } @Override - public CompletableFuture registerVanillaCategoryExtensions(IVanillaCategoryExtensionRegistration registration, IJeiClientExecutor clientExecutor) { + public void registerVanillaCategoryExtensions(IVanillaCategoryExtensionRegistration registration) { IExtendableRecipeCategory craftingCategory = registration.getCraftingCategory(); craftingCategory.addCategoryExtension(CraftingRecipe.class, r -> !r.isSpecial(), CraftingCategoryExtension::new); - return CompletableFuture.completedFuture(null); } @Override - public CompletableFuture registerRecipes(IRecipeRegistration registration, IJeiClientExecutor clientExecutor) { + public void registerRecipes(IRecipeRegistration registration) { ErrorUtil.checkNotNull(craftingCategory, "craftingCategory"); ErrorUtil.checkNotNull(stonecuttingCategory, "stonecuttingCategory"); ErrorUtil.checkNotNull(furnaceCategory, "furnaceCategory"); @@ -260,11 +254,10 @@ public CompletableFuture registerRecipes(IRecipeRegistration registration, List brewingRecipes = recipeHelper.getBrewingRecipes(ingredientManager, vanillaRecipeFactory); brewingRecipes.sort(Comparator.comparingInt(IJeiBrewingRecipe::getBrewingSteps)); registration.addRecipes(RecipeTypes.BREWING, brewingRecipes); - return CompletableFuture.completedFuture(null); } @Override - public CompletableFuture registerGuiHandlers(IGuiHandlerRegistration registration, IJeiClientExecutor clientExecutor) { + public void registerGuiHandlers(IGuiHandlerRegistration registration) { registration.addRecipeClickArea(CraftingScreen.class, 88, 32, 28, 23, RecipeTypes.CRAFTING); registration.addRecipeClickArea(InventoryScreen.class, 137, 29, 10, 13, RecipeTypes.CRAFTING); registration.addRecipeClickArea(BrewingStandScreen.class, 97, 16, 14, 30, RecipeTypes.BREWING); @@ -278,11 +271,10 @@ public CompletableFuture registerGuiHandlers(IGuiHandlerRegistration regis registration.addGuiContainerHandler(CraftingScreen.class, new RecipeBookGuiHandler<>()); registration.addGuiContainerHandler(InventoryScreen.class, new RecipeBookGuiHandler<>()); registration.addGuiContainerHandler(AbstractFurnaceScreen.class, new RecipeBookGuiHandler<>()); - return CompletableFuture.completedFuture(null); } @Override - public CompletableFuture registerRecipeTransferHandlers(IRecipeTransferRegistration registration, IJeiClientExecutor clientExecutor) { + public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) { registration.addRecipeTransferHandler(CraftingMenu.class, MenuType.CRAFTING, RecipeTypes.CRAFTING, 1, 9, 10, 36); registration.addRecipeTransferHandler(FurnaceMenu.class, MenuType.FURNACE, RecipeTypes.SMELTING, 0, 1, 3, 36); registration.addRecipeTransferHandler(FurnaceMenu.class, MenuType.FURNACE, RecipeTypes.FUELING, 1, 1, 3, 36); @@ -297,11 +289,10 @@ public CompletableFuture registerRecipeTransferHandlers(IRecipeTransferReg IRecipeTransferHandlerHelper transferHelper = registration.getTransferHelper(); PlayerRecipeTransferHandler recipeTransferHandler = new PlayerRecipeTransferHandler(transferHelper); registration.addRecipeTransferHandler(recipeTransferHandler, RecipeTypes.CRAFTING); - return CompletableFuture.completedFuture(null); } @Override - public CompletableFuture registerRecipeCatalysts(IRecipeCatalystRegistration registration, IJeiClientExecutor clientExecutor) { + public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { registration.addRecipeCatalyst(new ItemStack(Blocks.CRAFTING_TABLE), RecipeTypes.CRAFTING); registration.addRecipeCatalyst(new ItemStack(Blocks.STONECUTTER), RecipeTypes.STONECUTTING); registration.addRecipeCatalyst(new ItemStack(Blocks.FURNACE), RecipeTypes.SMELTING, RecipeTypes.FUELING); @@ -314,7 +305,6 @@ public CompletableFuture registerRecipeCatalysts(IRecipeCatalystRegistrati registration.addRecipeCatalyst(new ItemStack(Blocks.ANVIL), RecipeTypes.ANVIL); registration.addRecipeCatalyst(new ItemStack(Blocks.SMITHING_TABLE), RecipeTypes.SMITHING); registration.addRecipeCatalyst(new ItemStack(Blocks.COMPOSTER), RecipeTypes.COMPOSTING); - return CompletableFuture.completedFuture(null); } public Optional getCraftingCategory() { diff --git a/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java b/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java index 146aa5819..e6a3befb1 100644 --- a/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java +++ b/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java @@ -14,11 +14,11 @@ import mezz.jei.api.recipe.category.IRecipeCategory; import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.api.runtime.IIngredientVisibility; -import mezz.jei.api.runtime.IJeiClientExecutor; import mezz.jei.common.gui.textures.Textures; import mezz.jei.common.util.ErrorUtil; import mezz.jei.library.gui.ingredients.RecipeSlot; import mezz.jei.library.gui.recipes.RecipeLayout; +import mezz.jei.library.startup.ClientTaskExecutor; import net.minecraft.resources.ResourceLocation; import java.util.Collection; @@ -32,7 +32,7 @@ public class RecipeManager implements IRecipeManager { private final IIngredientManager ingredientManager; private final Textures textures; private final IIngredientVisibility ingredientVisibility; - private final IJeiClientExecutor clientExecutor; + private final ClientTaskExecutor clientExecutor; public RecipeManager( RecipeManagerInternal internal, @@ -40,7 +40,7 @@ public RecipeManager( IIngredientManager ingredientManager, Textures textures, IIngredientVisibility ingredientVisibility, - IJeiClientExecutor clientExecutor + ClientTaskExecutor clientExecutor ) { this.internal = internal; this.modIdHelper = modIdHelper; @@ -70,7 +70,7 @@ public IRecipeCatalystLookup createRecipeCatalystLookup(RecipeType recipeType public void addRecipes(RecipeType recipeType, List recipes) { ErrorUtil.checkNotNull(recipeType, "recipeType"); ErrorUtil.checkNotNull(recipes, "recipes"); - clientExecutor.runOnClientThread(() -> internal.addRecipes(recipeType, recipes)); + clientExecutor.execute(() -> internal.addRecipes(recipeType, recipes)); } @Override @@ -100,26 +100,26 @@ public IRecipeSlotDrawable createRecipeSlotDrawable(RecipeIngredientRole role, L public void hideRecipes(RecipeType recipeType, Collection recipes) { ErrorUtil.checkNotNull(recipes, "recipe"); ErrorUtil.checkNotNull(recipeType, "recipeType"); - clientExecutor.runOnClientThread(() -> internal.hideRecipes(recipeType, recipes)); + clientExecutor.execute(() -> internal.hideRecipes(recipeType, recipes)); } @Override public void unhideRecipes(RecipeType recipeType, Collection recipes) { ErrorUtil.checkNotNull(recipes, "recipe"); ErrorUtil.checkNotNull(recipeType, "recipeType"); - clientExecutor.runOnClientThread(() -> internal.unhideRecipes(recipeType, recipes)); + clientExecutor.execute(() -> internal.unhideRecipes(recipeType, recipes)); } @Override public void hideRecipeCategory(RecipeType recipeType) { ErrorUtil.checkNotNull(recipeType, "recipeType"); - clientExecutor.runOnClientThread(() -> internal.hideRecipeCategory(recipeType)); + clientExecutor.execute(() -> internal.hideRecipeCategory(recipeType)); } @Override public void unhideRecipeCategory(RecipeType recipeType) { ErrorUtil.checkNotNull(recipeType, "recipeType"); - clientExecutor.runOnClientThread(() -> internal.unhideRecipeCategory(recipeType)); + clientExecutor.execute(() -> internal.unhideRecipeCategory(recipeType)); } @Override diff --git a/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java b/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java index 5e5ed0f03..a2ead9eff 100644 --- a/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java +++ b/Library/src/main/java/mezz/jei/library/recipes/RecipeManagerInternal.java @@ -9,7 +9,6 @@ import mezz.jei.api.recipe.category.IRecipeCategory; import mezz.jei.api.runtime.IIngredientManager; import mezz.jei.api.runtime.IIngredientVisibility; -import mezz.jei.common.async.JeiStartTask; import mezz.jei.common.util.ErrorUtil; import mezz.jei.library.config.RecipeCategorySortingConfig; import mezz.jei.library.ingredients.IIngredientSupplier; @@ -117,7 +116,6 @@ private void addRecipes(RecipeTypeData recipeTypeData, Collection reci if (ingredientSupplier == null) { return false; } - JeiStartTask.interruptIfCanceled(); return addRecipe(recipeCategory, recipe, ingredientSupplier); }) .toList(); diff --git a/Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java b/Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java index 65e9633bb..7b001741b 100644 --- a/Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java +++ b/Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java @@ -7,12 +7,11 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -public final class ClientTaskExecutor implements Executor, Tickable { +public final class ClientTaskExecutor implements Executor { private static final long TICK_BUDGET = TimeUnit.MILLISECONDS.toNanos(2); private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); - @Override public void tick() { final long startTime = System.nanoTime(); do { diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiClientExecutor.java b/Library/src/main/java/mezz/jei/library/startup/JeiClientExecutor.java deleted file mode 100644 index c13e114df..000000000 --- a/Library/src/main/java/mezz/jei/library/startup/JeiClientExecutor.java +++ /dev/null @@ -1,38 +0,0 @@ -package mezz.jei.library.startup; - -import mezz.jei.api.runtime.IJeiClientExecutor; -import net.minecraft.client.renderer.texture.Tickable; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.Supplier; - -public final class JeiClientExecutor implements IJeiClientExecutor, Tickable { - private final Executor executor; - - public JeiClientExecutor(Executor executor) { - this.executor = executor; - } - - @Override - public void tick() { - if (executor instanceof Tickable tickable) { - tickable.tick(); - } - } - - @Override - public Executor getExecutor() { - return executor; - } - - @Override - public CompletableFuture runOnClientThread(Runnable runnable) { - return CompletableFuture.runAsync(runnable, executor); - } - - @Override - public CompletableFuture runOnClientThread(Supplier supplier) { - return CompletableFuture.supplyAsync(supplier, executor); - } -} diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index ca7f75bf9..50e3fb940 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -1,7 +1,6 @@ package mezz.jei.library.startup; import com.google.common.collect.ImmutableTable; -import mezz.jei.api.IAsyncModPlugin; import mezz.jei.api.IModPlugin; import mezz.jei.api.helpers.IColorHelper; import mezz.jei.api.helpers.IModIdHelper; @@ -46,7 +45,6 @@ import java.nio.file.Path; import java.util.List; -import java.util.concurrent.CompletableFuture; public final class JeiStarter { private static final Logger LOGGER = LogManager.getLogger(); @@ -59,7 +57,7 @@ public final class JeiStarter { @SuppressWarnings("FieldCanBeLocal") private final FileWatcher fileWatcher = new FileWatcher("JEI Config File Watcher"); private final ConfigManager configManager; - private final JeiClientExecutor clientExecutor; + private final ClientTaskExecutor clientExecutor; private final PluginCaller pluginCaller; private final JeiClientConfigs jeiClientConfigs; @@ -68,12 +66,11 @@ public final class JeiStarter { public JeiStarter(StartData data) { this.data = data; List plugins = data.plugins(); - List asyncModPlugins = data.asyncPlugins(); - this.vanillaPlugin = PluginHelper.getPluginWithClass(VanillaPlugin.class, plugins, asyncModPlugins) + this.vanillaPlugin = PluginHelper.getPluginWithClass(VanillaPlugin.class, plugins) .orElseThrow(() -> new IllegalStateException("vanilla plugin not found")); - JeiInternalPlugin jeiInternalPlugin = PluginHelper.getPluginWithClass(JeiInternalPlugin.class, plugins, asyncModPlugins) + JeiInternalPlugin jeiInternalPlugin = PluginHelper.getPluginWithClass(JeiInternalPlugin.class, plugins) .orElse(null); - PluginHelper.sortPlugins(asyncModPlugins, vanillaPlugin, jeiInternalPlugin); + PluginHelper.sortPlugins(plugins, vanillaPlugin, jeiInternalPlugin); Path configDir = Services.PLATFORM.getConfigHelper().createJeiConfigDir(); @@ -99,19 +96,16 @@ public JeiStarter(StartData data) { this.recipeCategorySortingConfig = new RecipeCategorySortingConfig(configDir.resolve("recipe-category-sort-order.ini")); - this.clientExecutor = new JeiClientExecutor(new ClientTaskExecutor()); + this.clientExecutor = new ClientTaskExecutor(); this.pluginCaller = new PluginCaller( data.plugins(), - data.asyncPlugins(), data.runtimePlugin(), - clientExecutor, - jeiClientConfigs.getClientConfig() + clientExecutor ); pluginCaller.callOnPlugins( "Sending ConfigManager", - p -> p.onConfigManagerAvailable(configManager), - p -> p.onConfigManagerAvailable(configManager, clientExecutor) + p -> p.onConfigManagerAvailable(configManager) ); } @@ -195,8 +189,7 @@ private void doActualStart() { //noinspection removal pluginCaller.callOnPlugins( "Registering Runtime (legacy)", - p -> p.registerRuntime(runtimeRegistration), - p -> CompletableFuture.completedFuture(null) + p -> p.registerRuntime(runtimeRegistration) ); pluginCaller.callOnRuntimePlugin( "Registering Runtime", @@ -222,8 +215,7 @@ private void doActualStart() { pluginCaller.callOnPlugins( "Sending Runtime", - p -> p.onRuntimeAvailable(jeiRuntime), - p -> p.onRuntimeAvailable(jeiRuntime, clientExecutor) + p -> p.onRuntimeAvailable(jeiRuntime) ); pluginCaller.callOnRuntimePlugin( "Sending Runtime to Runtime Plugin", @@ -242,8 +234,7 @@ public void stop() { } pluginCaller.callOnPlugins( "Sending Runtime Unavailable", - IModPlugin::onRuntimeUnavailable, - p -> p.onRuntimeUnavailable(clientExecutor) + IModPlugin::onRuntimeUnavailable ); pluginCaller.callOnRuntimePlugin( "Sending Runtime Unavailable to Runtime Plugin", diff --git a/Library/src/main/java/mezz/jei/library/startup/StartData.java b/Library/src/main/java/mezz/jei/library/startup/StartData.java index 6759d93ab..e7da2ced5 100644 --- a/Library/src/main/java/mezz/jei/library/startup/StartData.java +++ b/Library/src/main/java/mezz/jei/library/startup/StartData.java @@ -1,6 +1,5 @@ package mezz.jei.library.startup; -import mezz.jei.api.IAsyncModPlugin; import mezz.jei.api.IModPlugin; import mezz.jei.api.IRuntimePlugin; import mezz.jei.api.constants.ModIds; @@ -16,7 +15,6 @@ public record StartData( List plugins, - List asyncPlugins, IRuntimePlugin runtimePlugin, IConnectionToServer serverConnection, IInternalKeyMappings keyBindings @@ -56,7 +54,6 @@ public static StartData create( return new StartData( pluginFinder.getPlugins(IModPlugin.class), - pluginFinder.getPlugins(IAsyncModPlugin.class), runtimePlugin, connectionToServer, keyBindings From 58f38d3a793d7a0a9face537713b0215b9e1ae27 Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 21 May 2023 20:21:14 -0700 Subject: [PATCH 19/22] remove parallel loading # Conflicts: # Library/src/main/java/mezz/jei/library/load/PluginCaller.java # Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java # Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java --- .../mezz/jei/library/load/PluginCaller.java | 66 ++++++------- .../jei/library/recipes/RecipeManager.java | 10 +- .../library/startup/ClientTaskExecutor.java | 92 ++++++++++++++----- .../mezz/jei/library/startup/JeiStarter.java | 10 +- 4 files changed, 110 insertions(+), 68 deletions(-) diff --git a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java index 6d2c525c4..9e8870861 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java @@ -16,6 +16,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Function; +import java.util.stream.Stream; public class PluginCaller { private static final Logger LOGGER = LogManager.getLogger(); @@ -41,20 +42,26 @@ private void callSync( Consumer func ) { Set erroredPlugins = ConcurrentHashMap.newKeySet(); - for (T plugin : plugins) { - try { - ResourceLocation pluginUid = uidFunc.apply(plugin); - try (var ignored = timer.begin(title, pluginUid)) { - func.accept(plugin); - } catch (RuntimeException | LinkageError e) { - LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); - erroredPlugins.add(plugin); - } - } catch (RuntimeException e) { - LOGGER.error("Caught an error from mod plugin: {}", plugin.getClass(), e); - erroredPlugins.add(plugin); - } - } + Stream runnables = plugins.stream() + .map(plugin -> { + return () -> { + try { + ResourceLocation pluginUid = uidFunc.apply(plugin); + try (var ignored = timer.begin(title, pluginUid)) { + clientExecutor.runAsync(() -> func.accept(plugin)); + } catch (RuntimeException | LinkageError e) { + LOGGER.error("Caught an error from mod plugin: {} {}", plugin.getClass(), pluginUid, e); + erroredPlugins.add(plugin); + } + } catch (RuntimeException e) { + LOGGER.error("Caught an error from mod plugin: {}", plugin.getClass(), e); + erroredPlugins.add(plugin); + } + }; + }); + + clientExecutor.runAsync(runnables); + plugins.removeAll(erroredPlugins); } @@ -67,15 +74,13 @@ public void callOnPlugins( Stopwatch stopwatch = Stopwatch.createStarted(); try (PluginCallerTimer timer = new PluginCallerTimer()) { - CompletableFuture.runAsync(() -> { - callSync( - title, - timer, - plugins, - IModPlugin::getPluginUid, - func - ); - }, clientExecutor).join(); + callSync( + title, + timer, + plugins, + IModPlugin::getPluginUid, + func + ); } LOGGER.info("{} took {}", title, stopwatch); @@ -91,20 +96,7 @@ public void callOnRuntimePlugin( try (PluginCallerTimer timer = new PluginCallerTimer()) { ResourceLocation pluginUid = runtimePlugin.getPluginUid(); try (var ignored = timer.begin(title, pluginUid)) { - CompletableFuture future = CompletableFuture.supplyAsync(() -> asyncFun.apply(runtimePlugin)) - .thenCompose(f -> f); - - Minecraft minecraft = Minecraft.getInstance(); - if (minecraft.isSameThread()) { - minecraft.managedBlock(() -> { - if (future.isDone()) { - return true; - } - clientExecutor.tick(); - return false; - }); - } - future.join(); + clientExecutor.runAsync(() -> asyncFun.apply(runtimePlugin)); } } diff --git a/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java b/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java index e6a3befb1..0a505492e 100644 --- a/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java +++ b/Library/src/main/java/mezz/jei/library/recipes/RecipeManager.java @@ -70,7 +70,7 @@ public IRecipeCatalystLookup createRecipeCatalystLookup(RecipeType recipeType public void addRecipes(RecipeType recipeType, List recipes) { ErrorUtil.checkNotNull(recipeType, "recipeType"); ErrorUtil.checkNotNull(recipes, "recipes"); - clientExecutor.execute(() -> internal.addRecipes(recipeType, recipes)); + clientExecutor.runAsync(() -> internal.addRecipes(recipeType, recipes)); } @Override @@ -100,26 +100,26 @@ public IRecipeSlotDrawable createRecipeSlotDrawable(RecipeIngredientRole role, L public void hideRecipes(RecipeType recipeType, Collection recipes) { ErrorUtil.checkNotNull(recipes, "recipe"); ErrorUtil.checkNotNull(recipeType, "recipeType"); - clientExecutor.execute(() -> internal.hideRecipes(recipeType, recipes)); + clientExecutor.runAsync(() -> internal.hideRecipes(recipeType, recipes)); } @Override public void unhideRecipes(RecipeType recipeType, Collection recipes) { ErrorUtil.checkNotNull(recipes, "recipe"); ErrorUtil.checkNotNull(recipeType, "recipeType"); - clientExecutor.execute(() -> internal.unhideRecipes(recipeType, recipes)); + clientExecutor.runAsync(() -> internal.unhideRecipes(recipeType, recipes)); } @Override public void hideRecipeCategory(RecipeType recipeType) { ErrorUtil.checkNotNull(recipeType, "recipeType"); - clientExecutor.execute(() -> internal.hideRecipeCategory(recipeType)); + clientExecutor.runAsync(() -> internal.hideRecipeCategory(recipeType)); } @Override public void unhideRecipeCategory(RecipeType recipeType) { ErrorUtil.checkNotNull(recipeType, "recipeType"); - clientExecutor.execute(() -> internal.unhideRecipeCategory(recipeType)); + clientExecutor.runAsync(() -> internal.unhideRecipeCategory(recipeType)); } @Override diff --git a/Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java b/Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java index 7b001741b..fdfeef020 100644 --- a/Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java +++ b/Library/src/main/java/mezz/jei/library/startup/ClientTaskExecutor.java @@ -1,37 +1,85 @@ package mezz.jei.library.startup; import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.renderer.texture.Tickable; +import net.minecraft.client.Minecraft; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import java.util.stream.Stream; -public final class ClientTaskExecutor implements Executor { - private static final long TICK_BUDGET = TimeUnit.MILLISECONDS.toNanos(2); - - private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); +public final class ClientTaskExecutor { + private final InternalExecutor executor = new InternalExecutor(); public void tick() { - final long startTime = System.nanoTime(); - do { - Runnable r = this.taskQueue.poll(); - if (r != null) { - r.run(); - } else { - return; - } - } while ((System.nanoTime() - startTime) < TICK_BUDGET); + executor.tick(); } - @Override - public void execute(Runnable runnable) { - if (RenderSystem.isOnRenderThreadOrInit()) { - // we can't queue on the client render thread, - // it would block forever waiting for the next tick to happen - runnable.run(); - } else { - this.taskQueue.add(runnable); + public void runAsync(Stream runnables) { + Stream> futures = runnables.map(r -> CompletableFuture.runAsync(r, executor)); + CompletableFuture future = CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)); + join(future); + } + + public void runAsync(Runnable runnable) { + CompletableFuture future = CompletableFuture.runAsync(runnable, executor); + join(future); + } + + public void runAsync(Supplier> supplier) { + CompletableFuture future = CompletableFuture.supplyAsync(supplier, executor) + .thenCompose(f -> f); + join(future); + } + + @SuppressWarnings("UnusedReturnValue") + private T join(CompletableFuture future) { + Minecraft minecraft = Minecraft.getInstance(); + if (minecraft.isSameThread()) { + minecraft.managedBlock(() -> { + if (future.isDone()) { + return true; + } + tick(); + return false; + }); } + return future.join(); + } + + public InternalExecutor getExecutor() { + return executor; } + + private static final class InternalExecutor implements Executor { + private static final long TICK_BUDGET = TimeUnit.MILLISECONDS.toNanos(2); + + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + public void tick() { + final long startTime = System.nanoTime(); + do { + Runnable r = this.taskQueue.poll(); + if (r != null) { + r.run(); + } else { + return; + } + } while ((System.nanoTime() - startTime) < TICK_BUDGET); + } + + @Override + public void execute(Runnable runnable) { + if (RenderSystem.isOnRenderThreadOrInit()) { + // we can't queue on the client render thread, + // it would block forever waiting for the next tick to happen + runnable.run(); + } else { + taskQueue.add(runnable); + } + } + } + } diff --git a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java index 50e3fb940..0dc1f32dd 100644 --- a/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java +++ b/Library/src/main/java/mezz/jei/library/startup/JeiStarter.java @@ -14,6 +14,7 @@ import mezz.jei.common.async.JeiStartTask; import mezz.jei.common.config.ConfigManager; import mezz.jei.common.config.DebugConfig; +import mezz.jei.common.config.IClientConfig; import mezz.jei.common.config.IClientToggleState; import mezz.jei.common.config.JeiClientConfigs; import mezz.jei.common.config.file.ConfigSchemaBuilder; @@ -124,7 +125,8 @@ public void start() { return; } - if (jeiClientConfigs.getClientConfig().getAsyncLoadingEnabled()) { + IClientConfig clientConfig = jeiClientConfigs.getClientConfig(); + if (clientConfig.getAsyncLoadingEnabled()) { currentStartTask = new JeiStartTask(this::doActualStart); currentStartTask.start(); } else { @@ -193,7 +195,7 @@ private void doActualStart() { ); pluginCaller.callOnRuntimePlugin( "Registering Runtime", - p -> p.registerRuntime(runtimeRegistration, clientExecutor) + p -> p.registerRuntime(runtimeRegistration, clientExecutor.getExecutor()) ); JeiRuntime jeiRuntime = new JeiRuntime( @@ -219,7 +221,7 @@ private void doActualStart() { ); pluginCaller.callOnRuntimePlugin( "Sending Runtime to Runtime Plugin", - p -> p.onRuntimeAvailable(jeiRuntime, clientExecutor) + p -> p.onRuntimeAvailable(jeiRuntime, clientExecutor.getExecutor()) ); totalTime.stop(); @@ -238,7 +240,7 @@ public void stop() { ); pluginCaller.callOnRuntimePlugin( "Sending Runtime Unavailable to Runtime Plugin", - p -> p.onRuntimeUnavailable(clientExecutor) + p -> p.onRuntimeUnavailable(clientExecutor.getExecutor()) ); } From 4bfb889d30de1b7da32a79786d28489cc0ed6cf7 Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 21 May 2023 20:22:18 -0700 Subject: [PATCH 20/22] fix config description --- Common/src/main/java/mezz/jei/common/config/ClientConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java index 2749123e7..0debb50a0 100644 --- a/Common/src/main/java/mezz/jei/common/config/ClientConfig.java +++ b/Common/src/main/java/mezz/jei/common/config/ClientConfig.java @@ -70,7 +70,7 @@ public ClientConfig(IConfigSchemaBuilder schema) { asyncLoadingEnabled = loading.addBoolean( "asyncLoadingEnabled", false, - "Whether JEI should load asynchronously, so that it starts finishes loading after world join." + "Whether JEI should load asynchronously, so that it finishes loading after world join." ); IConfigCategoryBuilder sorting = schema.addCategory("sorting"); From 89aa6dabe6e64bb45b2cf3905269e22a59258841 Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 21 May 2023 20:26:14 -0700 Subject: [PATCH 21/22] fix fabric mod plugin declarations --- Fabric/src/main/resources/fabric.mod.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Fabric/src/main/resources/fabric.mod.json b/Fabric/src/main/resources/fabric.mod.json index fd092646a..1144d975b 100644 --- a/Fabric/src/main/resources/fabric.mod.json +++ b/Fabric/src/main/resources/fabric.mod.json @@ -25,10 +25,8 @@ "mezz.jei.fabric.JustEnoughItemsClient" ], "jei_mod_plugin": [ + "mezz.jei.gui.plugins.JeiGuiPlugin", "mezz.jei.library.plugins.debug.JeiDebugPlugin", - "mezz.jei.gui.plugins.JeiGuiPlugin" - ], - "jei_async_mod_plugin": [ "mezz.jei.library.plugins.jei.JeiInternalPlugin", "mezz.jei.library.plugins.vanilla.VanillaPlugin" ], From e81d6bf869e877821df6b8d1c188ed2fb617ebe3 Mon Sep 17 00:00:00 2001 From: mezz Date: Sun, 21 May 2023 20:26:25 -0700 Subject: [PATCH 22/22] clean up imports --- Library/src/main/java/mezz/jei/library/load/PluginCaller.java | 1 - Library/src/main/java/mezz/jei/library/load/PluginHelper.java | 1 - 2 files changed, 2 deletions(-) diff --git a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java index 9e8870861..cd1dd7888 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginCaller.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginCaller.java @@ -5,7 +5,6 @@ import mezz.jei.api.IRuntimePlugin; import mezz.jei.common.async.JeiStartTask; import mezz.jei.library.startup.ClientTaskExecutor; -import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/Library/src/main/java/mezz/jei/library/load/PluginHelper.java b/Library/src/main/java/mezz/jei/library/load/PluginHelper.java index 0f2e59e47..ece9b5b3e 100644 --- a/Library/src/main/java/mezz/jei/library/load/PluginHelper.java +++ b/Library/src/main/java/mezz/jei/library/load/PluginHelper.java @@ -4,7 +4,6 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Optional; -import java.util.stream.Stream; import mezz.jei.api.IModPlugin; import mezz.jei.library.plugins.vanilla.VanillaPlugin;