diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/handle/FileConfigurationHandle.java b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/FileConfigurationHandle.java index a52f38dd..e95e34e3 100644 --- a/src/main/java/org/mvplugins/multiverse/core/configuration/handle/FileConfigurationHandle.java +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/FileConfigurationHandle.java @@ -2,9 +2,11 @@ package org.mvplugins.multiverse.core.configuration.handle; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.logging.Logger; +import com.dumptruckman.minecraft.util.Logging; import io.vavr.control.Try; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; @@ -39,12 +41,32 @@ public abstract class FileConfigurationHandle exten */ @Override public Try load() { - return createConfigFile() - .andThenTry(this::loadConfigObject) - .andThenTry(() -> { - migrateConfig(); - setUpNodes(); - }); + return tryLoadConfigFile().andThenTry(() -> { + migrateConfig(); + setUpNodes(); + }); + } + + private Try tryLoadConfigFile() { + return Try.run(() -> { + createConfigFile(); + loadConfigObject(); + }).fold(this::handleLoadConfigFailure, Try::success); + } + + private @NotNull Try handleLoadConfigFailure(Throwable throwable) { + Logging.severe("Failed to load config file: " + configFile.getName(), throwable); + throwable.printStackTrace(); + return Try.run(() -> { + Path brokenConfigPath = configPath.resolveSibling(configFile.getName() + ".broken." + System.currentTimeMillis()); + Logging.severe("Moving broken config file to: " + brokenConfigPath.getFileName()); + Files.copy(configPath, brokenConfigPath); + Files.delete(configPath); + }).andThenTry(() -> { + Logging.severe("Multiverse-Core will now regenerate a fresh config file with all default options!"); + createConfigFile(); + loadConfigObject(); + }); } /** diff --git a/src/test/java/org/mvplugins/multiverse/core/config/ConfigTest.kt b/src/test/java/org/mvplugins/multiverse/core/config/ConfigTest.kt index 20d33745..5c24b4b5 100644 --- a/src/test/java/org/mvplugins/multiverse/core/config/ConfigTest.kt +++ b/src/test/java/org/mvplugins/multiverse/core/config/ConfigTest.kt @@ -10,10 +10,11 @@ import kotlin.test.* class ConfigTest : TestWithMockBukkit() { private lateinit var config : MVCoreConfig + private lateinit var configFile : File @BeforeTest fun setUp() { - val configFile = File(Path.of(multiverseCore.dataFolder.absolutePath, "config.yml").absolutePathString()) + configFile = File(Path.of(multiverseCore.dataFolder.absolutePath, "config.yml").absolutePathString()) if (configFile.exists()) configFile.delete() config = serviceLocator.getActiveService(MVCoreConfig::class.java).takeIf { it != null } ?: run { @@ -104,4 +105,17 @@ class ConfigTest : TestWithMockBukkit() { assertTrue(config.stringPropertyHandle.setProperty("invalid-property", false).isFailure) assertTrue(config.stringPropertyHandle.setProperty("version", 1.1).isFailure) } + + @Test + fun `Broken config should regen fresh config`() { + val brokenConfigData = getResourceAsText("/broken_config.yml") + assertNotNull(brokenConfigData) + configFile.writeText(brokenConfigData) + assertTrue(config.load().isSuccess) + assertTrue(config.save().isSuccess) + val brokenConfigFile = configFile.parentFile.listFiles({ _, fileName -> fileName.startsWith("config.yml.broken") }) + assertNotNull(brokenConfigFile) + assertEquals(1, brokenConfigFile.size) + assertConfigEquals("/fresh_config.yml", "config.yml") + } } diff --git a/src/test/resources/broken_config.yml b/src/test/resources/broken_config.yml new file mode 100644 index 00000000..418c2a5c --- /dev/null +++ b/src/test/resources/broken_config.yml @@ -0,0 +1,38 @@ +world: + enforce-access: false + enforce-gamemode: true + auto-purge-entities: false + +teleport: + use-finer-teleport-permissions: true + concurrent-teleport-limit: 50 + teleport-intercept: true + +spawn: + first-spawn-override: true + first-spawn-location: '' + enable-join-destination: false + join-destination: '' + +portal: + use-custom-portal-search: false + custom-portal-search-radius: 128 + +messaging: + enable-chat-prefix: false + chat-prefix-format: '[%world%]%chat%' + register-papi-hook: true + default-locale: en + per-player-locale: true +?! +command: + resolve-alias-name: true + confirm-mode: enable + use-confirm-otp: true + +misc: + global-debug: 0 + silent-start: false + show-donation-message: true + +version: 5.1