Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.utils.ReflectHelper;
import org.mvplugins.multiverse.core.world.key.WorldKeyOrName;

import java.lang.reflect.Method;
import java.nio.file.Path;
Expand Down Expand Up @@ -91,6 +92,31 @@ public static Option<World> getWorldByNameOrKey(@NotNull String nameOrKey) {
.toOption());
}


/**
* Check if the world represented by the given {@link WorldKeyOrName} exists and return it if present.
*
* <p>This first attempts to resolve the world by name using {@link WorldKeyOrName#usableName()} (which
* delegates to the traditional Bukkit world name lookup). If that fails it will attempt to resolve a
* {@link NamespacedKey} using {@link WorldKeyOrName#usableKey()} (which mirrors the newer Bukkit API
* that accepts namespaced keys, e.g. "minecraft:overworld").
*
* @param keyOrName The key-or-name wrapper providing either a world name or a {@link NamespacedKey}.
* @return The world if it exists.
*
* @since 5.7
*/
@ApiStatus.AvailableSince("5.7")
@NotNull
public static Option<World> getWorldByNameOrKey(@NotNull WorldKeyOrName keyOrName) {
return Option.of(Bukkit.getWorld(keyOrName.usableName()))
.orElse(() -> GET_WORLD_NAMESPACED_KEY_METHOD
.flatMap(method -> ReflectHelper.tryInvokeStaticMethod(method, keyOrName.usableKey()))
.filter(World.class::isInstance)
.map(World.class::cast)
.toOption());
}

