diff --git a/common/build.gradle b/common/build.gradle index 9ed3b9ae..e0e3b085 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -23,6 +23,10 @@ dependencies { modCompileOnly("me.shedaniel:RoughlyEnoughItems-fabric:${rei_version}") { transitive = false } + // compile against the JEI API but do not include it at runtime + modCompileOnly("mezz.jei:jei-${minecraft_version}-common:${jei_version}") + modCompileOnly("mezz.jei:jei-${minecraft_version}-gui:${jei_version}") + modCompileOnly("mezz.jei:jei-${minecraft_version}-lib:${jei_version}") // Remove the next line if you don't want to depend on the API // modApi "me.shedaniel:architectury:${rootProject.architectury_version}" } diff --git a/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java b/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java index c65a5ff8..7a020414 100644 --- a/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java +++ b/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java @@ -14,6 +14,9 @@ import net.minecraft.world.entity.Entity; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.packet.EntityIDSyncPacket; import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; +import org.embeddedt.modernfix.searchtree.JEIBackedSearchTree; +import org.embeddedt.modernfix.searchtree.REIBackedSearchTree; +import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry; import org.embeddedt.modernfix.world.IntegratedWatchdog; import java.lang.management.ManagementFactory; @@ -36,6 +39,8 @@ public class ModernFixClient { if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) { brandingString = "ModernFix " + ModernFixPlatformHooks.getVersionString(); } + SearchTreeProviderRegistry.register(JEIBackedSearchTree.PROVIDER); + SearchTreeProviderRegistry.register(REIBackedSearchTree.PROVIDER); } public void resetWorldLoadStateMachine() { diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/blast_search_trees/MinecraftMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/blast_search_trees/MinecraftMixin.java new file mode 100644 index 00000000..c534f65c --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/blast_search_trees/MinecraftMixin.java @@ -0,0 +1,32 @@ +package org.embeddedt.modernfix.common.mixin.perf.blast_search_trees; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.searchtree.SearchRegistry; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.searchtree.DummySearchTree; +import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +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; + +@Mixin(Minecraft.class) +@ClientOnlyMixin +public class MinecraftMixin { + @Shadow @Final private SearchRegistry searchRegistry; + + @Inject(method = "createSearchTrees", at = @At("HEAD"), cancellable = true) + private void replaceSearchTrees(CallbackInfo ci) { + SearchTreeProviderRegistry.Provider provider = SearchTreeProviderRegistry.getSearchTreeProvider(); + if(provider == null) + return; + ModernFix.LOGGER.info("Replacing search trees with '{}' provider", provider.getName()); + this.searchRegistry.register(SearchRegistry.CREATIVE_NAMES, list -> provider.getSearchTree(false)); + this.searchRegistry.register(SearchRegistry.CREATIVE_TAGS, list -> provider.getSearchTree(true)); + this.searchRegistry.register(SearchRegistry.RECIPE_COLLECTIONS, list -> new DummySearchTree<>()); + ci.cancel(); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index 1e49dcff..1d072c10 100644 --- a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -130,13 +130,8 @@ public class ModernFixEarlyConfig { } } - private static final boolean shouldReplaceSearchTrees; private static final boolean isDevEnv = ModernFixPlatformHooks.isDevEnv(); - static { - shouldReplaceSearchTrees = modPresent("jei"); - } - private static final ImmutableMap DEFAULT_SETTING_OVERRIDES = ImmutableMap.builder() .put("mixin.perf.dynamic_resources", false) .put("mixin.feature.direct_stack_trace", false) @@ -148,7 +143,6 @@ public class ModernFixEarlyConfig { .put("mixin.perf.dynamic_entity_renderers", false) .put("mixin.feature.integrated_server_watchdog", true) .put("mixin.perf.faster_item_rendering", false) - .put("mixin.perf.blast_search_trees", shouldReplaceSearchTrees) .put("mixin.devenv", isDevEnv) .put("mixin.perf.remove_spawn_chunks", isDevEnv) .build(); diff --git a/common/src/main/java/org/embeddedt/modernfix/searchtree/DummySearchTree.java b/common/src/main/java/org/embeddedt/modernfix/searchtree/DummySearchTree.java index f4ef7104..a430faaf 100644 --- a/common/src/main/java/org/embeddedt/modernfix/searchtree/DummySearchTree.java +++ b/common/src/main/java/org/embeddedt/modernfix/searchtree/DummySearchTree.java @@ -1,6 +1,7 @@ package org.embeddedt.modernfix.searchtree; import net.minecraft.client.searchtree.RefreshableSearchTree; +import net.minecraft.world.item.ItemStack; import java.util.Collections; import java.util.List; @@ -22,4 +23,22 @@ public class DummySearchTree implements RefreshableSearchTree { public List search(String pSearchText) { return Collections.emptyList(); } + + static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() { + + @Override + public RefreshableSearchTree getSearchTree(boolean tag) { + return new DummySearchTree<>(); + } + + @Override + public boolean canUse() { + return true; + } + + @Override + public String getName() { + return "Dummy"; + } + }; } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/searchtree/JEIBackedSearchTree.java b/common/src/main/java/org/embeddedt/modernfix/searchtree/JEIBackedSearchTree.java similarity index 50% rename from forge/src/main/java/org/embeddedt/modernfix/forge/searchtree/JEIBackedSearchTree.java rename to common/src/main/java/org/embeddedt/modernfix/searchtree/JEIBackedSearchTree.java index 85c4839b..e237733a 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/searchtree/JEIBackedSearchTree.java +++ b/common/src/main/java/org/embeddedt/modernfix/searchtree/JEIBackedSearchTree.java @@ -1,15 +1,19 @@ -package org.embeddedt.modernfix.forge.searchtree; +package org.embeddedt.modernfix.searchtree; +import com.google.common.collect.ImmutableList; import mezz.jei.api.ingredients.ITypedIngredient; import mezz.jei.gui.ingredients.IngredientFilter; import mezz.jei.gui.ingredients.IngredientFilterApi; import mezz.jei.library.runtime.JeiRuntime; +import net.minecraft.client.searchtree.RefreshableSearchTree; import net.minecraft.world.item.ItemStack; import org.embeddedt.modernfix.ModernFix; -import org.embeddedt.modernfix.forge.mixin.perf.blast_search_trees.IngredientFilterInvoker; -import org.embeddedt.modernfix.searchtree.DummySearchTree; +import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -23,7 +27,25 @@ public class JEIBackedSearchTree extends DummySearchTree { private String lastSearchText = ""; private final List listCache = new ArrayList<>(); - private static Field filterField = null; + private static final Field filterField; + private static final MethodHandle getIngredientListUncached; + + static { + MethodHandle m; + Field f; + try { + Method jeiMethod = IngredientFilter.class.getDeclaredMethod("getIngredientListUncached", String.class); + jeiMethod.setAccessible(true); + m = MethodHandles.lookup().unreflect(jeiMethod); + f = IngredientFilterApi.class.getDeclaredField("ingredientFilter"); + f.setAccessible(true); + } catch(ReflectiveOperationException | RuntimeException | NoClassDefFoundError e) { + m = null; + f = null; + } + getIngredientListUncached = m; + filterField = f; + } public JEIBackedSearchTree(boolean filteringByTag) { this.filteringByTag = filteringByTag; @@ -35,10 +57,6 @@ public class JEIBackedSearchTree extends DummySearchTree { IngredientFilterApi iFilterApi = (IngredientFilterApi)runtime.get().getIngredientFilter(); IngredientFilter filter; try { - if(filterField == null) { - filterField = IngredientFilterApi.class.getDeclaredField("ingredientFilter"); - filterField.setAccessible(true); - } filter = (IngredientFilter)filterField.get(iFilterApi); } catch(ReflectiveOperationException e) { ModernFix.LOGGER.error(e); @@ -54,7 +72,14 @@ public class JEIBackedSearchTree extends DummySearchTree { private List searchJEI(IngredientFilter filter, String pSearchText) { if(!pSearchText.equals(lastSearchText)) { listCache.clear(); - List> ingredients = ((IngredientFilterInvoker)filter).invokeGetIngredientListUncached(filteringByTag ? ("$" + pSearchText) : pSearchText); + List> ingredients; + String finalSearchTerm = filteringByTag ? ("$" + pSearchText) : pSearchText; + try { + ingredients = (List>)getIngredientListUncached.invokeExact(filter, finalSearchTerm); + } catch(Throwable e) { + ModernFix.LOGGER.error("Error searching", e); + ingredients = ImmutableList.of(); + } for(ITypedIngredient ingredient : ingredients) { if(ingredient.getIngredient() instanceof ItemStack) { listCache.add((ItemStack)ingredient.getIngredient()); @@ -64,4 +89,21 @@ public class JEIBackedSearchTree extends DummySearchTree { } return listCache; } + + public static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() { + @Override + public RefreshableSearchTree getSearchTree(boolean tag) { + return new JEIBackedSearchTree(tag); + } + + @Override + public boolean canUse() { + return ModernFixPlatformHooks.modPresent("jei") && getIngredientListUncached != null && filterField != null; + } + + @Override + public String getName() { + return "JEI"; + } + }; } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/searchtree/JEIRuntimeCapturer.java b/common/src/main/java/org/embeddedt/modernfix/searchtree/JEIRuntimeCapturer.java similarity index 94% rename from forge/src/main/java/org/embeddedt/modernfix/forge/searchtree/JEIRuntimeCapturer.java rename to common/src/main/java/org/embeddedt/modernfix/searchtree/JEIRuntimeCapturer.java index c8c6bc6d..9a24b52e 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/searchtree/JEIRuntimeCapturer.java +++ b/common/src/main/java/org/embeddedt/modernfix/searchtree/JEIRuntimeCapturer.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.forge.searchtree; +package org.embeddedt.modernfix.searchtree; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; diff --git a/common/src/main/java/org/embeddedt/modernfix/searchtree/REIBackedSearchTree.java b/common/src/main/java/org/embeddedt/modernfix/searchtree/REIBackedSearchTree.java index 7bb9f954..f48bb6ac 100644 --- a/common/src/main/java/org/embeddedt/modernfix/searchtree/REIBackedSearchTree.java +++ b/common/src/main/java/org/embeddedt/modernfix/searchtree/REIBackedSearchTree.java @@ -4,8 +4,10 @@ import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; import me.shedaniel.rei.api.common.entry.EntryStack; import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; import me.shedaniel.rei.impl.client.search.AsyncSearchManager; +import net.minecraft.client.searchtree.RefreshableSearchTree; import net.minecraft.world.item.ItemStack; import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; import java.util.ArrayList; import java.util.Collections; @@ -53,4 +55,21 @@ public class REIBackedSearchTree extends DummySearchTree { } return listCache; } + + public static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() { + @Override + public RefreshableSearchTree getSearchTree(boolean tag) { + return new REIBackedSearchTree(tag); + } + + @Override + public boolean canUse() { + return ModernFixPlatformHooks.modPresent("roughlyenoughitems"); + } + + @Override + public String getName() { + return "REI"; + } + }; } diff --git a/common/src/main/java/org/embeddedt/modernfix/searchtree/SearchTreeProviderRegistry.java b/common/src/main/java/org/embeddedt/modernfix/searchtree/SearchTreeProviderRegistry.java new file mode 100644 index 00000000..f1518faf --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/searchtree/SearchTreeProviderRegistry.java @@ -0,0 +1,34 @@ +package org.embeddedt.modernfix.searchtree; + +import net.minecraft.client.searchtree.RefreshableSearchTree; +import net.minecraft.world.item.ItemStack; +import org.embeddedt.modernfix.core.ModernFixMixinPlugin; + +import java.util.ArrayList; +import java.util.List; + +public class SearchTreeProviderRegistry { + private static final List searchTreeProviders = new ArrayList<>(); + + public static synchronized Provider getSearchTreeProvider() { + for(Provider p : searchTreeProviders) { + if(p.canUse()) + return p; + } + if(ModernFixMixinPlugin.instance.config.getEffectiveOptionForMixin("mixin.perf.blast_search_trees.Registry").isOverridden()) + return DummySearchTree.PROVIDER; + else + return null; + } + + public static synchronized void register(Provider p) { + if(p.canUse()) + searchTreeProviders.add(p); + } + + public interface Provider { + RefreshableSearchTree getSearchTree(boolean tag); + boolean canUse(); + String getName(); + } +} diff --git a/forge/build.gradle b/forge/build.gradle index 1a985834..c9605c69 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -38,11 +38,6 @@ dependencies { modCompileOnly("curse.maven:refinedstorage-243076:${refined_storage_version}") - // compile against the JEI API but do not include it at runtime - modCompileOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}") - - modCompileOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}") - modCompileOnly("curse.maven:jeresources-240630:3951643") modCompileOnly "me.shedaniel:RoughlyEnoughItems-forge:${rei_version}" modCompileOnly("dev.latvian.mods:kubejs-forge:${kubejs_version}") diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/core/BootstrapMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/core/BootstrapMixin.java index 428145b7..f41ccb15 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/core/BootstrapMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/core/BootstrapMixin.java @@ -1,6 +1,7 @@ package org.embeddedt.modernfix.forge.mixin.core; import net.minecraft.server.Bootstrap; +import net.minecraftforge.network.NetworkConstants; import org.slf4j.Logger; import org.embeddedt.modernfix.forge.load.ModWorkManagerQueue; import org.spongepowered.asm.mixin.Final; @@ -23,4 +24,13 @@ public class BootstrapMixin { ModWorkManagerQueue.replace(); } } + + /* for https://github.com/MinecraftForge/MinecraftForge/issues/9505 */ + @Inject(method = "bootStrap", at = @At("RETURN")) + private static void doClassloadHack(CallbackInfo ci) { + if(!isBootstrapped) { + NetworkConstants.init(); + LOGGER.info("Worked around Forge issue #9505"); + } + } } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/blast_search_trees/IngredientFilterInvoker.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/blast_search_trees/IngredientFilterInvoker.java deleted file mode 100644 index ef67771c..00000000 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/blast_search_trees/IngredientFilterInvoker.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.embeddedt.modernfix.forge.mixin.perf.blast_search_trees; - -import mezz.jei.api.ingredients.ITypedIngredient; -import mezz.jei.gui.ingredients.IngredientFilter; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -import java.util.List; - -@Mixin(IngredientFilter.class) -public interface IngredientFilterInvoker { - @Invoker(remap = false) - List> invokeGetIngredientListUncached(String filterText); -} diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/blast_search_trees/MinecraftMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/blast_search_trees/MinecraftMixin.java deleted file mode 100644 index 0b98faae..00000000 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/blast_search_trees/MinecraftMixin.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.embeddedt.modernfix.forge.mixin.perf.blast_search_trees; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.searchtree.SearchRegistry; -import net.minecraftforge.fml.ModContainer; -import net.minecraftforge.fml.ModList; -import org.embeddedt.modernfix.ModernFix; -import org.embeddedt.modernfix.annotation.ClientOnlyMixin; -import org.embeddedt.modernfix.searchtree.DummySearchTree; -import org.embeddedt.modernfix.searchtree.REIBackedSearchTree; -import org.embeddedt.modernfix.forge.searchtree.JEIBackedSearchTree; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -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.Optional; - -@Mixin(Minecraft.class) -@ClientOnlyMixin -public class MinecraftMixin { - @Shadow @Final private SearchRegistry searchRegistry; - - @Inject(method = "createSearchTrees", at = @At("HEAD"), cancellable = true) - private void replaceSearchTrees(CallbackInfo ci) { - ci.cancel(); - Optional jeiContainer = ModList.get().getModContainerById("jei"); - if(ModList.get().isLoaded("roughlyenoughitems")) { - ModernFix.LOGGER.info("Replaced creative search logic with REI"); - this.searchRegistry.register(SearchRegistry.CREATIVE_NAMES, list -> new REIBackedSearchTree(false)); - this.searchRegistry.register(SearchRegistry.CREATIVE_TAGS, list -> new REIBackedSearchTree(true)); - } else if(jeiContainer.isPresent()) { - ModernFix.LOGGER.info("Replaced creative search logic with JEI"); - this.searchRegistry.register(SearchRegistry.CREATIVE_NAMES, list -> new JEIBackedSearchTree(false)); - this.searchRegistry.register(SearchRegistry.CREATIVE_TAGS, list -> new JEIBackedSearchTree(true)); - } else { - ModernFix.LOGGER.info("Replaced creative search logic with dummy implementation"); - this.searchRegistry.register(SearchRegistry.CREATIVE_NAMES, list -> new DummySearchTree<>()); - this.searchRegistry.register(SearchRegistry.CREATIVE_TAGS, list -> new DummySearchTree<>()); - } - this.searchRegistry.register(SearchRegistry.RECIPE_COLLECTIONS, list -> new DummySearchTree<>()); - } -}