From 455b56749cdbb1f02321232a24778c04462fc0b7 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 25 Apr 2025 21:29:46 -0400 Subject: [PATCH 1/4] Document many mixin options --- .../assets/modernfix/lang/en_us.json | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) 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 e18097bc..c7e317c4 100644 --- a/common/src/main/resources/assets/modernfix/lang/en_us.json +++ b/common/src/main/resources/assets/modernfix/lang/en_us.json @@ -100,6 +100,7 @@ "modernfix.option.mixin.perf.deduplicate_wall_shapes": "Makes most wall blocks share the same shape object instead of each one having its own copy. Can reduce memory usage substantially when lots of wall blocks are added by mods.", "modernfix.option.mixin.perf.dynamic_resources.ae2": "AE2 compatibility patch for dynamic resources", "modernfix.option.mixin.perf.dynamic_resources.ctm": "CTM compatibility patch for dynamic resources", + "modernfix.option.mixin.perf.dynamic_resources.ldlib": "LDLib compatibility patch for dynamic resources", "modernfix.option.mixin.perf.dynamic_resources.rs": "Refined Storage compatibility patch for dynamic resources", "modernfix.option.mixin.perf.dynamic_resources.supermartijncore": "SuperMartijn642CoreLib compatibility patch for dynamic resources", "modernfix.option.mixin.perf.dynamic_resources.diagonalfences": "Diagonal Fences compatibility patch for dynamic resources", @@ -135,5 +136,21 @@ "modernfix.option.mixin.perf.forge_cap_retrieval": "Small micro-optimization that makes retrieving custom entity data slightly more efficient on Forge.", "modernfix.option.mixin.perf.forge_registry_lambda": "Fixes oversights in Forge that lead to excessive allocation in hot registry methods.", "modernfix.option.mixin.bugfix.restore_old_dragon_movement": "Fixes MC-272431, which tracks the ender dragon being unable to dive to the portal as it did in 1.13 and older. This causes the dragon to fly quite a bit differently from what modern players are used to and also patches out one-cycling, so it's not enabled by default. Thanks to Jukitsu for identifying the regression in the vanilla code.", - "modernfix.option.mixin.bugfix.missing_block_entities": "Hypixel sends chunks to the client that are missing some block entity data, which makes chests etc. appear invisible. This 'fixes' the problem by creating the needed data on the client. Has no effect for properly behaved servers or in singleplayer." + "modernfix.option.mixin.bugfix.missing_block_entities": "Hypixel sends chunks to the client that are missing some block entity data, which makes chests etc. appear invisible. This 'fixes' the problem by creating the needed data on the client. Has no effect for properly behaved servers or in singleplayer.", + "modernfix.option.mixin.bugfix.buffer_builder_leak": "Attempts to work around mods constructing BufferBuilder objects that leak memory. This can cause the JVM to crash with an hs_err_pid file in some rare cases. If you experience this type of crash, try disabling this option.", + "modernfix.option.mixin.bugfix.extra_experimental_screen": "Fixes the experimental feature warning being shown still being shown the first time you reopen a world that was created as experimental.", + "modernfix.option.mixin.bugfix.forge_at_inject_error": "Fixes a major oversight in Forge's early error handling code that causes some parts of mods to load, but not others, which usually results in the game crashing rather than displaying the intended error screen. This issue was fixed in NeoForge, and eventually fixed in new enough versions of Forge.", + "modernfix.option.mixin.bugfix.world_screen_skipped": "Fixes MC-251068, where deleting the last world takes you back to an empty world list.", + "modernfix.option.mixin.feature.blockentity_incorrect_thread": "**This is a debug option and should not be enabled for regular gameplay.** Attempts to detect mods interacting with block entities on the wrong thread and crash the game with more information, rather than an unclear ConcurrentModificationException", + "modernfix.option.mixin.feature.cause_lag_by_disabling_threads": "Disables the game's use of worker threads for the server and for chunk rendering. **This will make performance worse on almost all hardware**, and should only be enabled on a client, when you know exactly what you are doing, and probably only on hardware with very few (2 or less) physical CPU cores. In those rare cases, it can reduce lag spikes since there is less contention over the limited cores.", + "modernfix.option.mixin.feature.registry_event_progress": "Runs the Forge loading screen on a background thread for part of the loading process, to allow progress to be shown while content is being registered. May have compatibility issues with some GPU drivers (e.g. macOS), so do not enable it by default in a modpack.", + "modernfix.option.mixin.feature.remove_chat_signing": "Prevents the Minecraft client from obtaining the keypair for chat signing. This disables the client's ability to sign chat messages (like No Chat Reports, but in a simpler, albeit less user-friendly way).", + "modernfix.option.mixin.feature.remove_telemetry": "Prevents the Minecraft client from sending telemetry to Mojang. The telemetry is generally not relevant to them anyway for a modded instance.", + "modernfix.option.mixin.perf.chunk_meshing": "Implements some minor optimizations to the vanilla chunk meshing logic (they will have no effect with a mod that replaces chunk rendering entirely).", + "modernfix.option.mixin.perf.faster_structure_location": "Improves the speed at which structures like buried treasure can be located.", + "modernfix.option.mixin.perf.forge_registry_alloc": "Fixes more oversights in Forge that lead to excessive allocation in hot registry methods.", + "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." } From 51c4f3eae8beb0ccc2bfe20f44da9647cd3ffb9c Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 26 Apr 2025 12:06:12 -0400 Subject: [PATCH 2/4] Use smarter iteration order in model bake event registry This heavily improves the rate of cache hits when mods are baking many variants of the same model via iteration over the keys, as now all the variants will be obtained at once rather than being retrieved randomly. Cable Tiers used to take 8 seconds in the event before this change on a Ryzen 7700X, and is now nearly instant. --- .../modernfix/forge/dynresources/ModelBakeEventHelper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java index e0b19a4b..c1a36d27 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java @@ -6,6 +6,7 @@ import com.google.common.collect.Iterators; import com.google.common.collect.Sets; import com.google.common.graph.GraphBuilder; import com.google.common.graph.MutableGraph; +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; import net.minecraft.client.renderer.block.BlockModelShaper; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelBakery; @@ -54,7 +55,7 @@ public class ModelBakeEventHelper { private final MutableGraph dependencyGraph; public ModelBakeEventHelper(Map modelRegistry) { this.modelRegistry = modelRegistry; - this.topLevelModelLocations = new HashSet<>(modelRegistry.keySet()); + this.topLevelModelLocations = new ObjectLinkedOpenHashSet<>(); // Skip going through ModelLocationCache because most of the accesses will be misses ForgeRegistries.BLOCKS.getEntries().forEach(entry -> { var location = entry.getKey().location(); @@ -63,6 +64,7 @@ public class ModelBakeEventHelper { } }); ForgeRegistries.ITEMS.getKeys().forEach(key -> topLevelModelLocations.add(new ModelResourceLocation(key, "inventory"))); + this.topLevelModelLocations.addAll(modelRegistry.keySet()); this.dependencyGraph = GraphBuilder.undirected().build(); ModList.get().forEachModContainer((id, mc) -> { this.dependencyGraph.addNode(id); From 63aeef1ba01ff9a09ecaf125cc72a70a7118935b Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 26 Apr 2025 12:32:11 -0400 Subject: [PATCH 3/4] Add back datapack reload time tracking during world creation --- .../measure_time/WorldLoaderMixin.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/measure_time/WorldLoaderMixin.java diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/measure_time/WorldLoaderMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/measure_time/WorldLoaderMixin.java new file mode 100644 index 00000000..f96448a7 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/measure_time/WorldLoaderMixin.java @@ -0,0 +1,35 @@ +package org.embeddedt.modernfix.common.mixin.feature.measure_time; + +import com.google.common.base.Stopwatch; +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import net.minecraft.server.WorldLoader; +import org.embeddedt.modernfix.ModernFix; +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.CallbackInfoReturnable; + +import java.util.concurrent.CompletableFuture; + +@Mixin(WorldLoader.class) +public class WorldLoaderMixin { + @Inject(method = "load", at = @At("HEAD")) + private static void startStopwatch(CallbackInfoReturnable> cir, @Share("stopwatch") LocalRef stopwatch) { + stopwatch.set(Stopwatch.createStarted()); + } + + @ModifyReturnValue(method = "load", at = @At("RETURN")) + private static CompletableFuture finishStopwatch(CompletableFuture original, @Share("stopwatch") LocalRef stopwatch) { + Stopwatch watch = stopwatch.get(); + if (watch != null) { + return original.whenComplete((o, throwable) -> { + watch.stop(); + ModernFix.LOGGER.warn("Initial datapack load took {}", watch); + }); + } else { + return original; + } + } +} From 8c2c33093b3130c51e0891b8b5ca01ee5bcce95b Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 26 Apr 2025 16:15:24 -0400 Subject: [PATCH 4/4] Build creative mode tab search tree on first use This avoids holding search trees for many creative tabs that will likely never be searched during gameplay. --- .../SearchRegistryMixin.java | 17 ++++++ .../modernfix/searchtree/LazySearchTree.java | 60 +++++++++++++++++++ .../main/resources/modernfix.accesswidener | 3 +- 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/lazy_search_tree_registry/SearchRegistryMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/searchtree/LazySearchTree.java diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/lazy_search_tree_registry/SearchRegistryMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/lazy_search_tree_registry/SearchRegistryMixin.java new file mode 100644 index 00000000..b9ab980f --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/lazy_search_tree_registry/SearchRegistryMixin.java @@ -0,0 +1,17 @@ +package org.embeddedt.modernfix.common.mixin.perf.lazy_search_tree_registry; + +import net.minecraft.client.searchtree.SearchRegistry; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.searchtree.LazySearchTree; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(SearchRegistry.class) +@ClientOnlyMixin +public class SearchRegistryMixin { + @ModifyVariable(method = "register", at = @At("HEAD"), ordinal = 0, argsOnly = true) + private SearchRegistry.TreeBuilderSupplier useLazyBuilder(SearchRegistry.TreeBuilderSupplier supplier) { + return LazySearchTree.decorate(supplier); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/searchtree/LazySearchTree.java b/common/src/main/java/org/embeddedt/modernfix/searchtree/LazySearchTree.java new file mode 100644 index 00000000..af54daa8 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/searchtree/LazySearchTree.java @@ -0,0 +1,60 @@ +package org.embeddedt.modernfix.searchtree; + +import com.google.common.base.Stopwatch; +import net.minecraft.client.searchtree.RefreshableSearchTree; +import net.minecraft.client.searchtree.SearchRegistry; +import org.embeddedt.modernfix.ModernFix; + +import java.util.List; +import java.util.function.Function; + +public class LazySearchTree implements RefreshableSearchTree { + private final List contents; + private final Function, RefreshableSearchTree> treeBuilder; + + private volatile RefreshableSearchTree realTree; + + public LazySearchTree(List contents, Function, RefreshableSearchTree> treeBuilder) { + this.contents = contents; + this.treeBuilder = treeBuilder; + } + + private RefreshableSearchTree getRealTree() { + var t = realTree; + if (t == null) { + synchronized (this) { + t = realTree; + if (t == null) { + ModernFix.LOGGER.info("Building search tree for {} items (this may take a while)...", contents.size()); + Stopwatch s = Stopwatch.createStarted(); + t = this.treeBuilder.apply(contents); + t.refresh(); + s.stop(); + ModernFix.LOGGER.info("Building search tree for {} items took {}", contents.size(), s); + realTree = t; + } + } + } + return t; + } + + @Override + public List search(String query) { + if (query.isEmpty()) { + return this.contents; + } + return getRealTree().search(query); + } + + @Override + public void refresh() { + var t = this.realTree; + if (t != null) { + t.refresh(); + } + } + + public static SearchRegistry.TreeBuilderSupplier decorate(SearchRegistry.TreeBuilderSupplier originalSupplier) { + return list -> new LazySearchTree<>(list, originalSupplier); + } +} diff --git a/common/src/main/resources/modernfix.accesswidener b/common/src/main/resources/modernfix.accesswidener index 225fee71..05d46ae1 100644 --- a/common/src/main/resources/modernfix.accesswidener +++ b/common/src/main/resources/modernfix.accesswidener @@ -63,4 +63,5 @@ accessible field net/minecraft/client/renderer/entity/EnderDragonRenderer$Dragon accessible method net/minecraft/world/level/block/state/StateDefinition appendPropertyCodec (Lcom/mojang/serialization/MapCodec;Ljava/util/function/Supplier;Ljava/lang/String;Lnet/minecraft/world/level/block/state/properties/Property;)Lcom/mojang/serialization/MapCodec; accessible class net/minecraft/world/item/crafting/Ingredient$Value -accessible class net/minecraft/world/item/crafting/Ingredient$ItemValue \ No newline at end of file +accessible class net/minecraft/world/item/crafting/Ingredient$ItemValue +accessible class net/minecraft/client/searchtree/SearchRegistry$TreeEntry \ No newline at end of file