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 new file mode 100644 index 00000000..ca1979cb --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/suppress_narrator_stacktrace/GameNarratorMixin.java @@ -0,0 +1,29 @@ +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; +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; + +@ClientOnlyMixin +@Mixin(GameNarrator.class) +public class GameNarratorMixin { + @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 -> original.call(); + }; + } 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/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; + } + } +} 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/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index 5fbe1c36..fc2c7eba 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,25 @@ 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() { + // 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); + 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) { 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 39b28b20..cb993a35 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; @@ -164,7 +168,6 @@ public class ModernFixEarlyConfig { .put("mixin.feature.direct_stack_trace", false) .put("mixin.feature.stalled_chunk_load_detection", 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) @@ -310,6 +313,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."); @@ -399,6 +428,7 @@ public class ModernFixEarlyConfig { LOGGER.warn("Could not write configuration file", e); } + config.readGlobalProperties(); config.readJVMProperties(); } 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 92d5a1f0..968a6afe 100644 --- a/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java +++ b/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java @@ -33,7 +33,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"); 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)." } diff --git a/common/src/main/resources/modernfix.accesswidener b/common/src/main/resources/modernfix.accesswidener index 6ec128cf..3be41a44 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 diff --git a/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/mixin/perf/faster_ingredients/IngredientMixin.java b/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/mixin/perf/faster_ingredients/IngredientMixin.java index ab4be294..d438caf9 100644 --- a/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/mixin/perf/faster_ingredients/IngredientMixin.java +++ b/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/mixin/perf/faster_ingredients/IngredientMixin.java @@ -19,6 +19,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; @@ -189,4 +190,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(); + } }