Make world name matching case-insensitive to follow papermc behvaiour

This commit is contained in:
Ben Woo committed 2025-12-08 17:39:10 +08:00
1 parent 2b41c1367e
commit 13b949c692
4 files changed
+95 -29

No files matched your search

@@ -8,6 +8,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
@@ -344,7 +345,7 @@ public final class WorldManager {
private MultiverseWorld newMultiverseWorld(String worldName, WorldConfig worldConfig) {
MultiverseWorld mvWorld = new MultiverseWorld(worldName, worldConfig, config);
worldsMap.put(mvWorld.getName(), mvWorld);
worldsMap.put(mvWorld.getName().toLowerCase(Locale.ENGLISH), mvWorld);
corePermissions.addWorldPermissions(mvWorld);
return mvWorld;
}
@@ -381,7 +382,7 @@ public final class WorldManager {
locationManipulation,
entityPurger
);
loadedWorldsMap.put(loadedWorld.getName(), loadedWorld);
loadedWorldsMap.put(loadedWorld.getName().toLowerCase(Locale.ENGLISH), loadedWorld);
saveWorldsConfig();
pluginManager.callEvent(new MVWorldLoadedEvent(loadedWorld));
return loadedWorld;
@@ -500,7 +501,7 @@ public final class WorldManager {
locationManipulation,
entityPurger
);
loadedWorldsMap.put(loadedWorld.getName(), loadedWorld);
loadedWorldsMap.put(loadedWorld.getName().toLowerCase(Locale.ENGLISH), loadedWorld);
saveWorldsConfig();
pluginManager.callEvent(new MVWorldLoadedEvent(loadedWorld));
return Attempt.success(loadedWorld);
@@ -967,7 +968,9 @@ public final class WorldManager {
* @return The world if it exists.
*/
public Option<MultiverseWorld> getWorld(@Nullable String worldName) {
return getLoadedWorld(worldName).fold(() -> getUnloadedWorld(worldName), Option::of);
return getLoadedWorld(worldName)
.map(world -> (MultiverseWorld) world)
.orElse(() -> getUnloadedWorld(worldName));
}
/**
@@ -979,7 +982,8 @@ public final class WorldManager {
*/
public Option<MultiverseWorld> getWorldByNameOrAlias(@Nullable String worldNameOrAlias) {
return getLoadedWorldByNameOrAlias(worldNameOrAlias)
.fold(() -> getUnloadedWorldByNameOrAlias(worldNameOrAlias), Option::of);
.map(world -> (MultiverseWorld) world)
.orElse(() -> getUnloadedWorldByNameOrAlias(worldNameOrAlias));
}
/**
@@ -994,7 +998,8 @@ public final class WorldManager {
public Collection<MultiverseWorld> getWorlds() {
return worldsMap.values().stream()
.map(world -> getLoadedWorld(world)
.fold(() -> world, loadedWorld -> loadedWorld))
.map(loadedWorld -> (MultiverseWorld) loadedWorld)
.getOrElse(world))
.toList();
}
@@ -1005,7 +1010,7 @@ public final class WorldManager {
* @return True if the world is a world is known to multiverse, but may or may not be loaded.
*/
public boolean isWorld(@Nullable String worldName) {
return worldsMap.containsKey(worldName);
return worldName != null && worldsMap.containsKey(worldName.toLowerCase(Locale.ENGLISH));
}
/**
@@ -1015,7 +1020,9 @@ public final class WorldManager {
* @return The world if it exists.
*/
public Option<MultiverseWorld> getUnloadedWorld(@Nullable String worldName) {
return isLoadedWorld(worldName) ? Option.none() : Option.of(worldsMap.get(worldName));
return isLoadedWorld(worldName)
? Option.none()
: Option.of(worldName).flatMap(name -> Option.of(worldsMap.get(name.toLowerCase(Locale.ENGLISH))));
}
/**
@@ -1067,7 +1074,7 @@ public final class WorldManager {
* @return The multiverse world if it exists.
*/
public Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable World world) {
return world == null ? Option.none() : Option.of(loadedWorldsMap.get(world.getName()));
return Option.of(world).flatMap(notNullWorld -> getLoadedWorld(notNullWorld.getName()));
}
/**
@@ -1077,7 +1084,7 @@ public final class WorldManager {
* @return The multiverse world if it exists.
*/
public Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable MultiverseWorld world) {
return world == null ? Option.none() : Option.of(loadedWorldsMap.get(world.getName()));
return Option.of(world).flatMap(notNullWorld -> getLoadedWorld(notNullWorld.getName()));
}
/**
@@ -1087,7 +1094,8 @@ public final class WorldManager {
* @return The multiverse world if it exists.
*/
public Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable String worldName) {
return Option.of(loadedWorldsMap.get(worldName));
return Option.of(worldName)
.flatMap(name -> Option.of(loadedWorldsMap.get(name.toLowerCase(Locale.ENGLISH))));
}
/**
@@ -1148,7 +1156,7 @@ public final class WorldManager {
* @return True if the world is a multiverse world that is loaded.
*/
public boolean isLoadedWorld(@Nullable String worldName) {
return loadedWorldsMap.containsKey(worldName);
return worldName != null && loadedWorldsMap.containsKey(worldName.toLowerCase(Locale.ENGLISH));
}
/**
@@ -3,13 +3,13 @@ package org.mvplugins.multiverse.core.world.helpers;
import java.io.File;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Pattern;
import io.vavr.control.Option;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.utils.REPatterns;
/**
* <p>Utility class in helping to check the status of a world name and it's associated world folder.</p>
@@ -20,11 +20,11 @@ import org.jvnet.hk2.annotations.Service;
@Service
public final class WorldNameChecker {
private static final Pattern WORLD_NAME_PATTERN = Pattern.compile("[a-zA-Z0-9/._-]+");
private static final Set<String> BLACKLIST_NAMES = Set.of(
"cache",
"config",
"crash-reports",
"libraries",
"logs",
"plugins",
"versions");
@@ -47,18 +47,21 @@ public final class WorldNameChecker {
*/
@NotNull
public NameStatus checkName(@Nullable String worldName) {
return Option.of(worldName).map(name -> {
if (name.isEmpty()) {
return NameStatus.EMPTY;
}
if (BLACKLIST_NAMES.contains(name)) {
return NameStatus.BLACKLISTED;
}
if (!WORLD_NAME_PATTERN.matcher(name).matches()) {
return NameStatus.INVALID_CHARS;
}
return NameStatus.VALID;
}).getOrElse(NameStatus.EMPTY);
return Option.of(worldName)
.map(name -> name.toLowerCase(Locale.ENGLISH))
.map(name -> {
if (name.isEmpty()) {
return NameStatus.EMPTY;
}
if (BLACKLIST_NAMES.contains(name)) {
return NameStatus.BLACKLISTED;
}
if (!REPatterns.NAMESPACE_KEY.matcher(name).matches()) {
return NameStatus.INVALID_CHARS;
}
return NameStatus.VALID;
})
.getOrElse(NameStatus.EMPTY);
}
/**
@@ -43,7 +43,7 @@ class WorldManagerTest : TestWithMockBukkit() {
@Test
fun `Create world with custom options`() {
assertTrue(worldManager.createWorld(
CreateWorldOptions.worldName("world_nether")
CreateWorldOptions.worldName("Section/my-nether_world")
.environment(World.Environment.NETHER)
.generateStructures(false)
.seed(1234L)
@@ -51,11 +51,11 @@ class WorldManagerTest : TestWithMockBukkit() {
.worldType(WorldType.FLAT)
).isSuccess)
val getWorld = worldManager.getLoadedWorld("world_nether")
val getWorld = worldManager.getLoadedWorld("Section/my-nether_world")
assertTrue(getWorld.isDefined)
val world = getWorld.get()
assertNotNull(world)
assertEquals("world_nether", world.name)
assertEquals("Section/my-nether_world", world.name)
assertEquals(World.Environment.NETHER, world.environment)
assertFalse(world.canGenerateStructures().get())
assertEquals(1234L, world.seed)
@@ -84,6 +84,18 @@ class WorldManagerTest : TestWithMockBukkit() {
)
}
@Test
fun `Create world failed - world exists and loaded (case insensitive)`() {
assertEquals(
CreateFailureReason.WORLD_EXIST_LOADED,
worldManager.createWorld(CreateWorldOptions.worldName("WoRld2")).failureReason
)
assertEquals(
CreateFailureReason.WORLD_EXIST_LOADED,
worldManager.createWorld(CreateWorldOptions.worldName("WORLD2")).failureReason
)
}
@Test
fun `Create world failed - world exists but unloaded`() {
assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world)).isSuccess)
@@ -255,4 +267,46 @@ class WorldManagerTest : TestWithMockBukkit() {
assertEquals(world, worldManager.getWorldByNameOrAlias("testalias").orNull)
assertNull(worldManager.getUnloadedWorldByNameOrAlias("testalias").orNull)
}
@Test
fun `Get world`() {
assertEquals(world, worldManager.getLoadedWorld("world").orNull)
assertNull(worldManager.getUnloadedWorld("world").orNull)
assertEquals(world, worldManager.getWorld("world").orNull)
assertTrue(worldManager.isLoadedWorld("world"))
assertFalse(worldManager.isUnloadedWorld("world"))
assertTrue(worldManager.isWorld("world"))
val unloadedWorld = worldManager.unloadWorld(UnloadWorldOptions.world(world)).get()
assertNull(worldManager.getLoadedWorld("world").orNull)
assertEquals(unloadedWorld, worldManager.getUnloadedWorld("world").orNull)
assertEquals(unloadedWorld, worldManager.getWorld("world").orNull)
assertFalse(worldManager.isLoadedWorld("world"))
assertTrue(worldManager.isUnloadedWorld("world"))
assertTrue(worldManager.isWorld("world"))
}
@Test
fun `Get world is case insensitive`() {
assertEquals(world2, worldManager.getLoadedWorld("WORLD2").orNull)
assertNull(worldManager.getUnloadedWorld("wOrld2").orNull)
assertEquals(world2, worldManager.getWorld("wOrld2").orNull)
assertTrue(worldManager.isLoadedWorld("WoRlD2"))
assertFalse(worldManager.isUnloadedWorld("WoRlD2"))
assertTrue(worldManager.isWorld("wORLD2"))
val unloadedWorld2 = worldManager.unloadWorld(UnloadWorldOptions.world(world2)).get()
assertNull(worldManager.getLoadedWorld("wOrld2").orNull)
assertEquals(unloadedWorld2, worldManager.getUnloadedWorld("wOrld2").orNull)
assertEquals(unloadedWorld2, worldManager.getWorld("wORLD2").orNull)
assertFalse(worldManager.isLoadedWorld("WoRlD2"))
assertTrue(worldManager.isUnloadedWorld("WoRlD2"))
assertTrue(worldManager.isWorld("wORLD2"))
}
}
@@ -38,6 +38,7 @@ class WorldNameCheckerTest : TestWithMockBukkit() {
fun `Blacklisted world name`() {
assertEquals(WorldNameChecker.NameStatus.BLACKLISTED, worldNameChecker.checkName("logs"))
assertEquals(WorldNameChecker.NameStatus.BLACKLISTED, worldNameChecker.checkName("plugins"))
assertEquals(WorldNameChecker.NameStatus.BLACKLISTED, worldNameChecker.checkName("CACHE"))
}
@Test