From 4972081d8a10a7d8d1a84c91e88d92cc2f3e864d Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Thu, 3 Aug 2023 17:04:41 -0400 Subject: [PATCH 1/5] Forcefully inject access transformers from mods even if a load error occurs Related: https://github.com/neoforged/NeoForge/issues/43 --- .../core/config/ModernFixEarlyConfig.java | 1 + .../forge/classloading/ATInjector.java | 36 +++++++++++++++++++ .../forge/ModernFixPlatformHooksImpl.java | 4 +++ 3 files changed, 41 insertions(+) create mode 100644 forge/src/main/java/org/embeddedt/modernfix/forge/classloading/ATInjector.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 b247fbb4..ba4813f8 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 @@ -172,6 +172,7 @@ public class ModernFixEarlyConfig { .put("mixin.devenv", isDevEnv) .put("mixin.perf.remove_spawn_chunks", isDevEnv) .putConditionally(() -> !isFabric, "mixin.bugfix.fix_config_crashes", true) + .putConditionally(() -> !isFabric, "mixin.bugfix.forge_at_inject_error", true) .putConditionally(() -> isFabric, "mixin.perf.clear_fabric_mapping_tables", false) .build(); diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/classloading/ATInjector.java b/forge/src/main/java/org/embeddedt/modernfix/forge/classloading/ATInjector.java new file mode 100644 index 00000000..f86b9623 --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/classloading/ATInjector.java @@ -0,0 +1,36 @@ +package org.embeddedt.modernfix.forge.classloading; + +import net.minecraftforge.fml.loading.FMLLoader; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import net.minecraftforge.fml.loading.moddiscovery.ModValidator; +import net.minecraftforge.fml.util.ObfuscationReflectionHelper; +import org.apache.commons.lang3.tuple.Pair; +import org.embeddedt.modernfix.core.ModernFixMixinPlugin; +import org.embeddedt.modernfix.util.CommonModUtil; + +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; + +public class ATInjector { + public static void injectModATs() { + CommonModUtil.runWithoutCrash(() -> { + ModValidator validator = ObfuscationReflectionHelper.getPrivateValue(FMLLoader.class, null, "modValidator"); + List modFiles = ObfuscationReflectionHelper.getPrivateValue(ModValidator.class, validator, "candidateMods"); + List> list = modFiles.stream() + .filter(file -> file.getAccessTransformer().isPresent()) + .map(file -> Pair.of(file, file.getAccessTransformer().get())) + .collect(Collectors.toList()); + if(list.size() > 0) { + ModernFixMixinPlugin.instance.logger.warn("Applying ATs from {} mods despite being in errored state, this might cause a crash!", list.size()); + for(var pair : list) { + try { + FMLLoader.addAccessTransformer(pair.getRight(), pair.getLeft()); + } catch(RuntimeException e) { + ModernFixMixinPlugin.instance.logger.error("Exception occured applying AT from {}", pair.getLeft().getFileName(), e); + } + } + } + }, "applying mod ATs in errored state"); + } +} 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 index 8a53cfbe..6dde752b 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java +++ b/forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java @@ -24,6 +24,7 @@ import net.minecraftforge.server.ServerLifecycleHooks; import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.api.constants.IntegrationConstants; +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.packet.PacketHandler; @@ -138,6 +139,9 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks { } 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 */ From 1989f122c60a012a5993bb195bf7bc676678ddbe Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Thu, 3 Aug 2023 17:52:50 -0400 Subject: [PATCH 2/5] Remove locking system for Night Config files This can cause deadlocks if mods themselves are also using their own internal locks (Sophisticated Backpacks does this on 1.16 by using a CHM) This system will be replaced by a command/keybind to manually reload configs --- .../modernfix/forge/config/ConfigFixer.java | 23 ++-- .../forge/config/NightConfigFixer.java | 103 ++---------------- 2 files changed, 26 insertions(+), 100 deletions(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/config/ConfigFixer.java b/forge/src/main/java/org/embeddedt/modernfix/forge/config/ConfigFixer.java index eaf9621b..49558094 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/config/ConfigFixer.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/config/ConfigFixer.java @@ -8,6 +8,9 @@ import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; public class ConfigFixer { @@ -35,6 +38,7 @@ public class ConfigFixer { private static class LockingConfigHandler implements Consumer { private final Consumer actualHandler; private final String modId; + private final Lock lock = new ReentrantLock(); LockingConfigHandler(String id, Consumer actualHandler) { this.modId = id; @@ -43,14 +47,17 @@ public class ConfigFixer { @Override public void accept(ModConfig.ModConfigEvent modConfigEvent) { - Object cfgObj = NightConfigFixer.toWriteSyncConfig(modConfigEvent.getConfig().getConfigData()); - if(cfgObj != null) { - // don't synchronize on 'this' as it produces a deadlock when used alongside NightConfigFixer - synchronized (cfgObj) { - this.actualHandler.accept(modConfigEvent); - } - } else { - this.actualHandler.accept(modConfigEvent); + try { + if(lock.tryLock(2, TimeUnit.SECONDS)) { + try { + this.actualHandler.accept(modConfigEvent); + } finally { + lock.unlock(); + } + } else + ModernFix.LOGGER.error("Failed to post config event for {}, someone else is holding the lock", modId); + } catch(InterruptedException e) { + Thread.currentThread().interrupt(); } } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java index a6993a07..c0cc9432 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java @@ -1,29 +1,22 @@ package org.embeddedt.modernfix.forge.config; -import com.electronwill.nightconfig.core.file.CommentedFileConfig; -import com.electronwill.nightconfig.core.file.FileConfig; import com.electronwill.nightconfig.core.file.FileWatcher; import cpw.mods.modlauncher.api.LamdbaExceptionUtils; -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import net.minecraftforge.fml.common.ObfuscationReflectionHelper; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.util.CommonModUtil; import java.lang.reflect.Field; import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.LinkedHashSet; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; -/** - * Relatively simple patch to wait for config saving to finish, made complex by Night Config classes being package-private, - * and Forge not allowing mixins into libraries. - */ public class NightConfigFixer { + public static final LinkedHashSet configsToReload = new LinkedHashSet<>(); public static void monitorFileWatcher() { + if(!ModernFixMixinPlugin.instance.isOptionEnabled("bugfix.fix_config_crashes.NightConfigFixerMixin")) + return; CommonModUtil.runWithoutCrash(() -> { FileWatcher watcher = FileWatcher.defaultInstance(); Field field = FileWatcher.class.getDeclaredField("watchedFiles"); @@ -38,30 +31,6 @@ public class NightConfigFixer { private static final Class WATCHED_FILE = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.FileWatcher$WatchedFile")); private static final Field CHANGE_HANDLER = ObfuscationReflectionHelper.findField(WATCHED_FILE, "changeHandler"); - public static final Class WRITE_SYNC_CONFIG = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.WriteSyncFileConfig")); - private static final Class AUTOSAVE_CONFIG = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.AutosaveCommentedFileConfig")); - private static final Field AUTOSAVE_FILECONFIG = ObfuscationReflectionHelper.findField(AUTOSAVE_CONFIG, "fileConfig"); - - private static final Field CURRENTLY_WRITING = ObfuscationReflectionHelper.findField(WRITE_SYNC_CONFIG, "currentlyWriting"); - - private static final Map, Field> CONFIG_WATCHER_TO_CONFIG_FIELD = Collections.synchronizedMap(new HashMap<>()); - - private static Field getCurrentlyWritingFieldOnWatcher(Object watcher) { - return CONFIG_WATCHER_TO_CONFIG_FIELD.computeIfAbsent(watcher.getClass(), clz -> { - while(clz != null && clz != Object.class) { - for(Field f : clz.getDeclaredFields()) { - if(CommentedFileConfig.class.isAssignableFrom(f.getType())) { - f.setAccessible(true); - ModernFixMixinPlugin.instance.logger.debug("Found CommentedFileConfig: field '{}' on {}", f.getName(), clz.getName()); - return f; - } - } - clz = clz.getSuperclass(); - } - return null; - }); - } - static class MonitoringMap extends ConcurrentHashMap { public MonitoringMap(ConcurrentHashMap oldMap) { super(oldMap); @@ -82,27 +51,6 @@ public class NightConfigFixer { } } - private static final Set> UNKNOWN_FILE_CONFIG_CLASSES = Collections.synchronizedSet(new ReferenceOpenHashSet<>()); - - public static Object toWriteSyncConfig(Object config) { - if(config == null) - return null; - try { - if(WRITE_SYNC_CONFIG.isAssignableFrom(config.getClass())) { - return config; - } else if(AUTOSAVE_CONFIG.isAssignableFrom(config.getClass())) { - FileConfig fc = (FileConfig)AUTOSAVE_FILECONFIG.get(config); - return toWriteSyncConfig(fc); - } else { - if (UNKNOWN_FILE_CONFIG_CLASSES.add(config.getClass())) - ModernFixMixinPlugin.instance.logger.warn("Unexpected FileConfig class: {}", config.getClass().getName()); - return null; - } - } catch(ReflectiveOperationException e) { - return null; - } - } - static class MonitoringConfigTracker implements Runnable { private final Runnable configTracker; @@ -110,47 +58,18 @@ public class NightConfigFixer { this.configTracker = r; } - private void protectFromSaving(FileConfig config, Runnable runnable) throws ReflectiveOperationException { - Object writeSyncConfig = toWriteSyncConfig(config); - if(writeSyncConfig != null) { - // keep trying to write, releasing the config lock each time in case something else needs to lock it - // for any reason - while(true) { - // acquiring synchronized block here should in theory prevent any other concurrent loads/saves, based - // off WriteSyncFileConfig implementation - synchronized (writeSyncConfig) { - if(CURRENTLY_WRITING.getBoolean(writeSyncConfig)) { - ModernFixMixinPlugin.instance.logger.fatal("Config being written during load!!!"); - try { Thread.sleep(500); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } - continue; - } - // at this point, currentlyWriting is false, and we acquired synchronized lock, should be good to - // go - runnable.run(); - break; - } - } - } else { - runnable.run(); - } - } - /** - * This entrypoint runs when the file watcher has detected a change on the config file. Before passing - * this through to Forge, use reflection hacks to confirm the config system is not still writing to the file. - * If it is, spin until writing finishes. Immediately returning might result in the event never being observed. + * Add the config */ @Override public void run() { - try { - Field theField = getCurrentlyWritingFieldOnWatcher(this.configTracker); - if(theField != null) { - CommentedFileConfig cfg = (CommentedFileConfig)theField.get(this.configTracker); - // will synchronize and check saving flag - protectFromSaving(cfg, configTracker); + synchronized(configsToReload) { + int oldSize = configsToReload.size(); + configsToReload.add(configTracker); + if(oldSize == 0) { + ModernFixMixinPlugin.instance.logger.info("Config file change detected on disk. The Forge feature to watch config files for changes is currently disabled due to random corruption issues."); + ModernFixMixinPlugin.instance.logger.info("This functionality will be restored in a future ModernFix update."); } - } catch(ReflectiveOperationException e) { - e.printStackTrace(); } } } From dbff17a1ffd535efb9f7cbe2376f62540f7ab31e Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Thu, 3 Aug 2023 18:08:46 -0400 Subject: [PATCH 3/5] Better fix for config corruption Defer posting of all config reload events to the main thread, and don't process any until after the launch finishes. This should hopefully fix some synchronization issues --- .../forge/config/NightConfigFixer.java | 35 +++++++++++++++---- .../forge/init/ModernFixClientForge.java | 6 ++++ .../modernfix/forge/init/ModernFixForge.java | 11 ++++++ .../forge/ModernFixPlatformHooksImpl.java | 2 ++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java index c0cc9432..833e6eb8 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java @@ -3,17 +3,21 @@ package org.embeddedt.modernfix.forge.config; import com.electronwill.nightconfig.core.file.FileWatcher; import cpw.mods.modlauncher.api.LamdbaExceptionUtils; import net.minecraftforge.fml.common.ObfuscationReflectionHelper; +import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.util.CommonModUtil; import java.lang.reflect.Field; import java.nio.file.Path; +import java.util.ArrayList; import java.util.LinkedHashSet; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; public class NightConfigFixer { public static final LinkedHashSet configsToReload = new LinkedHashSet<>(); + private static int tickCounter = 0; public static void monitorFileWatcher() { if(!ModernFixMixinPlugin.instance.isOptionEnabled("bugfix.fix_config_crashes.NightConfigFixerMixin")) return; @@ -28,6 +32,30 @@ public class NightConfigFixer { }, "replacing Night Config watchedFiles map"); } + /** + * Called by the render thread on the client, and the server thread on the server. Processes all the accumulated + * file watch events. + */ + public static void runReloads() { + if((tickCounter++ % 20) != 0) + return; + List runnablesToRun; + synchronized (configsToReload) { + if(configsToReload.isEmpty()) + return; + runnablesToRun = new ArrayList<>(configsToReload); + configsToReload.clear(); + } + ModernFix.LOGGER.info("Processing {} config reloads", runnablesToRun.size()); + for(Runnable r : runnablesToRun) { + try { + r.run(); + } catch(RuntimeException e) { + e.printStackTrace(); + } + } + } + private static final Class WATCHED_FILE = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.FileWatcher$WatchedFile")); private static final Field CHANGE_HANDLER = ObfuscationReflectionHelper.findField(WATCHED_FILE, "changeHandler"); @@ -59,17 +87,12 @@ public class NightConfigFixer { } /** - * Add the config + * Add the config runnable to the list to be processed by the main thread. */ @Override public void run() { synchronized(configsToReload) { - int oldSize = configsToReload.size(); configsToReload.add(configTracker); - if(oldSize == 0) { - ModernFixMixinPlugin.instance.logger.info("Config file change detected on disk. The Forge feature to watch config files for changes is currently disabled due to random corruption issues."); - ModernFixMixinPlugin.instance.logger.info("This functionality will be restored in a future ModernFix update."); - } } } } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java index 05b901b0..bc361764 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java @@ -4,6 +4,7 @@ import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.DebugScreenOverlay; +import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.RecipesUpdatedEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.gui.ForgeIngameGui; @@ -20,7 +21,9 @@ import net.minecraftforge.fml.common.ObfuscationReflectionHelper; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.server.FMLServerStartedEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.fml.loading.FMLEnvironment; import org.embeddedt.modernfix.ModernFixClient; +import org.embeddedt.modernfix.forge.config.NightConfigFixer; import org.embeddedt.modernfix.screen.ModernFixConfigScreen; import java.util.ArrayList; @@ -51,6 +54,9 @@ public class ModernFixClientForge { if(event.phase == TickEvent.Phase.START && configKey.consumeClick()) { Minecraft.getInstance().setScreen(new ModernFixConfigScreen(Minecraft.getInstance().screen)); } + if(FMLEnvironment.dist == Dist.CLIENT && event.phase == TickEvent.Phase.START && ModernFixForge.launchDone) { + NightConfigFixer.runReloads(); + } } private static final List brandingList = new ArrayList<>(); diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java index 093158e5..72d67ede 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java @@ -6,6 +6,7 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.OnDatapackSyncEvent; import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.*; @@ -15,6 +16,7 @@ import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.server.FMLServerStartedEvent; import net.minecraftforge.fml.event.server.FMLServerStoppedEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.fml.loading.FMLEnvironment; import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.network.FMLNetworkConstants; import net.minecraftforge.fml.server.ServerLifecycleHooks; @@ -27,6 +29,7 @@ import org.embeddedt.modernfix.forge.classloading.ModFileScanDataDeduplicator; import org.embeddedt.modernfix.forge.ModernFixConfig; import org.embeddedt.modernfix.entity.EntityDataIDSyncHandler; import org.embeddedt.modernfix.forge.config.ConfigFixer; +import org.embeddedt.modernfix.forge.config.NightConfigFixer; import org.embeddedt.modernfix.forge.packet.PacketHandler; import org.embeddedt.modernfix.forge.registry.ObjectHolderClearer; import org.embeddedt.modernfix.forge.util.KubeUtil; @@ -36,6 +39,7 @@ import java.util.List; @Mod(ModernFix.MODID) public class ModernFixForge { private static ModernFix commonMod; + public static boolean launchDone = false; public ModernFixForge() { commonMod = new ModernFix(); @@ -54,6 +58,13 @@ public class ModernFixForge { ConfigFixer.replaceConfigHandlers(); } + @SubscribeEvent + public void onServerTick(TickEvent.ServerTickEvent event) { + if(FMLEnvironment.dist == Dist.DEDICATED_SERVER && event.phase == TickEvent.Phase.END && ModernFixForge.launchDone) { + NightConfigFixer.runReloads(); + } + } + @SubscribeEvent public void onDatapackSync(OnDatapackSyncEvent event) { if(event.getPlayer() != null) { 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 index 44565b96..6463a58c 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java +++ b/forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java @@ -30,6 +30,7 @@ import org.embeddedt.modernfix.api.constants.IntegrationConstants; import org.embeddedt.modernfix.forge.classloading.FastAccessTransformerList; import org.embeddedt.modernfix.forge.classloading.ModernFixResourceFinder; import org.embeddedt.modernfix.forge.config.NightConfigFixer; +import org.embeddedt.modernfix.forge.init.ModernFixForge; import org.embeddedt.modernfix.forge.packet.PacketHandler; import org.embeddedt.modernfix.forge.spark.SparkLaunchProfiler; import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; @@ -270,6 +271,7 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks { if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnForge")) { CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.stop("launch"), "Failed to stop profiler"); } + ModernFixForge.launchDone = true; } public String getPlatformName() { From c8749940f73bfd4e17a53b787ce0dceddbce965a Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Thu, 3 Aug 2023 18:09:57 -0400 Subject: [PATCH 4/5] Show log message after reloads are processed --- .../org/embeddedt/modernfix/forge/config/NightConfigFixer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java index 833e6eb8..b503a2a5 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java @@ -46,7 +46,6 @@ public class NightConfigFixer { runnablesToRun = new ArrayList<>(configsToReload); configsToReload.clear(); } - ModernFix.LOGGER.info("Processing {} config reloads", runnablesToRun.size()); for(Runnable r : runnablesToRun) { try { r.run(); @@ -54,6 +53,7 @@ public class NightConfigFixer { e.printStackTrace(); } } + ModernFix.LOGGER.info("Processed {} config reloads", runnablesToRun.size()); } private static final Class WATCHED_FILE = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.FileWatcher$WatchedFile")); From 14170ade1feabd4eee75d788814a5908472a0b80 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Thu, 3 Aug 2023 19:45:14 -0400 Subject: [PATCH 5/5] Implement /mfrc and /mfsrc commands to reload configs on client/server respectively --- .../forge/config/NightConfigFixer.java | 12 +++-------- .../forge/init/ModernFixClientForge.java | 12 ++++++++--- .../modernfix/forge/init/ModernFixForge.java | 21 ++++++++++++------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java index b503a2a5..a471e9ac 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java @@ -3,6 +3,7 @@ package org.embeddedt.modernfix.forge.config; import com.electronwill.nightconfig.core.file.FileWatcher; import cpw.mods.modlauncher.api.LamdbaExceptionUtils; import net.minecraftforge.fml.common.ObfuscationReflectionHelper; +import net.minecraftforge.fml.loading.FMLLoader; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.util.CommonModUtil; @@ -17,7 +18,6 @@ import java.util.function.Function; public class NightConfigFixer { public static final LinkedHashSet configsToReload = new LinkedHashSet<>(); - private static int tickCounter = 0; public static void monitorFileWatcher() { if(!ModernFixMixinPlugin.instance.isOptionEnabled("bugfix.fix_config_crashes.NightConfigFixerMixin")) return; @@ -32,17 +32,9 @@ public class NightConfigFixer { }, "replacing Night Config watchedFiles map"); } - /** - * Called by the render thread on the client, and the server thread on the server. Processes all the accumulated - * file watch events. - */ public static void runReloads() { - if((tickCounter++ % 20) != 0) - return; List runnablesToRun; synchronized (configsToReload) { - if(configsToReload.isEmpty()) - return; runnablesToRun = new ArrayList<>(configsToReload); configsToReload.clear(); } @@ -92,6 +84,8 @@ public class NightConfigFixer { @Override public void run() { synchronized(configsToReload) { + if(configsToReload.size() == 0) + ModernFixMixinPlugin.instance.logger.info("Please use /{} to reload any changed mod config files", FMLLoader.getDist().isDedicatedServer() ? "mfsrc" : "mfrc"); configsToReload.add(configTracker); } } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java index bc361764..60ac7521 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java @@ -4,7 +4,7 @@ import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.DebugScreenOverlay; -import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.ClientChatEvent; import net.minecraftforge.client.event.RecipesUpdatedEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.gui.ForgeIngameGui; @@ -21,7 +21,6 @@ import net.minecraftforge.fml.common.ObfuscationReflectionHelper; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.server.FMLServerStartedEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import net.minecraftforge.fml.loading.FMLEnvironment; import org.embeddedt.modernfix.ModernFixClient; import org.embeddedt.modernfix.forge.config.NightConfigFixer; import org.embeddedt.modernfix.screen.ModernFixConfigScreen; @@ -54,8 +53,15 @@ public class ModernFixClientForge { if(event.phase == TickEvent.Phase.START && configKey.consumeClick()) { Minecraft.getInstance().setScreen(new ModernFixConfigScreen(Minecraft.getInstance().screen)); } - if(FMLEnvironment.dist == Dist.CLIENT && event.phase == TickEvent.Phase.START && ModernFixForge.launchDone) { + } + + @SubscribeEvent(priority = EventPriority.LOW) + public void onClientChat(ClientChatEvent event) { + if(event.getMessage() != null && event.getMessage().trim().equals("/mfrc")) { NightConfigFixer.runReloads(); + event.setCanceled(true); + // add it to chat history + Minecraft.getInstance().gui.getChat().addRecentChat(event.getMessage()); } } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java index 72d67ede..844b9f9f 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java @@ -1,12 +1,14 @@ package org.embeddedt.modernfix.forge.init; import com.google.common.collect.ImmutableList; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; import net.minecraft.world.item.Item; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.OnDatapackSyncEvent; +import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.*; @@ -16,7 +18,6 @@ import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.server.FMLServerStartedEvent; import net.minecraftforge.fml.event.server.FMLServerStoppedEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import net.minecraftforge.fml.loading.FMLEnvironment; import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.network.FMLNetworkConstants; import net.minecraftforge.fml.server.ServerLifecycleHooks; @@ -24,10 +25,10 @@ import net.minecraftforge.registries.ForgeRegistries; import org.apache.commons.lang3.tuple.Pair; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; +import org.embeddedt.modernfix.entity.EntityDataIDSyncHandler; +import org.embeddedt.modernfix.forge.ModernFixConfig; import org.embeddedt.modernfix.forge.classloading.ClassLoadHack; import org.embeddedt.modernfix.forge.classloading.ModFileScanDataDeduplicator; -import org.embeddedt.modernfix.forge.ModernFixConfig; -import org.embeddedt.modernfix.entity.EntityDataIDSyncHandler; import org.embeddedt.modernfix.forge.config.ConfigFixer; import org.embeddedt.modernfix.forge.config.NightConfigFixer; import org.embeddedt.modernfix.forge.packet.PacketHandler; @@ -59,9 +60,15 @@ public class ModernFixForge { } @SubscribeEvent - public void onServerTick(TickEvent.ServerTickEvent event) { - if(FMLEnvironment.dist == Dist.DEDICATED_SERVER && event.phase == TickEvent.Phase.END && ModernFixForge.launchDone) { - NightConfigFixer.runReloads(); + public void onCommandRegister(RegisterCommandsEvent event) { + // Register separate commands since redirecting doesn't work without arguments + for(String name : new String[] { "mfrc", "mfsrc"}) { + event.getDispatcher().register(LiteralArgumentBuilder.literal(name) + .requires(source -> source.hasPermission(3)) + .executes(context -> { + NightConfigFixer.runReloads(); + return 1; + })); } }