Add async JEI loading

This commit is contained in:
embeddedt 2023-01-05 20:50:30 -05:00
parent 21d559ab94
commit 41da489c85
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
12 changed files with 245 additions and 10 deletions

View File

@ -121,14 +121,16 @@ repositories {
includeGroup "curse.maven"
}
}
// Put repositories for dependencies here
// ForgeGradle automatically adds the Forge maven and Maven Central for you
// If you have mod jar dependencies in ./libs, you can declare them as a repository like so:
// flatDir {
// dir 'libs'
// }
maven {
// location of the maven that hosts JEI files
name = "Progwml6 maven"
url = "https://dvs1.progwml6.com/files/maven/"
}
maven {
// location of a maven mirror for JEI files, as a fallback
name = "ModMaven"
url = "https://modmaven.dev"
}
}
dependencies {
@ -148,6 +150,9 @@ dependencies {
runtimeOnly fg.deobf("mekanism:Mekanism:${mekanism_version}")// core
runtimeOnly fg.deobf("mekanism:Mekanism:${mekanism_version}:generators")// Mekanism: Generators
runtimeOnly fg.deobf("mekanism:Mekanism:${mekanism_version}:tools")// Mekanism: Tools
compileOnly fg.deobf("mezz.jei:jei-${minecraft_version}:${jei_version}")
runtimeOnly fg.deobf("mezz.jei:jei-${minecraft_version}:${jei_version}")
}
// Example for how to get properties into the manifest for reading at runtime.

View File

@ -4,4 +4,5 @@ minecraft_version=1.16.5
forge_version=36.2.39
lazydfu_version=3249059
mekanism_version=1.16.5-10.1.2.457
parchment_version=2022.03.06
parchment_version=2022.03.06
jei_version=7.7.1.153

View File

@ -28,6 +28,7 @@ public class ModernFixConfig {
this.addMixinRule("perf.trim_model_caches", true);
this.addMixinRule("bugfix.concurrency", true);
this.addMixinRule("bugfix.edge_chunk_not_saved", true);
this.addMixinRule("perf.async_jei", true);
}
/**

View File

@ -0,0 +1,8 @@
package org.embeddedt.modernfix.jei.async;
public interface IAsyncJeiStarter {
static void checkForLoadInterruption() {
if(Thread.currentThread().isInterrupted())
throw new JEILoadingInterruptedException();
}
}

View File

@ -0,0 +1,4 @@
package org.embeddedt.modernfix.jei.async;
public class JEILoadingInterruptedException extends RuntimeException {
}

View File

@ -0,0 +1,98 @@
package org.embeddedt.modernfix.mixin.perf.async_jei;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.helpers.IModIdHelper;
import mezz.jei.config.*;
import mezz.jei.config.sorting.RecipeCategorySortingConfig;
import mezz.jei.events.EventBusHelper;
import mezz.jei.events.PlayerJoinedWorldEvent;
import mezz.jei.gui.textures.Textures;
import mezz.jei.ingredients.IIngredientSorter;
import mezz.jei.startup.ClientLifecycleHandler;
import mezz.jei.startup.JeiStarter;
import mezz.jei.startup.NetworkHandler;
import net.minecraft.client.Minecraft;
import net.minecraftforge.client.event.ClientPlayerNetworkEvent;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.jei.async.JEILoadingInterruptedException;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(ClientLifecycleHandler.class)
public class ClientLifecycleHandlerMixin {
@Shadow @Final private JeiStarter starter;
@Shadow @Final private List<IModPlugin> plugins;
@Shadow @Final private Textures textures;
@Shadow @Final private IClientConfig clientConfig;
@Shadow @Final private IEditModeConfig editModeConfig;
@Shadow @Final private IngredientFilterConfig ingredientFilterConfig;
@Shadow @Final private WorldConfig worldConfig;
@Shadow @Final private BookmarkConfig bookmarkConfig;
@Shadow @Final private IModIdHelper modIdHelper;
@Shadow @Final private RecipeCategorySortingConfig recipeCategorySortingConfig;
@Shadow @Final private IIngredientSorter ingredientSorter;
private volatile Thread reloadThread = null;
@Inject(method = "setupJEI", at = @At(value = "INVOKE", target = "Lmezz/jei/startup/ClientLifecycleHandler;startJEI()V"), cancellable = true, remap = false)
private void startAsync(CallbackInfo ci) {
ci.cancel();
startJEIAsync(() -> Minecraft.getInstance().execute(() -> EventBusHelper.post(new PlayerJoinedWorldEvent())));
}
/**
* @author embeddedt
* @reason force JEI starts to be asynchronous
*/
@Overwrite(remap = false)
public void startJEI() {
startJEIAsync(() -> {});
}
@Inject(method = "<init>", at = @At("TAIL"))
private void setupCancellationHandler(NetworkHandler networkHandler, Textures textures, CallbackInfo ci) {
EventBusHelper.addListener(this, ClientPlayerNetworkEvent.LoggedOutEvent.class, event -> cancelPreviousStart());
}
private void cancelPreviousStart() {
Thread currentReloadThread = reloadThread;
if(currentReloadThread != null) {
currentReloadThread.interrupt();
Minecraft.getInstance().managedBlock(currentReloadThread::isAlive);
reloadThread = null;
}
}
private void startJEIAsync(Runnable whenFinishedCb) {
cancelPreviousStart();
Thread newThread = new Thread(() -> {
try {
starter.start(
plugins,
textures,
clientConfig,
editModeConfig,
ingredientFilterConfig,
worldConfig,
bookmarkConfig,
modIdHelper,
recipeCategorySortingConfig,
ingredientSorter);
} catch(JEILoadingInterruptedException e) {
ModernFix.LOGGER.warn("JEI loading interrupted prematurely (this is normal)");
}
whenFinishedCb.run();
reloadThread = null;
}, "JEI Reload Thread");
newThread.setPriority(Thread.MIN_PRIORITY);
reloadThread = newThread;
newThread.start();
}
}

