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 ;
85import net .minecraftforge .fml .common .ObfuscationReflectionHelper ;
96import org .embeddedt .modernfix .core .ModernFixMixinPlugin ;
107import org .embeddedt .modernfix .util .CommonModUtil ;
118
129import java .lang .reflect .Field ;
1310import java .nio .file .Path ;
14- import java .util .Collections ;
15- import java .util .HashMap ;
16- import java .util .Map ;
17- import java .util .Set ;
11+ import java .util .LinkedHashSet ;
1812import java .util .concurrent .ConcurrentHashMap ;
1913import java .util .function .Function ;
2014
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- */
2515public class NightConfigFixer {
16+ public static final LinkedHashSet <Runnable > configsToReload = new LinkedHashSet <>();
2617 public static void monitorFileWatcher () {
18+ if (!ModernFixMixinPlugin .instance .isOptionEnabled ("bugfix.fix_config_crashes.NightConfigFixerMixin" ))
19+ return ;
2720 CommonModUtil .runWithoutCrash (() -> {
2821 FileWatcher watcher = FileWatcher .defaultInstance ();
2922 Field field = FileWatcher .class .getDeclaredField ("watchedFiles" );
@@ -38,30 +31,6 @@ public static void monitorFileWatcher() {
3831 private static final Class <?> WATCHED_FILE = LamdbaExceptionUtils .uncheck (() -> Class .forName ("com.electronwill.nightconfig.core.file.FileWatcher$WatchedFile" ));
3932 private static final Field CHANGE_HANDLER = ObfuscationReflectionHelper .findField (WATCHED_FILE , "changeHandler" );
4033
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 ();
60- }
61- return null ;
62- });
63- }
64-
6534 static class MonitoringMap extends ConcurrentHashMap <Path , Object > {
6635 public MonitoringMap (ConcurrentHashMap <Path , ?> oldMap ) {
6736 super (oldMap );
@@ -82,75 +51,25 @@ public Object computeIfAbsent(Path key, Function<? super Path, ?> mappingFunctio
8251 }
8352 }
8453
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-
10654 static class MonitoringConfigTracker implements Runnable {
10755 private final Runnable configTracker ;
10856
10957 MonitoringConfigTracker (Runnable r ) {
11058 this .configTracker = r ;
11159 }
11260
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-
13861 /**
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.
62+ * Add the config
14263 */
14364 @ Override
14465 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 );
66+ synchronized ( configsToReload ) {
67+ int oldSize = configsToReload . size ( );
68+ configsToReload . add ( configTracker );
69+ if ( oldSize == 0 ) {
70+ 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." );
71+ ModernFixMixinPlugin . instance . logger . info ( "This functionality will be restored in a future ModernFix update." );
15172 }
152- } catch (ReflectiveOperationException e ) {
153- e .printStackTrace ();
15473 }
15574 }
15675 }
0 commit comments