From d4cad8f718fd923115a3c7d1f9fcd3dab4ce0053 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 20 Sep 2024 07:29:32 -0500 Subject: [PATCH] Fix config file handler corruption due to reading/writing concurrently --- .../core/config/file/ConfigFileHandling.java | 149 ++++++++++-------- 1 file changed, 85 insertions(+), 64 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java index 12fb71a2a..d2cb9fa50 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java @@ -33,6 +33,7 @@ import org.apache.logging.log4j.Logger; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.locks.ReentrantLock; /** * Handles reading and writing config files. @@ -53,6 +54,9 @@ public class ConfigFileHandling /** This is the object for night-config */ private final CommentedFileConfig nightConfig; + /** prevents readers/writers from overlapping and causing the config file from being duplicated or corrupted */ + private final ReentrantLock readWriteLock = new ReentrantLock(); + //=============// @@ -72,40 +76,49 @@ public class ConfigFileHandling /** Saves the entire config to the file */ - public void saveToFile() - { - saveToFile(this.nightConfig); - } + public void saveToFile() { this.saveToFile(this.nightConfig); } /** Saves the entire config to the file */ public void saveToFile(CommentedFileConfig nightConfig) { - if (!Files.exists(configPath)) // Try to check if the config exists - { - reCreateFile(configPath); - } - - - loadNightConfig(nightConfig); - - - for (AbstractConfigType entry : this.configBase.entries) - { - if (ConfigEntry.class.isAssignableFrom(entry.getClass())) - { - createComment((ConfigEntry) entry, nightConfig); - saveEntry((ConfigEntry) entry, nightConfig); - } - } - - try { - nightConfig.save(); + this.readWriteLock.lock(); + + + + if (!Files.exists(this.configPath)) // Try to check if the config exists + { + reCreateFile(this.configPath); + } + + + this.loadNightConfig(nightConfig); + + + for (AbstractConfigType entry : this.configBase.entries) + { + if (ConfigEntry.class.isAssignableFrom(entry.getClass())) + { + this.createComment((ConfigEntry) entry, nightConfig); + this.saveEntry((ConfigEntry) entry, nightConfig); + } + } + + + try + { + nightConfig.save(); + } + catch (Exception e) + { + // If it fails to save, crash game + SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + this.configPath + "]", e); + } + } - catch (Exception e) + finally { - // If it fails to save, crash game - SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + configPath.toString() + "]", e); + this.readWriteLock.unlock(); } } @@ -116,69 +129,77 @@ public class ConfigFileHandling */ public void loadFromFile() { - int currentCfgVersion = this.configBase.configVersion; try { - // Dont load the real `this.nightConfig`, instead create a tempoary one - CommentedFileConfig tmpNightConfig = CommentedFileConfig.builder(this.configPath.toFile()).build(); - tmpNightConfig.load(); - // Attempt to get the version number - currentCfgVersion = (Integer) tmpNightConfig.get("_version"); - tmpNightConfig.close(); - } catch (Exception ignored) { } - - if (currentCfgVersion == this.configBase.configVersion) - { - // handle normally - } - else if (currentCfgVersion > this.configBase.configVersion) - { - this.logger.warn("Found config version [" + currentCfgVersion + "] which is newer than current mods config version of [" + this.configBase.configVersion + "]. You may have downgraded the mod and items may have been moved, you have been warned"); - } - else // if (currentCfgVersion < configBase.configVersion) - { - this.logger.warn(this.configBase.modName +" config is of an older version, currently there is no config updater... so resetting config"); + this.readWriteLock.lock(); + + int currentCfgVersion = this.configBase.configVersion; try { - Files.delete(this.configPath); + // Dont load the real `this.nightConfig`, instead create a tempoary one + CommentedFileConfig tmpNightConfig = CommentedFileConfig.builder(this.configPath.toFile()).build(); + tmpNightConfig.load(); + // Attempt to get the version number + currentCfgVersion = (Integer) tmpNightConfig.get("_version"); + tmpNightConfig.close(); } - catch (Exception e) + catch (Exception ignored) { } + + if (currentCfgVersion == this.configBase.configVersion) { - this.logger.error(e); + // handle normally } + else if (currentCfgVersion > this.configBase.configVersion) + { + this.logger.warn("Found config version [" + currentCfgVersion + "] which is newer than current mods config version of [" + this.configBase.configVersion + "]. You may have downgraded the mod and items may have been moved, you have been warned"); + } + else // if (currentCfgVersion < configBase.configVersion) + { + this.logger.warn(this.configBase.modName + " config is of an older version, currently there is no config updater... so resetting config"); + try + { + Files.delete(this.configPath); + } + catch (Exception e) + { + this.logger.error(e); + } + } + + this.loadFromFile(this.nightConfig); + this.nightConfig.set("_version", this.configBase.configVersion); + } + finally + { + this.readWriteLock.unlock(); } - - this.loadFromFile(this.nightConfig); - this.nightConfig.set("_version", this.configBase.configVersion); } /** * Loads the entire config from the file * * @apiNote This overwrites any value currently stored in the config */ - public void loadFromFile(CommentedFileConfig nightConfig) + private void loadFromFile(CommentedFileConfig nightConfig) { // Attempt to load the file and if it fails then save config to file - if (Files.exists(configPath)) + if (Files.exists(this.configPath)) { - loadNightConfig(nightConfig); + this.loadNightConfig(nightConfig); } else { - reCreateFile(configPath); + reCreateFile(this.configPath); } // Load all the entries for (AbstractConfigType entry : this.configBase.entries) { - if ( - ConfigEntry.class.isAssignableFrom(entry.getClass()) && - entry.getAppearance().showInFile - ) + if (ConfigEntry.class.isAssignableFrom(entry.getClass()) + && entry.getAppearance().showInFile) { - createComment((ConfigEntry) entry, nightConfig); - loadEntry((ConfigEntry) entry, nightConfig); + this.createComment((ConfigEntry) entry, nightConfig); + this.loadEntry((ConfigEntry) entry, nightConfig); } } @@ -190,7 +211,7 @@ public class ConfigFileHandling catch (Exception e) { // If it fails to save, crash game - SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + configPath.toString() + "]", e); + SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + this.configPath + "]", e); } }