diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java index 17aaa6188e..d71f8bacfb 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -758,6 +758,18 @@ public static World getWorld(@NotNull String name) { public static World getWorld(@NotNull UUID uid) { return server.getWorld(uid); } + // Paper start + /** + * Gets the world from the given NamespacedKey + * + * @param worldKey the NamespacedKey of the world to retrieve + * @return a world with the given NamespacedKey, or null if none exists + */ + @Nullable + public static World getWorld(@NotNull NamespacedKey worldKey) { + return server.getWorld(worldKey); + } + // Paper end /** * Create a new virtual {@link WorldBorder}. diff --git a/src/main/java/org/bukkit/RegionAccessor.java b/src/main/java/org/bukkit/RegionAccessor.java index b7e821e2e6..ca1803b451 100644 --- a/src/main/java/org/bukkit/RegionAccessor.java +++ b/src/main/java/org/bukkit/RegionAccessor.java @@ -19,7 +19,7 @@ * A RegionAccessor gives access to getting, modifying and spawning {@link Biome}, {@link BlockState} and {@link Entity}, * as well as generating some basic structures. */ -public interface RegionAccessor { +public interface RegionAccessor extends Keyed { // Paper /** * Gets the {@link Biome} at the given {@link Location}. @@ -397,4 +397,15 @@ public interface RegionAccessor { * {@link HeightMap} */ public int getHighestBlockYAt(@NotNull Location location, @NotNull HeightMap heightMap); + + // Paper start + /** + * Get the world's key + * + * @return the world's key + */ + @NotNull + @Override + NamespacedKey getKey(); + // Paper end } diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java index 98f107a064..537a5ef802 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java @@ -657,6 +657,17 @@ public interface Server extends PluginMessageRecipient { @Nullable public World getWorld(@NotNull UUID uid); + // Paper start + /** + * Gets the world from the given NamespacedKey + * + * @param worldKey the NamespacedKey of the world to retrieve + * @return a world with the given NamespacedKey, or null if none exists + */ + @Nullable + public World getWorld(@NotNull NamespacedKey worldKey); + // Paper end + /** * Create a new virtual {@link WorldBorder}. *
diff --git a/src/main/java/org/bukkit/WorldCreator.java b/src/main/java/org/bukkit/WorldCreator.java index daf3ebf75e..2f1a89cf2c 100644 --- a/src/main/java/org/bukkit/WorldCreator.java +++ b/src/main/java/org/bukkit/WorldCreator.java @@ -13,6 +13,7 @@ * Represents various types of options that may be used to create a world. */ public class WorldCreator { + private final NamespacedKey key; // Paper private final String name; private long seed; private World.Environment environment = World.Environment.NORMAL; @@ -30,13 +31,80 @@ public class WorldCreator { * @param name Name of the world that will be created */ public WorldCreator(@NotNull String name) { - if (name == null) { - throw new IllegalArgumentException("World name cannot be null"); + // Paper start + this(name, getWorldKey(name)); + } + + private static NamespacedKey getWorldKey(String name) { + final String mainLevelName = Bukkit.getUnsafe().getMainLevelName(); + if (name.equals(mainLevelName)) { + return NamespacedKey.minecraft("overworld"); + } else if (name.equals(mainLevelName + "_nether")) { + return NamespacedKey.minecraft("the_nether"); + } else if (name.equals(mainLevelName + "_the_end")) { + return NamespacedKey.minecraft("the_end"); + } else { + return NamespacedKey.minecraft(name.toLowerCase(java.util.Locale.ENGLISH).replace(" ", "_")); } + } - this.name = name; + /** + * Creates an empty WorldCreator for the given world name and key + * + * @param levelName LevelName of the world that will be created + * @param worldKey NamespacedKey of the world that will be created + */ + public WorldCreator(@NotNull String levelName, @NotNull NamespacedKey worldKey) { + if (levelName == null || worldKey == null) { + throw new IllegalArgumentException("World name and key cannot be null"); + } + this.name = levelName; this.seed = (new Random()).nextLong(); + this.key = worldKey; + } + + /** + * Creates an empty WorldCreator for the given key. + * LevelName will be the Key part of the NamespacedKey. + * + * @param worldKey NamespacedKey of the world that will be created + */ + public WorldCreator(@NotNull NamespacedKey worldKey) { + this(worldKey.getKey(), worldKey); + } + + /** + * Gets the key for this WorldCreator + * + * @return the key + */ + @NotNull + public NamespacedKey key() { + return key; + } + + /** + * Creates an empty WorldCreator for the given world name and key + * + * @param levelName LevelName of the world that will be created + * @param worldKey NamespacedKey of the world that will be created + */ + @NotNull + public static WorldCreator ofNameAndKey(@NotNull String levelName, @NotNull NamespacedKey worldKey) { + return new WorldCreator(levelName, worldKey); + } + + /** + * Creates an empty WorldCreator for the given key. + * LevelName will be the Key part of the NamespacedKey. + * + * @param worldKey NamespacedKey of the world that will be created + */ + @NotNull + public static WorldCreator ofKey(@NotNull NamespacedKey worldKey) { + return new WorldCreator(worldKey); } + // Paper end /** * Copies the options from the specified world diff --git a/src/main/java/org/bukkit/craftbukkit/v1_20_R1/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/v1_20_R1/CraftServer.java index 8c7315ad66..5037818730 100644 --- a/src/main/java/org/bukkit/craftbukkit/v1_20_R1/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/v1_20_R1/CraftServer.java @@ -1038,6 +1038,16 @@ public World createWorld(WorldCreator creator) { return world; } + // Paper start + World worldByKey = this.getWorld(creator.key()); + if (world != null || worldByKey != null) { + if (world == worldByKey) { + return world; + } + throw new IllegalArgumentException("Cannot create a world with key " + creator.key() + " and name " + name + " one (or both) already match a world that exists"); + } + // Paper end + if ((folder.exists()) && (!folder.isDirectory())) { throw new IllegalArgumentException("File exists with the name '" + name + "' and isn't a folder"); } @@ -1112,7 +1122,7 @@ public World createWorld(WorldCreator creator) { } else if (name.equals(levelName + "_the_end")) { worldKey = net.minecraft.world.level.Level.END; } else { - worldKey = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(name.toLowerCase(java.util.Locale.ENGLISH))); + worldKey = ResourceKey.create(Registries.DIMENSION, new net.minecraft.resources.ResourceLocation(creator.key().getNamespace().toLowerCase(java.util.Locale.ENGLISH), creator.key().getKey().toLowerCase(java.util.Locale.ENGLISH))); // Paper } net.minecraft.world.level.Level.craftWorldData(generator, creator.environment(), biomeProvider); @@ -1223,6 +1233,15 @@ public World getWorld(UUID uid) { return null; } + // Paper start + @Override + public World getWorld(NamespacedKey worldKey) { + ServerLevel worldServer = console.getLevel(ResourceKey.create(net.minecraft.core.registries.Registries.DIMENSION, CraftNamespacedKey.toMinecraft(worldKey))); + if (worldServer == null) return null; + return worldServer.getWorld(); + } + // Paper end + public void addWorld(World world) { // Check if a World already exists with the UID. if (getWorld(world.getUID()) != null) {