From 87c977a3e6e3bfbda45a805642d94ebffb29ce14 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 16 Jul 2025 21:50:00 -0400 Subject: [PATCH 01/12] Patch concurrency issue in ForgeRegistryTagManager --- .../ForgeRegistryTagManagerMixin.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/concurrency/ForgeRegistryTagManagerMixin.java diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/concurrency/ForgeRegistryTagManagerMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/concurrency/ForgeRegistryTagManagerMixin.java new file mode 100644 index 00000000..1367ff67 --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/concurrency/ForgeRegistryTagManagerMixin.java @@ -0,0 +1,31 @@ +package org.embeddedt.modernfix.forge.mixin.bugfix.concurrency; + +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import net.minecraft.tags.TagKey; +import net.minecraftforge.registries.tags.ITag; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Map; + +@Mixin(targets = {"net/minecraftforge/registries/ForgeRegistryTagManager"}) +public class ForgeRegistryTagManagerMixin { + @Shadow private volatile Map, ITag> tags; + + /** + * @author embeddedt (issue found by Uncandango) + * @reason vanilla does not use the correct double-checked locking paradigm, which leads to race conditions + */ + @WrapMethod(method = "getTag", remap = false) + private ITag getTagSafe(@NotNull TagKey name, Operation> original) { + ITag tag = this.tags.get(name); + if (tag == null) { + synchronized (this) { + tag = original.call(name); + } + } + return tag; + } +} From ee6489fb699a059a802a6ac6aaba560eaa821727 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 27 Jul 2025 14:55:34 -0400 Subject: [PATCH 02/12] Make mixin.perf.thread_priorities limit background thread count as well --- .../modernfix/core/ModernFixMixinPlugin.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index 5fbe1c36..73b5eb8d 100644 --- a/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java +++ b/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java @@ -87,9 +87,21 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { t.setDaemon(true); t.start(); } + + if (ModernFixPlatformHooks.INSTANCE.isClient() && ModernFixMixinPlugin.instance.isOptionEnabled("perf.thread_priorities.AdjustThreadCount")) { + computeBetterThreadCount(); + } } } + private void computeBetterThreadCount() { + // Server thread + client thread + GC thread + int reservedCores = 3; + int availableBackgroundCores = Math.max(1, Runtime.getRuntime().availableProcessors() - reservedCores); + logger.info("Configuring Minecraft's max.bg.threads option with {} threads", availableBackgroundCores); + System.setProperty("max.bg.threads", String.valueOf(availableBackgroundCores)); + } + @Override public void onLoad(String mixinPackage) { From 29c1a479a45e28129f9b27511377379fcf31b2f2 Mon Sep 17 00:00:00 2001 From: DerCommander323 <80590706+DerCommander323@users.noreply.github.com> Date: Sat, 2 Aug 2025 00:29:20 +0200 Subject: [PATCH 03/12] Add mixin.feature.suppress_narrator_stacktrace to prevent some log spam on Linux (#590) --- .../GameNarratorMixin.java | 28 +++++++++++++++++++ .../core/config/ModernFixEarlyConfig.java | 1 + .../assets/modernfix/lang/de_de.json | 3 +- .../assets/modernfix/lang/en_us.json | 3 +- 4 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/suppress_narrator_stacktrace/GameNarratorMixin.java diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/suppress_narrator_stacktrace/GameNarratorMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/suppress_narrator_stacktrace/GameNarratorMixin.java new file mode 100644 index 00000000..169ac08a --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/suppress_narrator_stacktrace/GameNarratorMixin.java @@ -0,0 +1,28 @@ +package org.embeddedt.modernfix.common.mixin.feature.suppress_narrator_stacktrace; + +import com.mojang.text2speech.Narrator; +import com.mojang.text2speech.NarratorLinux; +import com.mojang.text2speech.OperatingSystem; +import net.minecraft.client.GameNarrator; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@ClientOnlyMixin +@Mixin(GameNarrator.class) +public class GameNarratorMixin { + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/mojang/text2speech/Narrator;getNarrator()Lcom/mojang/text2speech/Narrator;")) + private Narrator suppressStacktracePrinting() { + try { + return switch (OperatingSystem.get()) { + case LINUX -> new NarratorLinux(); + default -> Narrator.getNarrator(); + }; + } catch (Narrator.InitializeException e) { + ModernFix.LOGGER.warn("Failed to initialize Linux Narrator. Make sure you have libflite installed!"); + return Narrator.EMPTY; + } + } +} 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 c120dcd7..9242109e 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 @@ -187,6 +187,7 @@ public class ModernFixEarlyConfig { .put("mixin.feature.log_stdout_in_log_files", true) .put("mixin.devenv", isDevEnv) .put("mixin.perf.remove_spawn_chunks", isDevEnv) + .put("mixin.feature.suppress_narrator_stacktrace", !isDevEnv) // Not compatible with mixin.devenv .putConditionally(() -> !isFabric, "mixin.bugfix.fix_config_crashes", true) .putConditionally(() -> !isFabric, "mixin.bugfix.forge_at_inject_error", true) .putConditionally(() -> !isFabric, "mixin.feature.registry_event_progress", false) diff --git a/common/src/main/resources/assets/modernfix/lang/de_de.json b/common/src/main/resources/assets/modernfix/lang/de_de.json index a7082b20..0af35633 100644 --- a/common/src/main/resources/assets/modernfix/lang/de_de.json +++ b/common/src/main/resources/assets/modernfix/lang/de_de.json @@ -120,5 +120,6 @@ "modernfix.option.mixin.perf.compact_mojang_registries": "(Fabric) Experimentelle Option, die die Speichernutzung von Registern um etwa 50 % reduziert. In den meisten Modpacks nicht nützlich, es sei denn, sie enthalten Millionen von Blöcken und Gegenständen.", "modernfix.option.mixin.perf.dynamic_block_codecs": "Vermeidet das Speichern eines Codecs für jeden Block (Zustand) und generiert und speichert ihn stattdessen bei Bedarf im laufenden Betrieb. Im Allgemeinen lohnt es sich nicht, es zu aktivieren, es sei denn, Sie haben eine Million Blöcke/Elemente.", "modernfix.option.mixin.perf.faster_command_suggestions": "Verringern Sie Verzögerungen, wenn beim Eingeben eines Befehls Hunderttausende Vorschläge eingehen", - "modernfix.option.mixin.perf.mojang_registry_size": "Behebt ein Problem, das dazu führt, dass sich die Registrierung von Blöcken/Elementen proportional zur bereits registrierten Anzahl verlangsamt. Dies verbessert die Startzeit." + "modernfix.option.mixin.perf.mojang_registry_size": "Behebt ein Problem, das dazu führt, dass sich die Registrierung von Blöcken/Elementen proportional zur bereits registrierten Anzahl verlangsamt. Dies verbessert die Startzeit.", + "modernfix.option.mixin.feature.suppress_narrator_stacktrace": "Hält das Spiel davon ab, einen sehr langen Stacktrace zu loggen, wenn der Erzähler auf Linux nicht erfolgreich lädt (oft verursacht dadurch, dass libflite nicht installiert ist)." } diff --git a/common/src/main/resources/assets/modernfix/lang/en_us.json b/common/src/main/resources/assets/modernfix/lang/en_us.json index 918d98ed..afc66191 100644 --- a/common/src/main/resources/assets/modernfix/lang/en_us.json +++ b/common/src/main/resources/assets/modernfix/lang/en_us.json @@ -152,5 +152,6 @@ "modernfix.option.mixin.perf.memoize_creative_tab_build": "Improves on vanilla's existing caching for creative tab contents in a way that is compatible with the timing requirements of mods like JEI/EMI. This can reduce the lag spike when opening the creative inventory for the first time in a modpack.", "modernfix.option.mixin.perf.potential_spawns_alloc": "Optimizes the Forge event for finding potential mobs that can spawn. This reduces allocations and the overhead of rebuilding a weighted list when no mods modify the potential spawns.", "modernfix.option.mixin.perf.ticking_chunk_alloc": "Optimizes chunk ticking in vanilla to reduce allocations.", - "modernfix.option.mixin.perf.worldgen_allocation": "Optimizes some world generation logic in vanilla to reduce object allocations." + "modernfix.option.mixin.perf.worldgen_allocation": "Optimizes some world generation logic in vanilla to reduce object allocations.", + "modernfix.option.mixin.feature.suppress_narrator_stacktrace": "Prevents the game from logging a very long stacktrace when the narrator fails to load on Linux (usually caused by not having libflite installed)." } From 8d4a7c337469f3b48d8745947700cf5acd7f5a6d Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 22 Jul 2025 16:49:47 -0400 Subject: [PATCH 04/12] Mark Spark worker as daemon thread --- .../java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java b/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java index 2bff5d0c..dacd6385 100644 --- a/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java +++ b/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java @@ -34,7 +34,7 @@ public class SparkLaunchProfiler { private static PlatformInfo platformInfo = new ModernFixPlatformInfo(); private static CommandSender commandSender = new ModernFixCommandSender(); private static Map ongoingSamplers = new Object2ReferenceOpenHashMap<>(); - private static ExecutorService executor = Executors.newSingleThreadScheduledExecutor((new ThreadFactoryBuilder()).setNameFormat("spark-modernfix-async-worker").build()); + private static ExecutorService executor = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setDaemon(true).setNameFormat("spark-modernfix-async-worker").build()); private static final SparkPlatform platform = new SparkPlatform(new ModernFixSparkPlugin()); private static final boolean USE_JAVA_SAMPLER_FOR_LAUNCH = !Boolean.getBoolean("modernfix.profileWithAsyncSampler"); From 44322a7d07fa77da1c6e18b51b8c1bc60c67bac8 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 1 Aug 2025 18:40:40 -0400 Subject: [PATCH 05/12] Tweak narrator mixin --- .../common/mixin/devenv/NarratorMixin.java | 17 ----------------- .../GameNarratorMixin.java | 9 +++++---- .../core/config/ModernFixEarlyConfig.java | 1 - 3 files changed, 5 insertions(+), 22 deletions(-) delete mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/devenv/NarratorMixin.java diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/devenv/NarratorMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/devenv/NarratorMixin.java deleted file mode 100644 index c9a7a74a..00000000 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/devenv/NarratorMixin.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.embeddedt.modernfix.common.mixin.devenv; - -import com.mojang.text2speech.Narrator; -import net.minecraft.client.GameNarrator; -import org.embeddedt.modernfix.annotation.ClientOnlyMixin; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(GameNarrator.class) -@ClientOnlyMixin -public class NarratorMixin { - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/mojang/text2speech/Narrator;getNarrator()Lcom/mojang/text2speech/Narrator;", remap = false)) - private Narrator useDummyNarrator() { - return Narrator.EMPTY; - } -} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/suppress_narrator_stacktrace/GameNarratorMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/suppress_narrator_stacktrace/GameNarratorMixin.java index 169ac08a..ca1979cb 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/suppress_narrator_stacktrace/GameNarratorMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/suppress_narrator_stacktrace/GameNarratorMixin.java @@ -1,5 +1,7 @@ package org.embeddedt.modernfix.common.mixin.feature.suppress_narrator_stacktrace; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.mojang.text2speech.Narrator; import com.mojang.text2speech.NarratorLinux; import com.mojang.text2speech.OperatingSystem; @@ -8,17 +10,16 @@ import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.annotation.ClientOnlyMixin; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; @ClientOnlyMixin @Mixin(GameNarrator.class) public class GameNarratorMixin { - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/mojang/text2speech/Narrator;getNarrator()Lcom/mojang/text2speech/Narrator;")) - private Narrator suppressStacktracePrinting() { + @WrapOperation(method = "", at = @At(value = "INVOKE", target = "Lcom/mojang/text2speech/Narrator;getNarrator()Lcom/mojang/text2speech/Narrator;")) + private Narrator suppressStacktracePrinting(Operation original) { try { return switch (OperatingSystem.get()) { case LINUX -> new NarratorLinux(); - default -> Narrator.getNarrator(); + default -> original.call(); }; } catch (Narrator.InitializeException e) { ModernFix.LOGGER.warn("Failed to initialize Linux Narrator. Make sure you have libflite installed!"); 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 9242109e..c120dcd7 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 @@ -187,7 +187,6 @@ public class ModernFixEarlyConfig { .put("mixin.feature.log_stdout_in_log_files", true) .put("mixin.devenv", isDevEnv) .put("mixin.perf.remove_spawn_chunks", isDevEnv) - .put("mixin.feature.suppress_narrator_stacktrace", !isDevEnv) // Not compatible with mixin.devenv .putConditionally(() -> !isFabric, "mixin.bugfix.fix_config_crashes", true) .putConditionally(() -> !isFabric, "mixin.bugfix.forge_at_inject_error", true) .putConditionally(() -> !isFabric, "mixin.feature.registry_event_progress", false) From 12a0414f61f19826adff7ca6a02c2b9a185bb96b Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 1 Aug 2025 18:41:55 -0400 Subject: [PATCH 06/12] Do not override user-specified `max.bg.threads` --- .../org/embeddedt/modernfix/core/ModernFixMixinPlugin.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index 73b5eb8d..fc2c7eba 100644 --- a/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java +++ b/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java @@ -95,6 +95,10 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { } private void computeBetterThreadCount() { + // Allow user-provided thread count to take precedence + if (System.getProperty("max.bg.threads") != null) { + return; + } // Server thread + client thread + GC thread int reservedCores = 3; int availableBackgroundCores = Math.max(1, Runtime.getRuntime().availableProcessors() - reservedCores); From e30a7fccf2dfdaf9c6850863bfc6b702360e3a8d Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 1 Aug 2025 18:59:13 -0400 Subject: [PATCH 07/12] Add global properties file Not populated by default. User must create `global/modernfix-global-mixins.properties` in the standard global .minecraft folder for their OS --- .../core/config/ModernFixEarlyConfig.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) 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 c120dcd7..28189681 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 @@ -5,6 +5,7 @@ import com.google.common.collect.*; import com.google.gson.*; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.embeddedt.modernfix.annotation.ClientOnlyMixin; @@ -21,6 +22,9 @@ import org.spongepowered.asm.mixin.Mixin; import java.io.*; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; import java.util.function.BooleanSupplier; import java.util.regex.Pattern; @@ -313,6 +317,32 @@ public class ModernFixEarlyConfig { } } + private void readGlobalProperties() { + Path minecraftFolder; + if (SystemUtils.IS_OS_MAC) { + minecraftFolder = Paths.get(System.getProperty("user.home"), "Library", "Application Support", "minecraft"); + } else if (SystemUtils.IS_OS_WINDOWS) { + minecraftFolder = Paths.get(System.getenv("APPDATA"), ".minecraft"); + } else { + minecraftFolder = Paths.get(System.getProperty("user.home"), ".minecraft"); + } + Path globalPropsFile = minecraftFolder.resolve("global").resolve("modernfix-global-mixins.properties"); + try { + if (Files.exists(globalPropsFile)) { + Properties properties = new Properties(); + try (var is = Files.newInputStream(globalPropsFile)) { + properties.load(is); + } + if (!properties.isEmpty()) { + LOGGER.info("Global properties specified: [{}]", properties.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(", "))); + readProperties(properties); + } + } + } catch (Exception e) { + LOGGER.error("Error reading global properties file", e); + } + } + private void readProperties(Properties props) { if(ALLOW_OVERRIDE_OVERRIDES) LOGGER.fatal("JVM argument given to override mod overrides. Issues opened with this option present will be ignored unless they can be reproduced without."); @@ -402,6 +432,7 @@ public class ModernFixEarlyConfig { LOGGER.warn("Could not write configuration file", e); } + config.readGlobalProperties(); config.readJVMProperties(); } From e411f11c0cc69f7bdb1c9ecf854c706ccb2f3fae Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 2 Aug 2025 15:29:41 -0400 Subject: [PATCH 08/12] Implement Forge ingredient invalidation correctly --- .../mixin/perf/faster_ingredients/IngredientMixin.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/faster_ingredients/IngredientMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/faster_ingredients/IngredientMixin.java index 2840e2f1..eea9aa02 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/faster_ingredients/IngredientMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/faster_ingredients/IngredientMixin.java @@ -17,6 +17,7 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.ArrayList; @@ -169,4 +170,9 @@ public abstract class IngredientMixin implements ExtendedIngredient { public void mfix$clearReference() { this.mfix$cachedItemStacks = null; } + + @Inject(method = "invalidate", at = @At("RETURN"), remap = false) + private void invalidateSoftReference(CallbackInfo ci) { + mfix$clearReference(); + } } From b30b319214c633d6caf84fa8f19d1138cd8878cc Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 9 Aug 2025 20:43:49 -0400 Subject: [PATCH 09/12] Deduplicate ResourcefulLib Highlight objects --- forge/build.gradle | 1 + .../HighlightHandlerMixin.java | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcefullib_highlight_deduplication/HighlightHandlerMixin.java diff --git a/forge/build.gradle b/forge/build.gradle index d304ab3d..5344f33d 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -61,6 +61,7 @@ dependencies { modCompileOnly("curse.maven:supermartijncore-454372:4455391") modCompileOnly("vazkii.patchouli:Patchouli:1.19.2-77") modCompileOnly("curse.maven:cofhcore-69162:5374122") + modCompileOnly("curse.maven:resourcefullib-570073:5659871") // runtime remapping at home for (extraModJar in fileTree(dir: extraModsDir, include: '*.jar')) { diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcefullib_highlight_deduplication/HighlightHandlerMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcefullib_highlight_deduplication/HighlightHandlerMixin.java new file mode 100644 index 00000000..157e694d --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcefullib_highlight_deduplication/HighlightHandlerMixin.java @@ -0,0 +1,69 @@ +package org.embeddedt.modernfix.forge.mixin.perf.resourcefullib_highlight_deduplication; + +import com.teamresourceful.resourcefullib.client.highlights.HighlightHandler; +import com.teamresourceful.resourcefullib.client.highlights.base.Highlight; +import com.teamresourceful.resourcefullib.client.highlights.base.HighlightLine; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.world.level.block.state.BlockState; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.annotation.RequiresMod; +import org.joml.Vector3f; +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; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +@Mixin(HighlightHandler.class) +@RequiresMod("resourcefullib") +@ClientOnlyMixin +public class HighlightHandlerMixin { + @Inject(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V", at = @At("RETURN"), remap = false) + private void deduplicateHighlights(CallbackInfo ci) { + Map stateCache; + + try { + var stateCacheMap = this.getClass().getDeclaredField("STATE_CACHE"); + if (stateCacheMap.get(null) instanceof HashMap hashMap) { + stateCache = (Map)hashMap; + } else { + throw new ReflectiveOperationException("Unexpected map type"); + } + } catch (ReflectiveOperationException e) { + ModernFix.LOGGER.error("Not applying Resourceful Lib patch due to reflection error", e); + return; + } + + ObjectOpenHashSet pointCache = new ObjectOpenHashSet<>(); + ObjectOpenHashSet lineCache = new ObjectOpenHashSet<>(); + ObjectOpenHashSet> listCache = new ObjectOpenHashSet<>(); + Function deduplicator = l -> { + if (!lineCache.contains(l)) { + l = new HighlightLine( + pointCache.addOrGet(l.start()), + pointCache.addOrGet(l.end()), + pointCache.addOrGet(l.normal()) + ); + } + return lineCache.addOrGet(l); + }; + + stateCache.replaceAll((rl, highlight) -> { + if (highlight == null) { + return null; + } + List newList = highlight.lines(); + if (!listCache.contains(newList)) { + newList = newList.stream().map(deduplicator).toList(); + } + return new Highlight(highlight.id(), listCache.addOrGet(newList)); + }); + + ModernFix.LOGGER.info("Deduplicated ResourcefulLib highlights ({} points, {} lines)", pointCache.size(), lineCache.size()); + } +} From f71277eb6406ca74453875573fd68e9b1df15e7c Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 9 Aug 2025 20:44:08 -0400 Subject: [PATCH 10/12] Memoize `VanillaRegistres.createLookup` --- .../VanillaRegistriesMixin.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/VanillaRegistriesMixin.java diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/VanillaRegistriesMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/VanillaRegistriesMixin.java new file mode 100644 index 00000000..97fdcc91 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/VanillaRegistriesMixin.java @@ -0,0 +1,22 @@ +package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries; + +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import net.minecraft.core.HolderLookup; +import net.minecraft.data.registries.VanillaRegistries; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(VanillaRegistries.class) +public class VanillaRegistriesMixin { + private static HolderLookup.Provider STATIC_PROVIDER; + + @WrapMethod(method = "createLookup") + private static HolderLookup.Provider modernfix$memoizeLookup(Operation original) { + synchronized (VanillaRegistries.class) { + if (STATIC_PROVIDER == null) { + STATIC_PROVIDER = original.call(); + } + return STATIC_PROVIDER; + } + } +} From 25976f3b870313502afe20913478090b9faf2f89 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 22 Aug 2025 20:23:07 -0400 Subject: [PATCH 11/12] Disable LazyYCondition logic in situations where it has no effect This saves roughly 10% of time in surface rule evaluation in some tests. Closes #585 Co-authored-by: Voidsong Dragonfly --- .../SurfaceRulesMixin.java | 30 +++++++++++++++++++ .../main/resources/modernfix.accesswidener | 3 ++ 2 files changed, 33 insertions(+) create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/worldgen_allocation/SurfaceRulesMixin.java diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/worldgen_allocation/SurfaceRulesMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/worldgen_allocation/SurfaceRulesMixin.java new file mode 100644 index 00000000..4635a560 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/worldgen_allocation/SurfaceRulesMixin.java @@ -0,0 +1,30 @@ +package org.embeddedt.modernfix.common.mixin.perf.worldgen_allocation; + +import net.minecraft.world.level.levelgen.SurfaceRules; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(targets = { + "net/minecraft/world/level/levelgen/SurfaceRules$BiomeConditionSource$1BiomeCondition", + "net/minecraft/world/level/levelgen/SurfaceRules$StoneDepthCheck$1StoneDepthCondition", + "net/minecraft/world/level/levelgen/SurfaceRules$VerticalGradientConditionSource$1VerticalGradientCondition", + "net/minecraft/world/level/levelgen/SurfaceRules$WaterConditionSource$1WaterCondition", + "net/minecraft/world/level/levelgen/SurfaceRules$YConditionSource$1YCondition", +}) +public abstract class SurfaceRulesMixin extends SurfaceRules.LazyCondition { + protected SurfaceRulesMixin(SurfaceRules.Context context) { + super(context); + } + + /** + * @author VoidsongDragonfly + * @reason Replacing Vanilla's use of {@link SurfaceRules.LazyYCondition LazyYCondition} that causes performance + * detriments due to unused caching behavior. The `lastUpdateY` field is updated every time the block position + * changes (making the cache useful only within a single block), and the targeted condition objects are not interned + * (meaning there is no caching happening anyway, as each instance uses its own cache). + * + */ + @Override + public boolean test() { + return compute(); + } +} diff --git a/common/src/main/resources/modernfix.accesswidener b/common/src/main/resources/modernfix.accesswidener index 57322acc..3cd36b76 100644 --- a/common/src/main/resources/modernfix.accesswidener +++ b/common/src/main/resources/modernfix.accesswidener @@ -11,8 +11,11 @@ accessible field net/minecraft/world/level/Level blockEntityTickers Ljava/util/L accessible class net/minecraft/client/renderer/RenderType$CompositeRenderType accessible method net/minecraft/nbt/CompoundTag (Ljava/util/Map;)V +accessible class net/minecraft/world/level/levelgen/SurfaceRules$Condition +accessible class net/minecraft/world/level/levelgen/SurfaceRules$LazyCondition accessible class net/minecraft/world/level/levelgen/SurfaceRules$SequenceRule accessible class net/minecraft/world/level/levelgen/SurfaceRules$SurfaceRule +accessible class net/minecraft/world/level/levelgen/SurfaceRules$Context accessible class net/minecraft/world/level/levelgen/DensityFunctions$Marker accessible class net/minecraft/world/level/levelgen/DensityFunctions$Marker$Type accessible method net/minecraft/world/level/levelgen/DensityFunctions$Marker (Lnet/minecraft/world/level/levelgen/DensityFunctions$Marker$Type;Lnet/minecraft/world/level/levelgen/DensityFunction;)V From 8133198cc2a82664928ad062ed18e61f09b0e68f Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 22 Aug 2025 20:24:10 -0400 Subject: [PATCH 12/12] Enable worldgen_allocation by default --- .../embeddedt/modernfix/core/config/ModernFixEarlyConfig.java | 1 - 1 file changed, 1 deletion(-) 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 28189681..853ba473 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 @@ -169,7 +169,6 @@ public class ModernFixEarlyConfig { .put("mixin.feature.stalled_chunk_load_detection", false) .put("mixin.perf.blast_search_trees.force", false) .put("mixin.bugfix.restore_old_dragon_movement", false) - .put("mixin.perf.worldgen_allocation", false) // experimental .put("mixin.feature.cause_lag_by_disabling_threads", false) .put("mixin.bugfix.missing_block_entities", false) .put("mixin.feature.blockentity_incorrect_thread", false)