From d485238c567aa675b92627e81987134680f996e1 Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Tue, 25 Aug 2026 14:15:23 +0800 Subject: [PATCH 1/2] Fix level-name changes causing mismatched name and key identities --- .../core/world/WorldsConfigManager.java | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/mvplugins/multiverse/core/world/WorldsConfigManager.java b/src/main/java/org/mvplugins/multiverse/core/world/WorldsConfigManager.java index c85e48bad..a10d75f52 100644 --- a/src/main/java/org/mvplugins/multiverse/core/world/WorldsConfigManager.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/WorldsConfigManager.java @@ -18,7 +18,9 @@ import org.jvnet.hk2.annotations.Service; import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.utils.ServerProperties; import org.mvplugins.multiverse.core.utils.result.Attempt; +import org.mvplugins.multiverse.core.world.helpers.DimensionFinder; import org.mvplugins.multiverse.core.world.key.WorldKeyOrName; import org.mvplugins.multiverse.core.world.key.WorldKeyParseFailReason; @@ -34,16 +36,18 @@ final class WorldsConfigManager { private final SortedMap worldConfigMap; private final File worldConfigFile; + private final ServerProperties serverProperties; private YamlConfiguration worldsConfig; private final MultiverseCore multiverseCore; @Inject - WorldsConfigManager(@NotNull MultiverseCore core, @NotNull MultiverseCore multiverseCore) { + WorldsConfigManager(@NotNull MultiverseCore multiverseCore, @NotNull ServerProperties serverProperties) { worldConfigMap = new TreeMap<>(); - worldConfigFile = core.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile(); + worldConfigFile = multiverseCore.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile(); this.multiverseCore = multiverseCore; + this.serverProperties = serverProperties; } /** @@ -202,9 +206,56 @@ private NewAndRemovedWorlds parseNewAndRemovedWorlds() { worldConfigMap.remove(s); } + migrateLevelNameChange(); + return new NewAndRemovedWorlds(newWorldsAdded, worldsRemoved); } + private void migrateLevelNameChange() { + serverProperties.getLevelName() + .peek(levelName -> { + boolean didMigrate = migrateLevelNameChange( + WorldKeyOrName.parseKey(NamespacedKey.minecraft("overworld")), + levelName + ); + didMigrate = migrateLevelNameChange( + WorldKeyOrName.parseKey(NamespacedKey.minecraft("the_nether")), + DimensionFinder.DEFAULT_NETHER_FORMAT.replaceOverworld(levelName) + ) || didMigrate; + didMigrate = migrateLevelNameChange( + WorldKeyOrName.parseKey(NamespacedKey.minecraft("the_end")), + DimensionFinder.DEFAULT_END_FORMAT.replaceOverworld(levelName) + ) || didMigrate; + + if (didMigrate) { + Logging.warning("We have detected and migrated your default world's name due to a level-name change."); + Logging.warning("Please check your worlds.yml file and world list to ensure everything is correct."); + Logging.warning("Additionally, all references to the old world name will no longer work. " + + "Please update all references within your configuration files/plugins."); + } + }); + } + + private boolean migrateLevelNameChange(WorldKeyOrName keyOrName, String newWorldName) { + if (getWorldConfig(WorldKeyOrName.parseKey(NamespacedKey.minecraft(newWorldName))).isDefined()) { + Logging.severe("Unable to migrate default world's name caused by level-name change."); + Logging.severe("We have detected a clash between the default name '%s' and an existing non-default world in your worlds.yml file.", newWorldName); + Logging.severe("Please change the level-name in server.properties to something else and restart the server."); + return false; + } + + return getWorldConfig(keyOrName).map(worldConfig -> { + if (newWorldName.equals(worldConfig.getLegacyWorldName())) { + return false; + } + Logging.info("Updating legacy world name for %s from %s to %s due to level-name change.", + keyOrName, worldConfig.getLegacyWorldName(), newWorldName); + worldConfig.setLegacyWorldName(newWorldName); + worldConfig.save(); + return true; + }).getOrElse(false); + } + /** * Whether the worlds.yml file has been loaded. * From f6a8a6b3b4bdf225a349083af2c983ea50436d45 Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Tue, 25 Aug 2026 15:03:10 +0800 Subject: [PATCH 2/2] Better error handling for invalid/mismatch world keys --- .../multiverse/core/world/WorldManager.java | 57 +++++++++++++------ .../core/world/WorldsConfigManager.java | 17 +++--- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java b/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java index 2c070b4b5..b574a54a4 100644 --- a/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java @@ -54,6 +54,7 @@ import org.mvplugins.multiverse.core.utils.result.Attempt; import org.mvplugins.multiverse.core.utils.result.FailureReason; import org.mvplugins.multiverse.core.utils.FileUtils; +import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter; import org.mvplugins.multiverse.core.world.biomeprovider.BiomeProviderFactory; import org.mvplugins.multiverse.core.world.entity.EntityPurger; import org.mvplugins.multiverse.core.world.generators.GeneratorProvider; @@ -244,7 +245,10 @@ private void autoLoadWorlds() { .filter(world -> !isLoadedWorld(world) && world.isAutoLoad()) .forEach(world -> loadWorld(LoadWorldOptions.world(world)) .onFailure(failure -> - Logging.severe("Failed to autoload world %s: %s", world.getName(), failure)) + Logging.severe("Failed to autoload world '%s': %s: %s", + world.getName(), + failure, + ChatTextFormatter.removeColor(failure.getFailureMessage().formatted()))) .onSuccess(newMVWorld -> Logging.fine("Autoloaded world %s", newMVWorld.getName()))); } @@ -282,15 +286,24 @@ private Attempt doCreateWorld( WorldKeyOrName keyOrName = keyOrNameWithOptions.keyOrName(); CreateWorldOptions options = keyOrNameWithOptions.options(); String generatorString = generatorProvider.parseGeneratorString(keyOrName.usableName(), options.generator()); - WorldCreator worldCreator = WorldCreatorCompatibility.ofKeyOrName(keyOrName) - .environment(options.environment()) - .generateStructures(options.generateStructures()) - .generatorSettings(options.generatorSettings()) - .seed(options.seed()) - .type(options.worldType()); - WorldCreatorCompatibility.setBonusChest(worldCreator, options.bonusChest()); - options.forcedSpawnPosition().peek(position -> WorldCreatorCompatibility.setForcedSpawnPosition(worldCreator, position)); - return addBiomeProviderToCreator(worldCreator, keyOrName.usableName(), options.biome()) + + return Try.of(() -> { + WorldCreator creator = WorldCreatorCompatibility.ofKeyOrName(keyOrName) + .environment(options.environment()) + .generateStructures(options.generateStructures()) + .generatorSettings(options.generatorSettings()) + .seed(options.seed()) + .type(options.worldType()); + WorldCreatorCompatibility.setBonusChest(creator, options.bonusChest()); + options.forcedSpawnPosition() + .peek(position -> WorldCreatorCompatibility.setForcedSpawnPosition(creator, position)); + return creator; + }) + .fold(throwable -> Attempt.failure(WorldCreatorFailureReason.BUKKIT_CREATION_FAILED, + Replace.WORLD.with(keyOrName.usableName()), + Replace.ERROR.with(throwable)), + Attempt::success) + .mapAttempt(creator -> addBiomeProviderToCreator(creator, keyOrName.usableName(), options.biome())) .mapAttempt(creator -> addGeneratorToCreator(creator, generatorString)) .mapAttempt(this::createBukkitWorld) .transform(CreateFailureReason.WORLD_CREATOR_FAILED) @@ -359,11 +372,15 @@ private Attempt doImportWorld( WorldKeyOrName keyOrName = keyOrNameWithOptions.keyOrName(); ImportWorldOptions options = keyOrNameWithOptions.options(); String generatorString = generatorProvider.parseGeneratorString(keyOrName.usableName(), options.generator()); - WorldCreator worldCreator = WorldCreatorCompatibility.ofKeyOrName(keyOrName) - .environment(options.environment()) - .generatorSettings(options.generatorSettings()); - return addBiomeProviderToCreator(worldCreator, keyOrName.usableName(), options.biome()) + return Try.of(() -> WorldCreatorCompatibility.ofKeyOrName(keyOrName) + .environment(options.environment()) + .generatorSettings(options.generatorSettings())) + .fold(throwable -> Attempt.failure(WorldCreatorFailureReason.BUKKIT_CREATION_FAILED, + Replace.WORLD.with(keyOrName.usableName()), + Replace.ERROR.with(throwable)), + Attempt::success) + .mapAttempt(creator -> addBiomeProviderToCreator(creator, keyOrName.usableName(), options.biome())) .mapAttempt(creator -> addGeneratorToCreator(creator, generatorString)) .mapAttempt(this::createBukkitWorld) .transform(ImportFailureReason.WORLD_CREATOR_FAILED) @@ -528,10 +545,14 @@ private Attempt doLoadWorld(@NotNull L } } - WorldCreator worldCreator = WorldCreatorCompatibility.ofNameAndKey(mvWorld.getKey(), mvWorld.getName()) - .environment(mvWorld.getEnvironment()) - .seed(mvWorld.getSeed()); - return addBiomeProviderToCreator(worldCreator, mvWorld.getName(), mvWorld.getBiome()) + return Try.of(() -> WorldCreatorCompatibility.ofNameAndKey(mvWorld.getKey(), mvWorld.getName()) + .environment(mvWorld.getEnvironment()) + .seed(mvWorld.getSeed())) + .fold(throwable -> Attempt.failure(WorldCreatorFailureReason.BUKKIT_CREATION_FAILED, + Replace.WORLD.with(mvWorld.getName()), + Replace.ERROR.with(throwable)), + Attempt::success) + .mapAttempt(creator -> addBiomeProviderToCreator(creator, mvWorld.getName(), mvWorld.getBiome())) .mapAttempt(creator -> addGeneratorToCreator(creator, mvWorld.getGenerator())) .mapAttempt(this::createBukkitWorld) .transform(LoadFailureReason.WORLD_CREATOR_FAILED) diff --git a/src/main/java/org/mvplugins/multiverse/core/world/WorldsConfigManager.java b/src/main/java/org/mvplugins/multiverse/core/world/WorldsConfigManager.java index a10d75f52..da83d3781 100644 --- a/src/main/java/org/mvplugins/multiverse/core/world/WorldsConfigManager.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/WorldsConfigManager.java @@ -237,17 +237,20 @@ private void migrateLevelNameChange() { } private boolean migrateLevelNameChange(WorldKeyOrName keyOrName, String newWorldName) { - if (getWorldConfig(WorldKeyOrName.parseKey(NamespacedKey.minecraft(newWorldName))).isDefined()) { - Logging.severe("Unable to migrate default world's name caused by level-name change."); - Logging.severe("We have detected a clash between the default name '%s' and an existing non-default world in your worlds.yml file.", newWorldName); - Logging.severe("Please change the level-name in server.properties to something else and restart the server."); - return false; - } - return getWorldConfig(keyOrName).map(worldConfig -> { if (newWorldName.equals(worldConfig.getLegacyWorldName())) { return false; } + + boolean hasConflictKey = WorldKeyOrName.parseKey(newWorldName).map(this::getWorldConfig) + .fold(failure -> false, Option::isDefined); + if (hasConflictKey) { + Logging.severe("Unable to migrate default world's name caused by level-name change."); + Logging.severe("We have detected a clash between the default name '%s' and an existing non-default world in your worlds.yml file.", newWorldName); + Logging.severe("Please change the level-name in server.properties to something else and restart the server."); + return false; + } + Logging.info("Updating legacy world name for %s from %s to %s due to level-name change.", keyOrName, worldConfig.getLegacyWorldName(), newWorldName); worldConfig.setLegacyWorldName(newWorldName);