diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/compatibility/BukkitCompatibility.java b/src/main/java/org/mvplugins/multiverse/core/utils/compatibility/BukkitCompatibility.java index f81d22e54..8cb1c219b 100644 --- a/src/main/java/org/mvplugins/multiverse/core/utils/compatibility/BukkitCompatibility.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/compatibility/BukkitCompatibility.java @@ -14,6 +14,8 @@ /** * Compatibility class used to handle API changes in {@link Bukkit} class. + * + * @since 5.6 */ @ApiStatus.AvailableSince("5.6") public final class BukkitCompatibility { @@ -26,6 +28,23 @@ public final class BukkitCompatibility { GET_WORLD_NAMESPACED_KEY_METHOD = Option.of(ReflectHelper.getMethod(Bukkit.class, "getWorld", NamespacedKey.class)); } + /** + * Check whether the server is using the new dimension storage system introduced in PaperMC 26.1. + *
+ * This is based of whether getLevelDirectory method exists in the server class,which is the main API change for + * the new dimension storage system. + * + * @return True if the server is using the new dimension storage system, else false. + * + * @since 5.6 + */ + @ApiStatus.AvailableSince("5.6") + public static boolean isUsingNewDimensionStorage() { + return GET_LEVEL_DIRECTORY_METHOD + .flatMap(method -> Option.of(ReflectHelper.invokeMethod(Bukkit.getServer(), method))) + .isDefined(); + } + /** * Gets the folder where all the worlds will be store. Before 26.1, all worlds are stored in the root directory * of the server, which can be obtained by {@link Server#getWorldContainer()}. @@ -34,6 +53,8 @@ public final class BukkitCompatibility { * level directory, which needs to be manually parsed. * * @return The location where all the worlds folders should be, depending on server's mc version. + * + * @since 5.6 */ @ApiStatus.AvailableSince("5.6") @NotNull @@ -55,6 +76,8 @@ public static Path getWorldFoldersDirectory() { * * @param nameOrKey Either a name or namespaced key string representation. * @return The world if it exists + * + * @since 5.6 */ @ApiStatus.AvailableSince("5.6") @NotNull 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 6471b214f..0639bf2c9 100644 --- a/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java @@ -317,8 +317,17 @@ private Attempt validateImportWorldOpti String worldName = options.worldName(); if (!worldNameChecker.isValidWorldName(worldName)) { return worldActionResult(ImportFailureReason.INVALID_WORLDNAME, worldName); - } else if (options.doFolderCheck() && !worldNameChecker.isValidWorldFolder(worldName)) { - return worldActionResult(ImportFailureReason.WORLD_FOLDER_INVALID, worldName); + } else if (options.doFolderCheck()) { + //todo This is a duplicate of folder check in load world + WorldNameChecker.FolderStatus folderStatus = worldNameChecker.checkFolder(options.worldName()); + if (!folderStatus.isLoadable()) { + return worldActionResult(ImportFailureReason.WORLD_FOLDER_INVALID, options.worldName()); + } + if (folderStatus == WorldNameChecker.FolderStatus.REQUIRES_MIGRATION) { + Logging.info("World '%s' will be automatically migrated by PaperMC to the new dimension " + + "location. If you face any issue with migration, please contact PaperMC support!", + options.worldName()); + } } return worldActionResult(options); } @@ -480,8 +489,16 @@ private Attempt doLoadWorld(@NotNull L return doLoadBukkitWorld(bukkitWorld, mvWorld); } - if (options.doFolderCheck() && !worldNameChecker.isValidWorldFolder(mvWorld.getName())) { - return worldActionResult(LoadFailureReason.WORLD_FOLDER_INVALID, mvWorld.getName()); + if (options.doFolderCheck()) { + WorldNameChecker.FolderStatus folderStatus = worldNameChecker.checkFolder(mvWorld.getName()); + if (!folderStatus.isLoadable()) { + return worldActionResult(LoadFailureReason.WORLD_FOLDER_INVALID, mvWorld.getName()); + } + if (folderStatus == WorldNameChecker.FolderStatus.REQUIRES_MIGRATION) { + Logging.info("World '%s' will be automatically migrated by PaperMC to the new dimension " + + "location. If you face any issue with migration, please contact PaperMC support!", + mvWorld.getName()); + } } WorldCreator worldCreator = WorldCreator.name(mvWorld.getName()) diff --git a/src/main/java/org/mvplugins/multiverse/core/world/helpers/WorldNameChecker.java b/src/main/java/org/mvplugins/multiverse/core/world/helpers/WorldNameChecker.java index 4bb264c20..6554a856d 100644 --- a/src/main/java/org/mvplugins/multiverse/core/world/helpers/WorldNameChecker.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/helpers/WorldNameChecker.java @@ -5,8 +5,9 @@ import java.util.Locale; import java.util.Set; -import com.dumptruckman.minecraft.util.Logging; import io.vavr.control.Option; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jvnet.hk2.annotations.Service; @@ -95,7 +96,7 @@ public boolean hasWorldFolder(@Nullable String worldName) { * @return True if check result is valid, else false. */ public boolean isValidWorldFolder(@Nullable String worldName) { - return checkFolder(worldName) == FolderStatus.VALID; + return checkFolder(worldName).loadable; } /** @@ -105,7 +106,7 @@ public boolean isValidWorldFolder(@Nullable String worldName) { * @return True if check result is valid, else false. */ public boolean isValidWorldFolder(@Nullable File worldFolder) { - return checkFolder(worldFolder) == FolderStatus.VALID; + return checkFolder(worldFolder).loadable; } /** @@ -120,7 +121,13 @@ public FolderStatus checkFolder(@Nullable String worldName) { return FolderStatus.DOES_NOT_EXIST; } File worldFolder = BukkitCompatibility.getWorldFoldersDirectory().resolve(worldName).toFile(); - Logging.finer("Checking valid folder for world '%s' at: '%s'", worldName, worldFolder.getPath()); + if (BukkitCompatibility.isUsingNewDimensionStorage()) { + File oldWorldFolder = Bukkit.getWorldContainer().toPath().resolve(worldName).toFile(); + if (checkFolder(oldWorldFolder) == FolderStatus.VALID) { + return FolderStatus.REQUIRES_MIGRATION; + } + } + return checkFolder(worldFolder); } @@ -230,16 +237,39 @@ public enum FolderStatus { /** * Folder is valid. */ - VALID, + VALID(true), + + /** + * This folder will cause PaperMC to migrate to new dimension world folder in 26.1+ + */ + REQUIRES_MIGRATION(true), /** * Folder exist, but contents in it doesnt look like a world. */ - NOT_A_WORLD, + NOT_A_WORLD(false), /** * Folder does not exist. */ - DOES_NOT_EXIST + DOES_NOT_EXIST(false), + ; + + private final boolean loadable; + + FolderStatus(boolean loadable) { + this.loadable = loadable; + } + + /** + * Whether this folder status is loadable, meaning it has the basic world data and can be loaded as a world. + * Note that this does not guarantee the server will definitely load the world with no errors. + * + * @return True if folder probably is loadable by the server, else false. + */ + @ApiStatus.AvailableSince("5.6") + public boolean isLoadable() { + return loadable; + } } }