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..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,8 @@ 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()), new SimpleCommandRegistrar(this.commandNode, this.lock.writeLock()), @@ -182,6 +185,11 @@ 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(this.worldName) + .build(); this.localStateRegistry = LimboProtocol.getLimboStateRegistry(); this.refresh(); @@ -987,7 +995,9 @@ private JoinGamePacket createJoinGamePacket(ProtocolVersion version) { joinGame.setReducedDebugInfo(this.reducedDebugInfo); String key = dimension.getKey(); - joinGame.setDimensionInfo(new DimensionInfo(key, key, false, false, version)); + String registryIdentifier = version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2) + && 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(); @@ -1323,7 +1333,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 +1356,7 @@ private JoinGamePacket createLegacyJoinGamePacket() { } private DefaultSpawnPositionPacket createDefaultSpawnPositionPacket() { - return new DefaultSpawnPositionPacket(this.world.getDimension().getKey(), + 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 46885c9e..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; @@ -44,6 +45,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 +56,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 = Key.key(worldName).asString(); this.defaultBiome = Biome.of(dimension.getDefaultBiome()); this.spawnX = posX; @@ -183,6 +192,12 @@ public Dimension getDimension() { return this.dimension; } + @NonNull + @Override + public String getWorldName() { + return this.worldName; + } + @Override public double getSpawnX() { return this.spawnX;