From 11f18cd739644fd72e8598efffd8cd39ed2389d1 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 29 Apr 2025 11:14:26 -0400 Subject: [PATCH] Enable use of FML's unused TracingPrintStream for tracking mod messages on System.out We implement this ourselves because we need it on 1.20.1 anyway, it will eventually be implemented upstream in NeoForge, at which point this feature will be removed: https://github.com/neoforged/FancyModLoader/issues/277 --- .../core/config/ModernFixEarlyConfig.java | 1 + .../forge/ModernFixPlatformHooksImpl.java | 183 ++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java 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 01fe319b..1b78d074 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 @@ -181,6 +181,7 @@ public class ModernFixEarlyConfig { .put("mixin.feature.snapshot_easter_egg", true) .put("mixin.feature.warn_missing_perf_mods", true) .put("mixin.feature.spark_profile_launch", false) + .put("mixin.feature.log_stdout_in_log_files", true) .put("mixin.devenv", isDevEnv) .putConditionally(() -> !isFabric, "mixin.bugfix.fix_config_crashes", true) .putConditionally(() -> !isFabric, "mixin.feature.registry_event_progress", false) diff --git a/forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java b/forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java new file mode 100644 index 00000000..dbf8edf9 --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java @@ -0,0 +1,183 @@ +package org.embeddedt.modernfix.platform.forge; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.client.searchtree.SearchRegistry; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.CreativeModeTabSearchRegistry; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.fml.ModLoader; +import net.minecraftforge.fml.loading.FMLLoader; +import net.minecraftforge.fml.loading.FMLPaths; +import net.minecraftforge.fml.loading.LoadingModList; +import net.minecraftforge.fml.loading.TracingPrintStream; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.network.PacketDistributor; +import net.minecraftforge.server.ServerLifecycleHooks; +import org.embeddedt.modernfix.api.constants.IntegrationConstants; +import org.embeddedt.modernfix.core.ModernFixMixinPlugin; +import org.embeddedt.modernfix.forge.classloading.ATInjector; +import org.embeddedt.modernfix.forge.classloading.FastAccessTransformerList; +import org.embeddedt.modernfix.forge.config.NightConfigFixer; +import org.embeddedt.modernfix.forge.config.NightConfigWatchThrottler; +import org.embeddedt.modernfix.forge.init.ModernFixForge; +import org.embeddedt.modernfix.forge.packet.PacketHandler; +import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; +import org.embeddedt.modernfix.spark.SparkLaunchProfiler; +import org.embeddedt.modernfix.util.CommonModUtil; +import org.embeddedt.modernfix.util.DummyList; +import org.objectweb.asm.tree.ClassNode; +import org.slf4j.LoggerFactory; +import org.spongepowered.asm.mixin.injection.struct.InjectorGroupInfo; + +import java.lang.reflect.Field; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks { + public boolean isClient() { + return FMLLoader.getDist() == Dist.CLIENT; + } + + public boolean isDedicatedServer() { + return FMLLoader.getDist().isDedicatedServer(); + } + + private static final String verString = Optional.ofNullable( + ModernFixMixinPlugin.class.getPackage().getImplementationVersion()) + .orElse("[unknown]"); + + public String getVersionString() { + return verString; + } + + public boolean modPresent(String modId) { + return FMLLoader.getLoadingModList().getModFileById(modId) != null; + } + + public boolean isDevEnv() { + return !FMLLoader.isProduction(); + } + + public MinecraftServer getCurrentServer() { + return ServerLifecycleHooks.getCurrentServer(); + } + + public boolean isEarlyLoadingNormally() { + return LoadingModList.get().getErrors().isEmpty(); + } + + public boolean isLoadingNormally() { + return isEarlyLoadingNormally() && ModLoader.isLoadingStateValid(); + } + + public Path getGameDirectory() { + return FMLPaths.GAMEDIR.get(); + } + + public void sendPacket(ServerPlayer player, Object packet) { + PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), packet); + } + + public void injectPlatformSpecificHacks() { + if(!isEarlyLoadingNormally() && ModernFixMixinPlugin.instance.isOptionEnabled("bugfix.forge_at_inject_error.ATInjector")) { + ATInjector.injectModATs(); + } + FastAccessTransformerList.attemptReplace(); + + /* https://github.com/FabricMC/Mixin/pull/99 */ + try { + Field groupMembersField = InjectorGroupInfo.class.getDeclaredField("members"); + groupMembersField.setAccessible(true); + Field noGroupField = InjectorGroupInfo.Map.class.getDeclaredField("NO_GROUP"); + noGroupField.setAccessible(true); + InjectorGroupInfo noGroup = (InjectorGroupInfo)noGroupField.get(null); + groupMembersField.set(noGroup, new DummyList<>()); + } catch(NoSuchFieldException ignored) { + // Connector will replace FML's mixin with one which already has the fix, don't bother logging + } catch(RuntimeException | ReflectiveOperationException e) { + ModernFixMixinPlugin.instance.logger.error("Failed to patch mixin memory leak", e); + } + + if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnForge")) { + CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.start("launch"), "Failed to start profiler"); + } + + if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.log_stdout_in_log_files.PrintStreamReplacement")) { + System.setOut(new TracingPrintStream(LoggerFactory.getLogger("STDOUT"), System.out)); + System.setErr(new TracingPrintStream(LoggerFactory.getLogger("STDERR"), System.err)); + } + + NightConfigFixer.monitorFileWatcher(); + NightConfigWatchThrottler.throttle(); + } + + public void applyASMTransformers(String mixinClassName, ClassNode targetClass) { + + } + + public void onServerCommandRegister(Consumer> handler) { + MinecraftForge.EVENT_BUS.addListener((RegisterCommandsEvent event) -> { + handler.accept(event.getDispatcher()); + }); + } + + private static Multimap modOptions; + public Multimap getCustomModOptions() { + if(modOptions == null) { + modOptions = ArrayListMultimap.create(); + for (ModInfo meta : LoadingModList.get().getMods()) { + meta.getConfigElement(IntegrationConstants.INTEGRATIONS_KEY).ifPresent(optionsObj -> { + if(optionsObj instanceof Map) { + Map options = (Map)optionsObj; + options.forEach((key, value) -> { + if(key instanceof String && value instanceof String) { + modOptions.put((String)key, (String)value); + } + }); + } + }); + } + } + return modOptions; + } + + public void registerCreativeSearchTrees(SearchRegistry registry, SearchRegistry.TreeBuilderSupplier nameSupplier, SearchRegistry.TreeBuilderSupplier tagSupplier, BiConsumer, List> populator) { + for (SearchRegistry.Key nameKey : CreativeModeTabSearchRegistry.getNameSearchKeys().values()) { + registry.register(nameKey, nameSupplier); + } + for (SearchRegistry.Key tagKey : CreativeModeTabSearchRegistry.getTagSearchKeys().values()) { + registry.register(tagKey, tagSupplier); + } + Map> tagSearchKeys = CreativeModeTabSearchRegistry.getTagSearchKeys(); + CreativeModeTabSearchRegistry.getNameSearchKeys().forEach((tab, nameSearchKey) -> { + SearchRegistry.Key tagSearchKey = tagSearchKeys.get(tab); + tab.setSearchTreeBuilder((contents) -> { + populator.accept(nameSearchKey, contents); + populator.accept(tagSearchKey, contents); + }); + }); + } + + public void onLaunchComplete() { + if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnForge")) { + CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.stop("launch"), "Failed to stop profiler"); + } + ModernFixForge.launchDone = true; + } + + public String getPlatformName() { + return "Forge"; + } +}