View File

@ -0,0 +1,20 @@
package org.embeddedt.modernfix.mixin.perf.async_jei;
import mezz.jei.api.ingredients.IIngredientType;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.gui.ingredients.IIngredientListElement;
import mezz.jei.ingredients.IngredientListElementFactory;
import net.minecraft.util.NonNullList;
import org.embeddedt.modernfix.jei.async.IAsyncJeiStarter;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(IngredientListElementFactory.class)
public class IngredientListElementFactoryMixin {
@Inject(method = "addToBaseList", at = @At(value = "INVOKE", target = "Lmezz/jei/ingredients/IngredientOrderTracker;getOrderIndex(Ljava/lang/Object;Lmezz/jei/api/ingredients/IIngredientHelper;)I"), remap = false)
private static void checkForInterrupt(NonNullList<IIngredientListElement<?>> baseList, IIngredientManager ingredientManager, IIngredientType ingredientType, CallbackInfo ci) {
IAsyncJeiStarter.checkForLoadInterruption();
}
}

View File

@ -0,0 +1,47 @@
package org.embeddedt.modernfix.mixin.perf.async_jei;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.helpers.IModIdHelper;
import mezz.jei.config.*;
import mezz.jei.config.sorting.RecipeCategorySortingConfig;
import mezz.jei.gui.textures.Textures;
import mezz.jei.ingredients.IIngredientSorter;
import mezz.jei.load.PluginCaller;
import mezz.jei.startup.JeiStarter;
import net.minecraft.client.Minecraft;
import org.embeddedt.modernfix.jei.async.IAsyncJeiStarter;
import org.embeddedt.modernfix.jei.async.JEILoadingInterruptedException;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CancellationException;
import java.util.List;
import java.util.concurrent.CompletionException;
import java.util.function.Consumer;
@Mixin(JeiStarter.class)
public class JeiStarterMixin {
@Shadow private boolean started;
@Inject(method = "start", at = @At(value = "INVOKE", target = "Lmezz/jei/util/ErrorUtil;checkNotEmpty(Ljava/util/Collection;Ljava/lang/String;)V", ordinal = 0, shift = At.Shift.AFTER), remap = false)
private void setStartedFlag(List<IModPlugin> plugins, Textures textures, IClientConfig clientConfig, IEditModeConfig editModeConfig, IIngredientFilterConfig ingredientFilterConfig, IWorldConfig worldConfig, BookmarkConfig bookmarkConfig, IModIdHelper modIdHelper, RecipeCategorySortingConfig recipeCategorySortingConfig, IIngredientSorter ingredientSorter, CallbackInfo ci) {
/* We need to set this ASAP so the reload system will restart the async load if needed */
started = true;
}
@Redirect(method = "start", at = @At(value = "INVOKE", target = "Lmezz/jei/load/PluginCaller;callOnPlugins(Ljava/lang/String;Ljava/util/List;Ljava/util/function/Consumer;)V"), remap = false)
private void callOnPluginsViaMainThread(String title, List<IModPlugin> plugins, Consumer<IModPlugin> func) {
PluginCaller.callOnPlugins(title, plugins, plugin -> {
try {
Minecraft.getInstance().executeBlocking(() -> func.accept(plugin));
} catch(CancellationException | CompletionException e) {
Thread.currentThread().interrupt();
}
});
}
}

