11package org .embeddedt .modernfix .forge .config ;
22
3- import com .electronwill .nightconfig .core .file .CommentedFileConfig ;
4- import com .electronwill .nightconfig .core .file .FileConfig ;
53import com .electronwill .nightconfig .core .file .FileWatcher ;
64import cpw .mods .modlauncher .api .LamdbaExceptionUtils ;
7- import it . unimi . dsi . fastutil . objects . ReferenceOpenHashSet ;
5+ import net . minecraftforge . fml . loading . FMLLoader ;
86import net .minecraftforge .fml .util .ObfuscationReflectionHelper ;
7+ import org .embeddedt .modernfix .ModernFix ;
98import org .embeddedt .modernfix .core .ModernFixMixinPlugin ;
109import org .embeddedt .modernfix .util .CommonModUtil ;
1110
1211import java .lang .reflect .Field ;
1312import java .nio .file .Path ;
14- import java .util .Collections ;
15- import java .util .HashMap ;
16- import java .util .Map ;
17- import java .util .Set ;
13+ import java .util .ArrayList ;
14+ import java .util .LinkedHashSet ;
15+ import java .util .List ;
1816import java .util .concurrent .ConcurrentHashMap ;
1917import java .util .function .Function ;
2018
21- /**
22- * Relatively simple patch to wait for config saving to finish, made complex by Night Config classes being package-private,
23- * and Forge not allowing mixins into libraries.
24- */
2519public class NightConfigFixer {
20+ public static final LinkedHashSet <Runnable > configsToReload = new LinkedHashSet <>();
2621 public static void monitorFileWatcher () {
22+ if (!ModernFixMixinPlugin .instance .isOptionEnabled ("bugfix.fix_config_crashes.NightConfigFixerMixin" ))
23+ return ;
2724 CommonModUtil .runWithoutCrash (() -> {
2825 FileWatcher watcher = FileWatcher .defaultInstance ();
2926 Field field = FileWatcher .class .getDeclaredField ("watchedFiles" );
@@ -35,33 +32,25 @@ public static void monitorFileWatcher() {
3532 }, "replacing Night Config watchedFiles map" );
3633 }
3734
38- private static final Class <?> WATCHED_FILE = LamdbaExceptionUtils .uncheck (() -> Class .forName ("com.electronwill.nightconfig.core.file.FileWatcher$WatchedFile" ));
39- private static final Field CHANGE_HANDLER = ObfuscationReflectionHelper .findField (WATCHED_FILE , "changeHandler" );
40-
41- public static final Class <?> WRITE_SYNC_CONFIG = LamdbaExceptionUtils .uncheck (() -> Class .forName ("com.electronwill.nightconfig.core.file.WriteSyncFileConfig" ));
42- private static final Class <?> AUTOSAVE_CONFIG = LamdbaExceptionUtils .uncheck (() -> Class .forName ("com.electronwill.nightconfig.core.file.AutosaveCommentedFileConfig" ));
43- private static final Field AUTOSAVE_FILECONFIG = ObfuscationReflectionHelper .findField (AUTOSAVE_CONFIG , "fileConfig" );
44-
45- private static final Field CURRENTLY_WRITING = ObfuscationReflectionHelper .findField (WRITE_SYNC_CONFIG , "currentlyWriting" );
46-
47- private static final Map <Class <?>, Field > CONFIG_WATCHER_TO_CONFIG_FIELD = Collections .synchronizedMap (new HashMap <>());
48-
49- private static Field getCurrentlyWritingFieldOnWatcher (Object watcher ) {
50- return CONFIG_WATCHER_TO_CONFIG_FIELD .computeIfAbsent (watcher .getClass (), clz -> {
51- while (clz != null && clz != Object .class ) {
52- for (Field f : clz .getDeclaredFields ()) {
53- if (CommentedFileConfig .class .isAssignableFrom (f .getType ())) {
54- f .setAccessible (true );
55- ModernFixMixinPlugin .instance .logger .debug ("Found CommentedFileConfig: field '{}' on {}" , f .getName (), clz .getName ());
56- return f ;
57- }
58- }
59- clz = clz .getSuperclass ();
35+ public static void runReloads () {
36+ List <Runnable > runnablesToRun ;
37+ synchronized (configsToReload ) {
38+ runnablesToRun = new ArrayList <>(configsToReload );
39+ configsToReload .clear ();
40+ }
41+ for (Runnable r : runnablesToRun ) {
42+ try {
43+ r .run ();
44+ } catch (RuntimeException e ) {
45+ e .printStackTrace ();
6046 }
61- return null ;
62- } );
47+ }
48+ ModernFix . LOGGER . info ( "Processed {} config reloads" , runnablesToRun . size () );
6349 }
6450
51+ private static final Class <?> WATCHED_FILE = LamdbaExceptionUtils .uncheck (() -> Class .forName ("com.electronwill.nightconfig.core.file.FileWatcher$WatchedFile" ));
52+ private static final Field CHANGE_HANDLER = ObfuscationReflectionHelper .findField (WATCHED_FILE , "changeHandler" );
53+
6554 static class MonitoringMap extends ConcurrentHashMap <Path , Object > {
6655 public MonitoringMap (ConcurrentHashMap <Path , ?> oldMap ) {
6756 super (oldMap );
@@ -82,75 +71,22 @@ public Object computeIfAbsent(Path key, Function<? super Path, ?> mappingFunctio
8271 }
8372 }
8473
85- private static final Set <Class <?>> UNKNOWN_FILE_CONFIG_CLASSES = Collections .synchronizedSet (new ReferenceOpenHashSet <>());
86-
87- public static Object toWriteSyncConfig (Object config ) {
88- if (config == null )
89- return null ;
90- try {
91- if (WRITE_SYNC_CONFIG .isAssignableFrom (config .getClass ())) {
92- return config ;
93- } else if (AUTOSAVE_CONFIG .isAssignableFrom (config .getClass ())) {
94- FileConfig fc = (FileConfig )AUTOSAVE_FILECONFIG .get (config );
95- return toWriteSyncConfig (fc );
96- } else {
97- if (UNKNOWN_FILE_CONFIG_CLASSES .add (config .getClass ()))
98- ModernFixMixinPlugin .instance .logger .warn ("Unexpected FileConfig class: {}" , config .getClass ().getName ());
99- return null ;
100- }
101- } catch (ReflectiveOperationException e ) {
102- return null ;
103- }
104- }
105-
10674 static class MonitoringConfigTracker implements Runnable {
10775 private final Runnable configTracker ;
10876
10977 MonitoringConfigTracker (Runnable r ) {
11078 this .configTracker = r ;
11179 }
11280
113- private void protectFromSaving (FileConfig config , Runnable runnable ) throws ReflectiveOperationException {
114- Object writeSyncConfig = toWriteSyncConfig (config );
115- if (writeSyncConfig != null ) {
116- // keep trying to write, releasing the config lock each time in case something else needs to lock it
117- // for any reason
118- while (true ) {
119- // acquiring synchronized block here should in theory prevent any other concurrent loads/saves, based
120- // off WriteSyncFileConfig implementation
121- synchronized (writeSyncConfig ) {
122- if (CURRENTLY_WRITING .getBoolean (writeSyncConfig )) {
123- ModernFixMixinPlugin .instance .logger .fatal ("Config being written during load!!!" );
124- try { Thread .sleep (500 ); } catch (InterruptedException e ) { Thread .currentThread ().interrupt (); }
125- continue ;
126- }
127- // at this point, currentlyWriting is false, and we acquired synchronized lock, should be good to
128- // go
129- runnable .run ();
130- break ;
131- }
132- }
133- } else {
134- runnable .run ();
135- }
136- }
137-
13881 /**
139- * This entrypoint runs when the file watcher has detected a change on the config file. Before passing
140- * this through to Forge, use reflection hacks to confirm the config system is not still writing to the file.
141- * If it is, spin until writing finishes. Immediately returning might result in the event never being observed.
82+ * Add the config runnable to the list to be processed by the main thread.
14283 */
14384 @ Override
14485 public void run () {
145- try {
146- Field theField = getCurrentlyWritingFieldOnWatcher (this .configTracker );
147- if (theField != null ) {
148- CommentedFileConfig cfg = (CommentedFileConfig )theField .get (this .configTracker );
149- // will synchronize and check saving flag
150- protectFromSaving (cfg , configTracker );
151- }
152- } catch (ReflectiveOperationException e ) {
153- e .printStackTrace ();
86+ synchronized (configsToReload ) {
87+ if (configsToReload .size () == 0 )
88+ ModernFixMixinPlugin .instance .logger .info ("Please use /{} to reload any changed mod config files" , FMLLoader .getDist ().isDedicatedServer () ? "mfsrc" : "mfrc" );
89+ configsToReload .add (configTracker );
15490 }
15591 }
15692 }
0 commit comments