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"; + } +}