From df644e90a94afba8af208eaea5bd5a8999729f72 Mon Sep 17 00:00:00 2001 From: AVuTuVA Date: Fri, 10 Jul 2026 11:38:25 +0300 Subject: [PATCH 1/2] Add custom world identifiers --- .../elytrium/limboapi/api/LimboFactory.java | 18 ++++++++++++++++++ .../limboapi/api/chunk/VirtualWorld.java | 10 ++++++++++ .../java/net/elytrium/limboapi/LimboAPI.java | 6 ++++++ .../elytrium/limboapi/server/LimboImpl.java | 14 +++++++++++--- .../limboapi/server/world/SimpleWorld.java | 14 ++++++++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/net/elytrium/limboapi/api/LimboFactory.java b/api/src/main/java/net/elytrium/limboapi/api/LimboFactory.java index 2d2e9373..1c21dee6 100644 --- a/api/src/main/java/net/elytrium/limboapi/api/LimboFactory.java +++ b/api/src/main/java/net/elytrium/limboapi/api/LimboFactory.java @@ -118,6 +118,24 @@ public interface LimboFactory { */ VirtualWorld createVirtualWorld(Dimension dimension, double posX, double posY, double posZ, float yaw, float pitch); + /** + * Creates a new virtual world with a custom identifier. + * + * @param dimension World dimension type. + * @param worldName Namespaced identifier used to distinguish the world on modern clients. + * @param posX Spawn location. (X) + * @param posY Spawn location. (Y) + * @param posZ Spawn location. (Z) + * @param yaw Spawn rotation. (Yaw) + * @param pitch Spawn rotation. (Pitch) + * + * @return new virtual world. + */ + default VirtualWorld createVirtualWorld(Dimension dimension, String worldName, + double posX, double posY, double posZ, float yaw, float pitch) { + throw new UnsupportedOperationException("Custom world names are not supported by this LimboFactory"); + } + /** * Creates new virtual chunk with plain biomes set as default. * You need to provide the chunk location, you can get it using {@code blockCoordinate >> 4}. diff --git a/api/src/main/java/net/elytrium/limboapi/api/chunk/VirtualWorld.java b/api/src/main/java/net/elytrium/limboapi/api/chunk/VirtualWorld.java index ccf1d040..d6ea7740 100644 --- a/api/src/main/java/net/elytrium/limboapi/api/chunk/VirtualWorld.java +++ b/api/src/main/java/net/elytrium/limboapi/api/chunk/VirtualWorld.java @@ -46,6 +46,16 @@ public interface VirtualWorld { @NonNull Dimension getDimension(); + /** + * Returns the identifier used to distinguish this world from other worlds on modern clients. + * + * @return the namespaced world identifier. + */ + @NonNull + default String getWorldName() { + return this.getDimension().getKey(); + } + double getSpawnX(); double getSpawnY(); diff --git a/plugin/src/main/java/net/elytrium/limboapi/LimboAPI.java b/plugin/src/main/java/net/elytrium/limboapi/LimboAPI.java index 1feb146f..3faffc13 100644 --- a/plugin/src/main/java/net/elytrium/limboapi/LimboAPI.java +++ b/plugin/src/main/java/net/elytrium/limboapi/LimboAPI.java @@ -380,6 +380,12 @@ public VirtualWorld createVirtualWorld(Dimension dimension, double posX, double return new SimpleWorld(dimension, posX, posY, posZ, yaw, pitch); } + @Override + public VirtualWorld createVirtualWorld(Dimension dimension, String worldName, + double posX, double posY, double posZ, float yaw, float pitch) { + return new SimpleWorld(dimension, worldName, posX, posY, posZ, yaw, pitch); + } + @Override public VirtualChunk createVirtualChunk(int posX, int posZ) { return new SimpleChunk(posX, posZ); diff --git a/plugin/src/main/java/net/elytrium/limboapi/server/LimboImpl.java b/plugin/src/main/java/net/elytrium/limboapi/server/LimboImpl.java index 24e1566e..26adaa98 100644 --- a/plugin/src/main/java/net/elytrium/limboapi/server/LimboImpl.java +++ b/plugin/src/main/java/net/elytrium/limboapi/server/LimboImpl.java @@ -148,6 +148,7 @@ public class LimboImpl implements Limbo { private final RootCommandNode commandNode = new RootCommandNode<>(); private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final List queuedToRelease = new ArrayList<>(); + private final ImmutableSet levelNames; private final List> registrars = ImmutableList.of( new BrigadierCommandRegistrar(this.commandNode, this.lock.writeLock()), new SimpleCommandRegistrar(this.commandNode, this.lock.writeLock()), @@ -182,6 +183,10 @@ public class LimboImpl implements Limbo { public LimboImpl(LimboAPI plugin, VirtualWorld world) { this.plugin = plugin; this.world = world; + this.levelNames = ImmutableSet.builder() + .addAll(LEVELS) + .add(world.getWorldName()) + .build(); this.localStateRegistry = LimboProtocol.getLimboStateRegistry(); this.refresh(); @@ -987,7 +992,10 @@ private JoinGamePacket createJoinGamePacket(ProtocolVersion version) { joinGame.setReducedDebugInfo(this.reducedDebugInfo); String key = dimension.getKey(); - joinGame.setDimensionInfo(new DimensionInfo(key, key, false, false, version)); + String worldName = this.world.getWorldName(); + String registryIdentifier = version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2) + && version.lessThan(ProtocolVersion.MINECRAFT_1_19) ? worldName : key; + joinGame.setDimensionInfo(new DimensionInfo(registryIdentifier, worldName, false, false, version)); joinGame.setEnforcesSecureChat(Settings.IMP.MAIN.SEND_ENFORCE_SECURE_CHAT); CompoundBinaryTag.Builder registryContainer = CompoundBinaryTag.builder(); @@ -1323,7 +1331,7 @@ private JoinGamePacket createJoinGamePacket(ProtocolVersion version) { } CURRENT_DIMENSION_DATA_FIELD.invokeExact(joinGame, currentDimensionData); - LEVEL_NAMES_FIELDS.invokeExact(joinGame, LEVELS); + LEVEL_NAMES_FIELDS.invokeExact(joinGame, this.levelNames); REGISTRY_FIELD.invokeExact(joinGame, registryContainer.build()); } catch (Throwable e) { throw new ReflectionException(e); @@ -1346,7 +1354,7 @@ private JoinGamePacket createLegacyJoinGamePacket() { } private DefaultSpawnPositionPacket createDefaultSpawnPositionPacket() { - return new DefaultSpawnPositionPacket(this.world.getDimension().getKey(), + return new DefaultSpawnPositionPacket(this.world.getWorldName(), (int) this.world.getSpawnX(), (int) this.world.getSpawnY(), (int) this.world.getSpawnZ(), 0.0F, 0.0f); } diff --git a/plugin/src/main/java/net/elytrium/limboapi/server/world/SimpleWorld.java b/plugin/src/main/java/net/elytrium/limboapi/server/world/SimpleWorld.java index 46885c9e..47a009f8 100644 --- a/plugin/src/main/java/net/elytrium/limboapi/server/world/SimpleWorld.java +++ b/plugin/src/main/java/net/elytrium/limboapi/server/world/SimpleWorld.java @@ -44,6 +44,8 @@ public class SimpleWorld implements VirtualWorld { private final List> distanceChunkMap = new ArrayList<>(); @NonNull private final Dimension dimension; + @NonNull + private final String worldName; private final VirtualBiome defaultBiome; private final double spawnX; @@ -53,7 +55,13 @@ public class SimpleWorld implements VirtualWorld { private final float pitch; public SimpleWorld(@NonNull Dimension dimension, double posX, double posY, double posZ, float yaw, float pitch) { + this(dimension, dimension.getKey(), posX, posY, posZ, yaw, pitch); + } + + public SimpleWorld(@NonNull Dimension dimension, @NonNull String worldName, + double posX, double posY, double posZ, float yaw, float pitch) { this.dimension = dimension; + this.worldName = worldName; this.defaultBiome = Biome.of(dimension.getDefaultBiome()); this.spawnX = posX; @@ -183,6 +191,12 @@ public Dimension getDimension() { return this.dimension; } + @NonNull + @Override + public String getWorldName() { + return this.worldName; + } + @Override public double getSpawnX() { return this.spawnX; From 84fede1f18d666d2b2466b7ec777d456c426fd05 Mon Sep 17 00:00:00 2001 From: AVuTuVA Date: Sat, 11 Jul 2026 12:52:56 +0300 Subject: [PATCH 2/2] validate and cache custom world identifiers --- .../java/net/elytrium/limboapi/server/LimboImpl.java | 12 +++++++----- .../elytrium/limboapi/server/world/SimpleWorld.java | 3 ++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/plugin/src/main/java/net/elytrium/limboapi/server/LimboImpl.java b/plugin/src/main/java/net/elytrium/limboapi/server/LimboImpl.java index 26adaa98..f77028b3 100644 --- a/plugin/src/main/java/net/elytrium/limboapi/server/LimboImpl.java +++ b/plugin/src/main/java/net/elytrium/limboapi/server/LimboImpl.java @@ -112,6 +112,7 @@ import net.elytrium.limboapi.protocol.packets.s2c.TimeUpdatePacket; import net.elytrium.limboapi.protocol.packets.s2c.UpdateViewPositionPacket; import net.elytrium.limboapi.server.world.SimpleTagManager; +import net.kyori.adventure.key.Key; import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.BinaryTagIO; import net.kyori.adventure.nbt.BinaryTagTypes; @@ -148,6 +149,7 @@ public class LimboImpl implements Limbo { private final RootCommandNode commandNode = new RootCommandNode<>(); private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final List queuedToRelease = new ArrayList<>(); + private final String worldName; private final ImmutableSet levelNames; private final List> registrars = ImmutableList.of( new BrigadierCommandRegistrar(this.commandNode, this.lock.writeLock()), @@ -183,9 +185,10 @@ public class LimboImpl implements Limbo { public LimboImpl(LimboAPI plugin, VirtualWorld world) { this.plugin = plugin; this.world = world; + this.worldName = Key.key(world.getWorldName()).asString(); this.levelNames = ImmutableSet.builder() .addAll(LEVELS) - .add(world.getWorldName()) + .add(this.worldName) .build(); this.localStateRegistry = LimboProtocol.getLimboStateRegistry(); @@ -992,10 +995,9 @@ private JoinGamePacket createJoinGamePacket(ProtocolVersion version) { joinGame.setReducedDebugInfo(this.reducedDebugInfo); String key = dimension.getKey(); - String worldName = this.world.getWorldName(); String registryIdentifier = version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2) - && version.lessThan(ProtocolVersion.MINECRAFT_1_19) ? worldName : key; - joinGame.setDimensionInfo(new DimensionInfo(registryIdentifier, worldName, false, false, version)); + && version.lessThan(ProtocolVersion.MINECRAFT_1_19) ? this.worldName : key; + joinGame.setDimensionInfo(new DimensionInfo(registryIdentifier, this.worldName, false, false, version)); joinGame.setEnforcesSecureChat(Settings.IMP.MAIN.SEND_ENFORCE_SECURE_CHAT); CompoundBinaryTag.Builder registryContainer = CompoundBinaryTag.builder(); @@ -1354,7 +1356,7 @@ private JoinGamePacket createLegacyJoinGamePacket() { } private DefaultSpawnPositionPacket createDefaultSpawnPositionPacket() { - return new DefaultSpawnPositionPacket(this.world.getWorldName(), + return new DefaultSpawnPositionPacket(this.worldName, (int) this.world.getSpawnX(), (int) this.world.getSpawnY(), (int) this.world.getSpawnZ(), 0.0F, 0.0f); } diff --git a/plugin/src/main/java/net/elytrium/limboapi/server/world/SimpleWorld.java b/plugin/src/main/java/net/elytrium/limboapi/server/world/SimpleWorld.java index 47a009f8..b287855d 100644 --- a/plugin/src/main/java/net/elytrium/limboapi/server/world/SimpleWorld.java +++ b/plugin/src/main/java/net/elytrium/limboapi/server/world/SimpleWorld.java @@ -34,6 +34,7 @@ import net.elytrium.limboapi.api.chunk.VirtualWorld; import net.elytrium.limboapi.material.Biome; import net.elytrium.limboapi.server.world.chunk.SimpleChunk; +import net.kyori.adventure.key.Key; import net.kyori.adventure.nbt.CompoundBinaryTag; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -61,7 +62,7 @@ public SimpleWorld(@NonNull Dimension dimension, double posX, double posY, doubl public SimpleWorld(@NonNull Dimension dimension, @NonNull String worldName, double posX, double posY, double posZ, float yaw, float pitch) { this.dimension = dimension; - this.worldName = worldName; + this.worldName = Key.key(worldName).asString(); this.defaultBiome = Biome.of(dimension.getDefaultBiome()); this.spawnX = posX;