private BukkitCompatibility() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.mvplugins.multiverse.core.teleportation.BlockSafety;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import org.mvplugins.multiverse.core.utils.ServerProperties;
import org.mvplugins.multiverse.core.utils.compatibility.BukkitCompatibility;
import org.mvplugins.multiverse.core.utils.compatibility.WorldCompatibility;
import org.mvplugins.multiverse.core.utils.compatibility.WorldCreatorCompatibility;
import org.mvplugins.multiverse.core.utils.result.Attempt;
Expand Down Expand Up @@ -263,9 +264,9 @@ private Attempt<KeyOrNameWithOptions<CreateWorldOptions>, CreateFailureReason> p
keyOrName -> worldActionResult(CreateFailureReason.INVALID_WORLDNAME, keyOrName))
.failIf(keyOrName -> keyOrName.isKey() && !WorldCreatorCompatibility.canCreateWorldWithKey(),
keyOrName -> worldActionResult(CreateFailureReason.NAMESPACEDKEY_UNSUPPORTED, keyOrName))
.failIf(keyOrName -> getLoadedWorld(keyOrName.usableName()).isDefined(),
.failIf(worldStore::isLoadedWorld,
keyOrName -> worldActionResult(CreateFailureReason.WORLD_EXIST_LOADED, keyOrName))
.failIf(keyOrName -> getWorld(keyOrName.usableName()).isDefined(),
.failIf(worldStore::isUnloadedWorld,
keyOrName -> worldActionResult(CreateFailureReason.WORLD_EXIST_UNLOADED, keyOrName))
.failIf(keyOrName -> options.doFolderCheck() && worldNameChecker.hasWorldFolder(keyOrName),
keyOrName -> worldActionResult(CreateFailureReason.WORLD_EXIST_FOLDER, keyOrName))
Expand Down Expand Up @@ -321,12 +322,12 @@ public Attempt<LoadedMultiverseWorld, ImportFailureReason> importWorld(ImportWor
keyOrName -> worldActionResult(ImportFailureReason.INVALID_WORLDNAME, keyOrName))
.failIf(keyOrName -> keyOrName.isKey() && !WorldCreatorCompatibility.canCreateWorldWithKey(),
keyOrName -> worldActionResult(ImportFailureReason.NAMESPACEDKEY_UNSUPPORTED, keyOrName))
.failIf(keyOrName -> getLoadedWorld(keyOrName.usableName()).isDefined(),
.failIf(worldStore::isLoadedWorld,
keyOrName -> worldActionResult(ImportFailureReason.WORLD_EXIST_LOADED, keyOrName))
.failIf(keyOrName -> getWorld(keyOrName.usableName()).isDefined(),
.failIf(worldStore::isUnloadedWorld,
keyOrName -> worldActionResult(ImportFailureReason.WORLD_EXIST_UNLOADED, keyOrName))
.map(keyOrName -> new KeyOrNameWithOptions<>(keyOrName, options))
.mapAttempt(pair -> Option.of(Bukkit.getWorld(pair.keyOrName().usableName()))
.mapAttempt(pair -> BukkitCompatibility.getWorldByNameOrKey(pair.keyOrName())
.map(bukkitWorld -> doImportBukkitWorld(pair, bukkitWorld))
.getOrElse(() -> validateImportWorldOptions(pair).mapAttempt(this::doImportWorld)));
}
Expand Down Expand Up @@ -809,9 +810,9 @@ private Attempt<KeyOrNameWithOptions<CloneWorldOptions>, CloneFailureReason> par
keyOrName -> worldActionResult(CloneFailureReason.INVALID_WORLDNAME, keyOrName))
.failIf(keyOrName -> keyOrName.isKey() && !WorldCreatorCompatibility.canCreateWorldWithKey(),
keyOrName -> worldActionResult(CloneFailureReason.NAMESPACEDKEY_UNSUPPORTED, keyOrName))
.failIf(keyOrName -> isLoadedWorld(keyOrName.usableName()),
.failIf(worldStore::isLoadedWorld,
keyOrName -> worldActionResult(CloneFailureReason.WORLD_EXIST_LOADED, keyOrName))
.failIf(keyOrName -> isWorld(keyOrName.usableName()),
.failIf(worldStore::isWorld,
keyOrName -> worldActionResult(CloneFailureReason.WORLD_EXIST_UNLOADED, keyOrName))
.failIf(worldNameChecker::hasWorldFolder,
keyOrName -> worldActionResult(CloneFailureReason.WORLD_EXIST_FOLDER, keyOrName))
Expand Down
86 changes: 86 additions & 0 deletions src/main/java/org/mvplugins/multiverse/core/world/WorldStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import com.google.common.collect.Multimap;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.utils.CaseInsensitiveStringMap;
import org.mvplugins.multiverse.core.world.key.WorldKeyOrName;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -148,24 +150,108 @@ List<MultiverseWorld> getUnloadedWorlds() {
return List.copyOf(unloadedList);
}

@NotNull
Option<MultiverseWorld> getWorld(@Nullable WorldKeyOrName keyOrName) {
return keyOrName == null
? Option.none()
: getWorld(keyOrName.usableKey()).orElse(() -> getWorld(keyOrName.usableName()));
}

@NotNull
Option<MultiverseWorld> getWorld(@Nullable NamespacedKey worldKey) {
return Option.of(worldKey).map(NamespacedKey::toString).flatMap(this::getWorld);
}

@NotNull
Option<MultiverseWorld> getWorld(@Nullable String worldKeyString) {
return Option.of((MultiverseWorld) loadedMap.get(worldKeyString))
.orElse(() -> Option.of(unloadedMap.get(worldKeyString)));
}

boolean isWorld(@Nullable WorldKeyOrName keyOrName) {
return keyOrName != null && (isWorld(keyOrName.usableKey()) || isWorld(keyOrName.usableName()));
}

boolean isWorld(@Nullable NamespacedKey worldKey) {
return getWorld(worldKey).isDefined();
}

boolean isWorld(@Nullable String worldKeyString) {
return getWorld(worldKeyString).isDefined();
}

@NotNull
Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable WorldKeyOrName keyOrName) {
return keyOrName == null
? Option.none()
: getLoadedWorld(keyOrName.usableKey()).orElse(() -> getLoadedWorld(keyOrName.usableName()));
}

@NotNull
Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable NamespacedKey worldKey) {
return Option.of(worldKey).map(NamespacedKey::toString).flatMap(this::getLoadedWorld);
}

@NotNull
Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable String worldKeyString) {
return Option.of(loadedMap.get(worldKeyString))
.filter(MultiverseWorld::isLoaded);
}

boolean isLoadedWorld(@Nullable WorldKeyOrName keyOrName) {
return keyOrName != null && (isLoadedWorld(keyOrName.usableKey()) || isLoadedWorld(keyOrName.usableName()));
}

boolean isLoadedWorld(@Nullable NamespacedKey worldKey) {
return getLoadedWorld(worldKey).isDefined();
}

boolean isLoadedWorld(@Nullable String worldKeyString) {
return getLoadedWorld(worldKeyString).isDefined();
}

@NotNull
Option<MultiverseWorld> getUnloadedWorld(@Nullable WorldKeyOrName keyOrName) {
return keyOrName == null
? Option.none()
: getUnloadedWorld(keyOrName.usableKey()).orElse(() -> getUnloadedWorld(keyOrName.usableName()));
}

@NotNull
Option<MultiverseWorld> getUnloadedWorld(@Nullable NamespacedKey worldKey) {
return Option.of(worldKey).map(NamespacedKey::toString).flatMap(this::getUnloadedWorld);
}

@NotNull
Option<MultiverseWorld> getUnloadedWorld(@Nullable String worldKeyString) {
return Option.of(unloadedMap.get(worldKeyString))
.filter(world -> !world.isLoaded());
}

boolean isUnloadedWorld(@Nullable WorldKeyOrName keyOrName) {
return keyOrName != null && (isUnloadedWorld(keyOrName.usableKey()) || isUnloadedWorld(keyOrName.usableName()));
}

boolean isUnloadedWorld(@Nullable NamespacedKey worldKey) {
return getUnloadedWorld(worldKey).isDefined();
}

boolean isUnloadedWorld(@Nullable String worldKeyString) {
return getUnloadedWorld(worldKeyString).isDefined();
}

@NotNull
Option<MultiverseWorld> getUnloadedWorldRef(@Nullable WorldKeyOrName keyOrName) {
return keyOrName == null
? Option.none()
: getUnloadedWorldRef(keyOrName.usableKey()).orElse(() -> getUnloadedWorldRef(keyOrName.usableName()));
}

@NotNull
Option<MultiverseWorld> getUnloadedWorldRef(@Nullable NamespacedKey worldKey) {
return Option.of(worldKey).map(NamespacedKey::toString).flatMap(this::getUnloadedWorldRef);
}

@NotNull
Option<MultiverseWorld> getUnloadedWorldRef(@Nullable String worldKeyString) {
return Option.of(unloadedMap.get(worldKeyString));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.locale.message.LocalizableMessage;

Check warning on line 10 in src/main/java/org/mvplugins/multiverse/core/world/key/WorldKeyOrName.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 'org.mvplugins.multiverse.core.locale.message.LocalizableMessage' should be separated from previous imports. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/key/WorldKeyOrName.java:10:1: warning: 'org.mvplugins.multiverse.core.locale.message.LocalizableMessage' should be separated from previous imports. (com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderCheck)
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement;
import org.mvplugins.multiverse.core.utils.ServerProperties;
import org.mvplugins.multiverse.core.utils.compatibility.UnsafeValuesCompatibility;
Expand All @@ -23,7 +24,7 @@
* @since 5.7
*/
@ApiStatus.AvailableSince("5.7")
public sealed abstract class WorldKeyOrName implements Comparable<WorldKeyOrName> permits WorldKeyOrName.Key, WorldKeyOrName.Name {
public sealed abstract class WorldKeyOrName implements Comparable<WorldKeyOrName>, LocalizableMessage permits WorldKeyOrName.Key, WorldKeyOrName.Name {

Check warning on line 27 in src/main/java/org/mvplugins/multiverse/core/world/key/WorldKeyOrName.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 'abstract' modifier out of order with the JLS suggestions. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/key/WorldKeyOrName.java:27:15: warning: 'abstract' modifier out of order with the JLS suggestions. (com.puppycrawl.tools.checkstyle.checks.modifier.ModifierOrderCheck)

Check warning on line 27 in src/main/java/org/mvplugins/multiverse/core/world/key/WorldKeyOrName.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Line is longer than 120 characters (found 151). Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/key/WorldKeyOrName.java:27:0: warning: Line is longer than 120 characters (found 151). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)

Check warning on line 27 in src/main/java/org/mvplugins/multiverse/core/world/key/WorldKeyOrName.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this redundant permitted list.

See more on https://sonarcloud.io/project/issues?id=Multiverse_Multiverse-Core&issues=AZ4HmEuDcb5M4RlZQgUb&open=AZ4HmEuDcb5M4RlZQgUb&pullRequest=3470

Check warning on line 27 in src/main/java/org/mvplugins/multiverse/core/world/key/WorldKeyOrName.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Reorder the modifiers to comply with the Java Language Specification.

See more on https://sonarcloud.io/project/issues?id=Multiverse_Multiverse-Core&issues=AZ4HmEuDcb5M4RlZQgUa&open=AZ4HmEuDcb5M4RlZQgUa&pullRequest=3470

private static final String DEFAULT_OVERWORLD_KEY = "overworld";
private static final String DEFAULT_NETHER_KEY = "the_nether";
Expand Down Expand Up @@ -63,7 +64,7 @@
* @since 5.7
*/
@ApiStatus.AvailableSince("5.7")
public static Attempt<WorldKeyOrName, WorldKeyParseFailReason> parseName(@NonNull String name) {
public static Attempt<WorldKeyOrName, WorldKeyParseFailReason> parseName(@NotNull String name) {
return Try.of(() -> NamespacedKey.minecraft(mapWorldNameToMinecraftKey(name)))
.map(usableKey -> Attempt.<WorldKeyOrName, WorldKeyParseFailReason>success(new Name(name, usableKey)))
.recover(throwable -> Attempt.failure(WorldKeyParseFailReason.INVALID_WORLD_NAME,
Expand All @@ -72,7 +73,7 @@
MessageReplacement.Replace.WORLD.with(name)));
}

private static String mapWorldNameToMinecraftKey(@NonNull String nameOrKey) {
private static String mapWorldNameToMinecraftKey(@NotNull String nameOrKey) {
String defaultLevelName = getMostAccurateLevelName();
String lowerCaseName = nameOrKey.toLowerCase(Locale.ROOT);
if (defaultLevelName.equalsIgnoreCase(lowerCaseName)) {
Expand All @@ -95,7 +96,7 @@
* @since 5.7
*/
@ApiStatus.AvailableSince("5.7")
public static Attempt<WorldKeyOrName, WorldKeyParseFailReason> parseKey(@NonNull String nameOrKey) {
public static Attempt<WorldKeyOrName, WorldKeyParseFailReason> parseKey(@NotNull String nameOrKey) {
return Option.of(NamespacedKey.fromString(nameOrKey))
.filter(Objects::nonNull)
.map(key -> Attempt.<WorldKeyOrName, WorldKeyParseFailReason>success(new Key(key, usableNameFromKey(key))))
Expand All @@ -112,7 +113,7 @@
* @since 5.7
*/
@ApiStatus.AvailableSince("5.7")
public static WorldKeyOrName parseKey(@NonNull NamespacedKey key) {
public static WorldKeyOrName parseKey(@NotNull NamespacedKey key) {
return new Key(key, usableNameFromKey(key));
}

Expand Down Expand Up @@ -237,10 +238,18 @@
* @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
*/
@Override
public int compareTo(@NonNull WorldKeyOrName o) {
public int compareTo(WorldKeyOrName o) {
return serialise().compareTo(o.serialise());
}

/**
* {@inheritDoc}
*/
@Override
public @Nullable Message getLocalizableMessage() {
return Message.of(serialise());
}

public static final class Key extends WorldKeyOrName {

private final NamespacedKey key;
Expand Down
Loading