View File

@ -0,0 +1,21 @@
package org.embeddedt.modernfix.mixin.perf.async_jei;
import mezz.jei.api.IModPlugin;
import mezz.jei.load.PluginCaller;
import org.embeddedt.modernfix.jei.async.IAsyncJeiStarter;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
import java.util.function.Consumer;
@Mixin(PluginCaller.class)
public class PluginCallerMixin {
@Inject(method = "callOnPlugins", at = @At(value = "INVOKE", target = "Ljava/util/Iterator;hasNext()Z"), remap = false)
private static void checkForInterrupt(String title, List<IModPlugin> plugins, Consumer<IModPlugin> func, CallbackInfo ci) {
IAsyncJeiStarter.checkForLoadInterruption();
}
}

View File

@ -0,0 +1,18 @@
package org.embeddedt.modernfix.mixin.perf.async_jei;
import com.google.common.collect.ImmutableListMultimap;
import mezz.jei.recipes.RecipeManagerInternal;
import net.minecraft.util.ResourceLocation;
import org.embeddedt.modernfix.jei.async.IAsyncJeiStarter;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(RecipeManagerInternal.class)
public class RecipeManagerInternalMixin {
@Inject(method = "addRecipes", at = @At(value = "INVOKE", target = "Lmezz/jei/recipes/RecipeManagerInternal;addRecipeTyped(Ljava/lang/Object;Lnet/minecraft/util/ResourceLocation;)V"), remap = false)
private void checkForInterrupt(ImmutableListMultimap<ResourceLocation, Object> recipes, CallbackInfo ci) {
IAsyncJeiStarter.checkForLoadInterruption();
}
}

View File

@ -56,3 +56,10 @@ mandatory = true
versionRange = "[1.16.5,1.17)"
ordering = "NONE"
side = "BOTH"
[[dependencies.modernfix]]
modId = "jei"
mandatory = false
# This version range declares a minimum of the current minecraft version up to but not including the next major version
versionRange = "[7.7.1.153,)"
ordering = "BEFORE"
side = "CLIENT"

View File

@ -22,7 +22,12 @@
"perf.skip_first_datapack_reload.MinecraftMixin",
"bugfix.concurrency.RenderTypeMixin",
"perf.parallelize_model_loading.ModelBakeryMixin",
"perf.trim_model_caches.ModelManagerMixin"
"perf.trim_model_caches.ModelManagerMixin",
"perf.async_jei.IngredientListElementFactoryMixin",
"perf.async_jei.ClientLifecycleHandlerMixin",
"perf.async_jei.JeiStarterMixin",
"perf.async_jei.PluginCallerMixin",
"perf.async_jei.RecipeManagerInternalMixin"
],
"injectors": {
"defaultRequire": 1