diff --git a/config/mv_checks.xml b/config/mv_checks.xml index 78a095c1..994e530d 100644 --- a/config/mv_checks.xml +++ b/config/mv_checks.xml @@ -145,7 +145,10 @@ - + + + + @@ -380,7 +383,9 @@ - + + + @@ -518,15 +523,30 @@ - - + + + - + + + + + + + + + + + + + + + diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java index 6d19ca24..54159120 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java @@ -18,6 +18,7 @@ import com.onarandombox.MultiverseCore.worldnew.helpers.FilesManipulator; import com.onarandombox.MultiverseCore.worldnew.options.CloneWorldOptions; import com.onarandombox.MultiverseCore.worldnew.options.CreateWorldOptions; import com.onarandombox.MultiverseCore.worldnew.options.ImportWorldOptions; +import com.onarandombox.MultiverseCore.worldnew.options.KeepWorldSettingsOptions; import com.onarandombox.MultiverseCore.worldnew.options.RegenWorldOptions; import com.onarandombox.MultiverseCore.worldnew.results.CloneWorldResult; import com.onarandombox.MultiverseCore.worldnew.results.CreateWorldResult; @@ -54,7 +55,7 @@ import static com.onarandombox.MultiverseCore.worldnew.helpers.DataStore.WorldCo /** * This manager contains all the world managing functions that your heart desires. */ -@Service +@Service // SUPPRESS CHECKSTYLE: ClassFanOutComplexity This is the world manager, it's going to be complex. public class WorldManager { private static final List CLONE_IGNORE_FILES = Arrays.asList("uid.dat", "session.lock"); @@ -100,7 +101,7 @@ public class WorldManager { * @return The result of the load. */ public Try initAllWorlds() { - return populateWorldFromConfig().andThenTry(() -> { + return updateWorldsFromConfig().andThenTry(() -> { loadDefaultWorlds(); autoLoadWorlds(); saveWorldsConfig(); @@ -108,30 +109,33 @@ public class WorldManager { } /** - * Populate world map from the worlds.yml config. + * Updates the current set of worlds to match the worlds config. * - * @return The result of the world map population. + * @return A successful Try if the worlds.yml config was loaded successfully. */ - private Try populateWorldFromConfig() { + private Try updateWorldsFromConfig() { return worldsConfigManager.load().mapTry(result -> { - var newWorldConfigs = result._1(); - var removedWorlds = result._2(); - - newWorldConfigs.forEach(worldConfig -> getWorld(worldConfig.getWorldName()) - .peek(unloadedWorld -> unloadedWorld.setWorldConfig(worldConfig)) - .onEmpty(() -> { - MultiverseWorld mvWorld = new MultiverseWorld(worldConfig.getWorldName(), worldConfig); - worldsMap.put(mvWorld.getName(), mvWorld); - })); - - removedWorlds.forEach(worldName -> removeWorld(worldName) - .onFailure(failure -> Logging.severe("Failed to unload world %s: %s", worldName, failure)) - .onSuccess(success -> Logging.fine("Unloaded world %s as it was removed from config", worldName))); - + loadNewWorldConfigs(result._1()); + removeWorldsNotInConfigs(result._2()); return null; }); } + private void loadNewWorldConfigs(Collection newWorldConfigs) { + newWorldConfigs.forEach(worldConfig -> getWorld(worldConfig.getWorldName()) + .peek(unloadedWorld -> unloadedWorld.setWorldConfig(worldConfig)) + .onEmpty(() -> { + MultiverseWorld mvWorld = new MultiverseWorld(worldConfig.getWorldName(), worldConfig); + worldsMap.put(mvWorld.getName(), mvWorld); + })); + } + + private void removeWorldsNotInConfigs(Collection removedWorlds) { + removedWorlds.forEach(worldName -> removeWorld(worldName) + .onFailure(failure -> Logging.severe("Failed to unload world %s: %s", worldName, failure)) + .onSuccess(success -> Logging.fine("Unloaded world %s as it was removed from config", worldName))); + } + /** * Load worlds that are already loaded by bukkit before Multiverse-Core is loaded. */ @@ -166,29 +170,40 @@ public class WorldManager { * @return The result of the creation. */ public Result createWorld(CreateWorldOptions options) { - // Params validations + return invalidateCreateWorldOptions(options).getOrElse(() -> createValidatedWorld(options)); + } + + private Option> invalidateCreateWorldOptions( + CreateWorldOptions options) { + Result result = null; + if (!worldNameChecker.isValidWorldName(options.worldName())) { - return worldActionResult(CreateWorldResult.Failure.INVALID_WORLDNAME, options.worldName()); - } - if (getLoadedWorld(options.worldName()).isDefined()) { - return worldActionResult(CreateWorldResult.Failure.WORLD_EXIST_LOADED, options.worldName()); - } - if (getWorld(options.worldName()).isDefined()) { - return worldActionResult(CreateWorldResult.Failure.WORLD_EXIST_UNLOADED, options.worldName()); - } - File worldFolder = new File(Bukkit.getWorldContainer(), options.worldName()); - if (worldFolder.exists()) { - return worldActionResult(CreateWorldResult.Failure.WORLD_EXIST_FOLDER, options.worldName()); + result = worldActionResult(CreateWorldResult.Failure.INVALID_WORLDNAME, options.worldName()); + } else if (getLoadedWorld(options.worldName()).isDefined()) { + result = worldActionResult(CreateWorldResult.Failure.WORLD_EXIST_LOADED, options.worldName()); + } else if (getWorld(options.worldName()).isDefined()) { + result = worldActionResult(CreateWorldResult.Failure.WORLD_EXIST_UNLOADED, options.worldName()); + } else if (hasWorldFolder(options.worldName())) { + result = worldActionResult(CreateWorldResult.Failure.WORLD_EXIST_FOLDER, options.worldName()); } + return Option.of(result); + } + + private boolean hasWorldFolder(String worldName) { + File worldFolder = new File(Bukkit.getWorldContainer(), worldName); + return worldFolder.exists(); + } + + private Result createValidatedWorld( + CreateWorldOptions options) { String parsedGenerator = parseGenerator(options.worldName(), options.generator()); return createBukkitWorld(WorldCreator.name(options.worldName()) .environment(options.environment()) .generateStructures(options.generateStructures()) .generator(parsedGenerator) .seed(options.seed()) - .type(options.worldType())) - .fold( + .type(options.worldType())).fold( exception -> worldActionResult(CreateWorldResult.Failure.BUKKIT_CREATION_FAILED, options.worldName(), exception.getMessage()), world -> { @@ -197,7 +212,6 @@ public class WorldManager { }); } - /** * Imports an existing world folder. * @@ -205,25 +219,32 @@ public class WorldManager { * @return The result of the import. */ public Result importWorld(ImportWorldOptions options) { - // Params validations + return invalidateImportWorldOptions(options).getOrElse(() -> importValidatedWorld(options)); + } + + private Option> invalidateImportWorldOptions( + ImportWorldOptions options) { + Result result = null; + if (!worldNameChecker.isValidWorldName(options.worldName())) { - return worldActionResult(ImportWorldResult.Failure.INVALID_WORLDNAME, options.worldName()); - } - if (!worldNameChecker.isValidWorldFolder(options.worldName())) { - return worldActionResult(ImportWorldResult.Failure.WORLD_FOLDER_INVALID, options.worldName()); - } - if (isLoadedWorld(options.worldName())) { - return worldActionResult(ImportWorldResult.Failure.WORLD_EXIST_LOADED, options.worldName()); - } - if (isWorld(options.worldName())) { - return worldActionResult(ImportWorldResult.Failure.WORLD_EXIST_UNLOADED, options.worldName()); + result = worldActionResult(ImportWorldResult.Failure.INVALID_WORLDNAME, options.worldName()); + } else if (!worldNameChecker.isValidWorldFolder(options.worldName())) { + result = worldActionResult(ImportWorldResult.Failure.WORLD_FOLDER_INVALID, options.worldName()); + } else if (isLoadedWorld(options.worldName())) { + result = worldActionResult(ImportWorldResult.Failure.WORLD_EXIST_LOADED, options.worldName()); + } else if (isWorld(options.worldName())) { + result = worldActionResult(ImportWorldResult.Failure.WORLD_EXIST_UNLOADED, options.worldName()); } + return Option.of(result); + } + + private Result importValidatedWorld( + ImportWorldOptions options) { String parsedGenerator = parseGenerator(options.worldName(), options.generator()); return createBukkitWorld(WorldCreator.name(options.worldName()) .environment(options.environment()) - .generator(parsedGenerator)) - .fold( + .generator(parsedGenerator)).fold( exception -> worldActionResult(ImportWorldResult.Failure.BUKKIT_CREATION_FAILED, options.worldName(), exception.getMessage()), world -> { @@ -291,17 +312,27 @@ public class WorldManager { * @return The result of the load. */ public Result loadWorld(@NotNull MultiverseWorld mvWorld) { - // Params validations + return invalidateWorldToLoad(mvWorld).getOrElse(() -> loadValidatedWorld(mvWorld)); + } + + private Option> invalidateWorldToLoad( + @NotNull MultiverseWorld mvWorld) { + Result result = null; + if (loadTracker.contains(mvWorld.getName())) { // This is to prevent recursive calls by WorldLoadEvent Logging.fine("World already loading: " + mvWorld.getName()); - return worldActionResult(LoadWorldResult.Failure.WORLD_ALREADY_LOADING, mvWorld.getName()); - } - if (isLoadedWorld(mvWorld)) { + result = worldActionResult(LoadWorldResult.Failure.WORLD_ALREADY_LOADING, mvWorld.getName()); + } else if (isLoadedWorld(mvWorld)) { Logging.severe("World already loaded: " + mvWorld.getName()); - return worldActionResult(LoadWorldResult.Failure.WORLD_EXIST_LOADED, mvWorld.getName()); + result = worldActionResult(LoadWorldResult.Failure.WORLD_EXIST_LOADED, mvWorld.getName()); } + return Option.of(result); + } + + private Result loadValidatedWorld( + @NotNull MultiverseWorld mvWorld) { return createBukkitWorld(WorldCreator.name(mvWorld.getName()) .environment(mvWorld.getEnvironment()) .generator(Strings.isNullOrEmpty(mvWorld.getGenerator()) ? null : mvWorld.getGenerator()) @@ -353,8 +384,8 @@ public class WorldManager { * @param world The multiverse world to unload. * @return The result of the unload action. */ - public Result - unloadWorld(@NotNull LoadedMultiverseWorld world) { + public Result unloadWorld( + @NotNull LoadedMultiverseWorld world) { if (unloadTracker.contains(world.getName())) { // This is to prevent recursive calls by WorldUnloadEvent Logging.fine("World already unloading: " + world.getName()); @@ -385,8 +416,8 @@ public class WorldManager { * @param worldName The name of the world to remove. * @return The result of the remove. */ - public Result - removeWorld(@NotNull String worldName) { + public Result removeWorld( + @NotNull String worldName) { return getWorld(worldName) .map(this::removeWorld) .getOrElse(() -> worldActionResult(RemoveWorldResult.Failure.WORLD_NON_EXISTENT, worldName)); @@ -412,8 +443,8 @@ public class WorldManager { * @param loadedWorld The multiverse world to remove. * @return The result of the remove. */ - public Result - removeWorld(@NotNull LoadedMultiverseWorld loadedWorld) { + public Result removeWorld( + @NotNull LoadedMultiverseWorld loadedWorld) { var result = unloadWorld(loadedWorld); if (result.isFailure()) { return Result.failure(RemoveWorldResult.Failure.UNLOAD_FAILED, result.getReasonMessage()); @@ -524,8 +555,8 @@ public class WorldManager { })); } - private Result - cloneWorldValidateWorld(@NotNull CloneWorldOptions options) { + private Result cloneWorldValidateWorld( + @NotNull CloneWorldOptions options) { String newWorldName = options.newWorldName(); if (!worldNameChecker.isValidWorldName(newWorldName)) { Logging.severe("Invalid world name: " + newWorldName); @@ -545,8 +576,8 @@ public class WorldManager { return Result.success(); } - private Result - cloneWorldCopyFolder(@NotNull CloneWorldOptions options) { + private Result cloneWorldCopyFolder( + @NotNull CloneWorldOptions options) { // TODO: Check null? File worldFolder = options.world().getBukkitWorld().map(World::getWorldFolder).getOrNull(); File newWorldFolder = new File(Bukkit.getWorldContainer(), options.newWorldName()); @@ -557,8 +588,14 @@ public class WorldManager { } private void cloneWorldTransferData(@NotNull CloneWorldOptions options, @NotNull LoadedMultiverseWorld newWorld) { - LoadedMultiverseWorld world = options.world(); + DataTransfer dataTransfer = transferData(options, options.world()); + dataTransfer.pasteAllTo(newWorld); + } + + private DataTransfer transferData( + @NotNull KeepWorldSettingsOptions options, @NotNull LoadedMultiverseWorld world) { DataTransfer dataTransfer = new DataTransfer<>(); + if (options.keepWorldConfig()) { dataTransfer.addDataStore(new WorldConfigStore(), world); } @@ -568,7 +605,8 @@ public class WorldManager { if (options.keepWorldBorder()) { dataTransfer.addDataStore(new WorldBorderStore(), world); } - dataTransfer.pasteAllTo(newWorld); + + return dataTransfer; } /** @@ -581,16 +619,7 @@ public class WorldManager { // TODO: Teleport players out of world, and back in after regen LoadedMultiverseWorld world = options.world(); - DataTransfer dataTransfer = new DataTransfer<>(); - if (options.keepWorldConfig()) { - dataTransfer.addDataStore(new WorldConfigStore(), world); - } - if (options.keepGameRule()) { - dataTransfer.addDataStore(new GameRulesStore(), world); - } - if (options.keepWorldBorder()) { - dataTransfer.addDataStore(new WorldBorderStore(), world); - } + DataTransfer dataTransfer = transferData(options, world); CreateWorldOptions createWorldOptions = CreateWorldOptions.worldName(world.getName()) .environment(world.getEnvironment()) diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/CloneWorldOptions.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/CloneWorldOptions.java index e6b66366..153e2dd1 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/CloneWorldOptions.java +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/CloneWorldOptions.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; /** * Options for customizing the cloning of a world. */ -public class CloneWorldOptions { +public final class CloneWorldOptions implements KeepWorldSettingsOptions { /** * Creates a new {@link CloneWorldOptions} instance with the given world. @@ -55,6 +55,7 @@ public class CloneWorldOptions { * @param keepGameRule Whether to keep the game rule of the world during cloning. * @return This {@link CloneWorldOptions} instance. */ + @Override public @NotNull CloneWorldOptions keepGameRule(boolean keepGameRule) { this.keepGameRule = keepGameRule; return this; @@ -65,10 +66,12 @@ public class CloneWorldOptions { * * @return Whether to keep the game rule of the world during cloning. */ + @Override public boolean keepGameRule() { return keepGameRule; } + @Override public @NotNull CloneWorldOptions keepWorldConfig(boolean keepWorldConfig) { this.keepWorldConfig = keepWorldConfig; return this; @@ -79,10 +82,12 @@ public class CloneWorldOptions { * * @return Whether to keep the world config of the world during cloning. */ + @Override public boolean keepWorldConfig() { return keepWorldConfig; } + @Override public @NotNull CloneWorldOptions keepWorldBorder(boolean keepWorldBorder) { this.keepWorldBorder = keepWorldBorder; return this; @@ -93,6 +98,7 @@ public class CloneWorldOptions { * * @return Whether to keep the world border of the world during cloning. */ + @Override public boolean keepWorldBorder() { return keepWorldBorder; } diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/KeepWorldSettingsOptions.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/KeepWorldSettingsOptions.java new file mode 100644 index 00000000..51a6bb94 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/KeepWorldSettingsOptions.java @@ -0,0 +1,51 @@ +package com.onarandombox.MultiverseCore.worldnew.options; + +import org.jetbrains.annotations.NotNull; + +public sealed interface KeepWorldSettingsOptions permits CloneWorldOptions, RegenWorldOptions { + + /** + * Sets whether to keep the game rule of the world. + * + * @param keepGameRule Whether to keep the game rule of the world. + * @return This {@link KeepWorldSettingsOptions} instance. + */ + @NotNull KeepWorldSettingsOptions keepGameRule(boolean keepGameRule); + + /** + * Gets whether to keep the game rule of the world. + * + * @return Whether to keep the game rule of the world. + */ + boolean keepGameRule(); + + /** + * Sets whether to keep the world config of the world. + * + * @param keepWorldConfig Whether to keep the world config of the world. + * @return This {@link KeepWorldSettingsOptions} instance. + */ + @NotNull KeepWorldSettingsOptions keepWorldConfig(boolean keepWorldConfig); + + /** + * Gets whether to keep the world config of the world. + * + * @return Whether to keep the world config of the world. + */ + boolean keepWorldConfig(); + + /** + * Sets whether to keep the world border of the world. + * + * @param keepWorldBorder Whether to keep the world border of the world. + * @return This {@link KeepWorldSettingsOptions} instance. + */ + @NotNull KeepWorldSettingsOptions keepWorldBorder(boolean keepWorldBorder); + + /** + * Gets whether to keep the world border of the world. + * + * @return Whether to keep the world border of the world. + */ + boolean keepWorldBorder(); +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/RegenWorldOptions.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/RegenWorldOptions.java index 9cd9c8d7..00ed0476 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/RegenWorldOptions.java +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/RegenWorldOptions.java @@ -9,7 +9,7 @@ import java.util.Random; /** * Options for customizing the regeneration of a world. */ -public class RegenWorldOptions { +public final class RegenWorldOptions implements KeepWorldSettingsOptions { /** * Creates a new {@link RegenWorldOptions} instance with the given world. @@ -48,6 +48,7 @@ public class RegenWorldOptions { * @param keepGameRule Whether to keep the game rule of the world during regeneration. * @return This {@link RegenWorldOptions} instance. */ + @Override public @NotNull RegenWorldOptions keepGameRule(boolean keepGameRule) { this.keepGameRule = keepGameRule; return this; @@ -58,6 +59,7 @@ public class RegenWorldOptions { * * @return Whether to keep the game rule of the world during regeneration. */ + @Override public boolean keepGameRule() { return keepGameRule; } @@ -68,6 +70,7 @@ public class RegenWorldOptions { * @param keepWorldConfig Whether to keep the world config of the world during regeneration. * @return This {@link RegenWorldOptions} instance. */ + @Override public @NotNull RegenWorldOptions keepWorldConfig(boolean keepWorldConfig) { this.keepWorldConfig = keepWorldConfig; return this;