diff --git a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java index 07b35926b..3f86aced0 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -35,7 +35,7 @@ public enum ProtocolVersion { MINECRAFT_1_15(573, "1.15"), MINECRAFT_1_15_1(575, "1.15.1"), MINECRAFT_1_15_2(578, "1.15.2"), - MINECRAFT_1_16(718, "1.16"); + MINECRAFT_1_16(721, "1.16"); private final int protocol; private final String name; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/DimensionInfo.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/DimensionInfo.java index 1a5a5245b..803f011a1 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/DimensionInfo.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/DimensionInfo.java @@ -1,5 +1,7 @@ package com.velocitypowered.proxy.protocol; +import com.velocitypowered.proxy.connection.MinecraftConnection; + import javax.annotation.Nonnull; public class DimensionInfo { @@ -9,6 +11,13 @@ public class DimensionInfo { private final boolean isFlat; private final boolean isDebugType; + /** + * Initializes a new {@link DimensionInfo} instance. + * @param dimensionIdentifier the identifier for the dimension from the registry + * @param dimensionLevelName the level name as displayed in the F3 menu and logs + * @param isFlat if true will set world lighting below surface-level to not display fog + * @param isDebugType if true constrains the world to the very limited debug-type world + */ public DimensionInfo(@Nonnull String dimensionIdentifier, @Nonnull String dimensionLevelName, boolean isFlat, boolean isDebugType) { if (dimensionIdentifier == null || dimensionIdentifier.isEmpty() diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/DimensionRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/DimensionRegistry.java index 70ea76e26..2c917d29f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/DimensionRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/DimensionRegistry.java @@ -12,15 +12,26 @@ import net.kyori.nbt.TagType; public class DimensionRegistry { + // Mapping: + // dimensionIdentifier (Client connection refers to this), + // dimensionType (The game refers to this). private final @Nonnull Map dimensionRegistry; private final @Nonnull Set worldNames; + /** + * Initializes a new {@link DimensionRegistry} instance. + * This registry is required for 1.16+ clients/servers to communicate, + * it constrains the dimension types and names the client can be sent + * in a Respawn action (dimension change). + * @param dimensionRegistry a populated map containing dimensionIdentifier and dimensionType sets + * @param worldNames a populated {@link Set} of the dimension level names the server offers + */ public DimensionRegistry(Map dimensionRegistry, Set worldNames) { if (dimensionRegistry == null || dimensionRegistry.isEmpty() || worldNames == null || worldNames.isEmpty()) { throw new IllegalArgumentException( - "DimensionRegistry requires valid arguments, not null and not empty"); + "Dimension registry requires valid arguments, not null and not empty"); } this.dimensionRegistry = dimensionRegistry; this.worldNames = worldNames; @@ -34,45 +45,64 @@ public class DimensionRegistry { return worldNames; } - public @Nonnull String getDimensionIdentifier(@Nonnull String dimensionName) { - if (dimensionName == null) { - throw new IllegalArgumentException("DimensionName cannot be null!"); + /** + * Returns the internal dimension type as used by the game. + * @param dimensionIdentifier how the type is identified by the connection + * @return game internal dimension type + */ + public @Nonnull String getDimensionType(@Nonnull String dimensionIdentifier) { + if (dimensionIdentifier == null) { + throw new IllegalArgumentException("Dimension identifier cannot be null!"); } - if (dimensionName == null || !dimensionRegistry.containsKey(dimensionName)) { - throw new NoSuchElementException("DimensionName " + dimensionName + if (dimensionIdentifier == null || !dimensionRegistry.containsKey(dimensionIdentifier)) { + throw new NoSuchElementException("Dimension with identifier " + dimensionIdentifier + " doesn't exist in this Registry!"); } - return dimensionRegistry.get(dimensionName); + return dimensionRegistry.get(dimensionIdentifier); } - public @Nonnull String getDimensionName(@Nonnull String dimensionIdentifier) { - if (dimensionIdentifier == null) { - throw new IllegalArgumentException("DimensionIdentifier cannot be null!"); + /** + * Returns the dimension identifier as used by the client. + * @param dimensionType the internal dimension type + * @return game dimension identifier + */ + public @Nonnull String getDimensionIdentifier(@Nonnull String dimensionType) { + if (dimensionType == null) { + throw new IllegalArgumentException("Dimension type cannot be null!"); } for (Map.Entry entry : dimensionRegistry.entrySet()) { - if (entry.getValue().equals(dimensionIdentifier)) { + if (entry.getValue().equals(dimensionType)) { return entry.getKey(); } } - throw new NoSuchElementException("DimensionIdentifier " + dimensionIdentifier + throw new NoSuchElementException("Dimension type " + dimensionType + " doesn't exist in this Registry!"); } + /** + * Checks a {@link DimensionInfo} against this registry. + * @param toValidate the {@link DimensionInfo} to validate + * @return true: the dimension information is valid for this registry + */ public boolean isValidFor(@Nonnull DimensionInfo toValidate) { if (toValidate == null) { - throw new IllegalArgumentException("DimensionInfo cannot be null"); + throw new IllegalArgumentException("Dimension info cannot be null"); } try { if (!worldNames.contains(toValidate.getDimensionLevelName())) { return false; } - getDimensionName(toValidate.getDimensionIdentifier()); + getDimensionType(toValidate.getDimensionIdentifier()); return true; } catch (NoSuchElementException thrown) { return false; } } + /** + * Encodes the stored Dimension registry as CompoundTag. + * @return the CompoundTag containing identifier:type mappings + */ public CompoundTag encodeToCompoundTag() { CompoundTag ret = new CompoundTag(); ListTag list = new ListTag(TagType.COMPOUND); @@ -86,6 +116,10 @@ public class DimensionRegistry { return ret; } + /** + * Decodes a CompoundTag storing dimension mappings to a Map identifier:type. + * @param toParse CompoundTag containing a dimension registry + */ public static Map parseToMapping(@Nonnull CompoundTag toParse) { if (toParse == null) { throw new IllegalArgumentException("CompoundTag cannot be null"); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index 07f174b51..658c818d7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -126,44 +126,52 @@ public enum StateRegistry { clientbound.register(BossBar.class, BossBar::new, map(0x0C, MINECRAFT_1_9, false), - map(0x0D, MINECRAFT_1_15, false)); + map(0x0D, MINECRAFT_1_15, false), + map(0x0C, MINECRAFT_1_16, false)); clientbound.register(Chat.class, Chat::new, map(0x02, MINECRAFT_1_8, true), map(0x0F, MINECRAFT_1_9, true), map(0x0E, MINECRAFT_1_13, true), - map(0x0F, MINECRAFT_1_15, true)); + map(0x0F, MINECRAFT_1_15, true), + map(0x0E, MINECRAFT_1_16, true)); clientbound.register(TabCompleteResponse.class, TabCompleteResponse::new, map(0x3A, MINECRAFT_1_8, false), map(0x0E, MINECRAFT_1_9, false), map(0x10, MINECRAFT_1_13, false), - map(0x11, MINECRAFT_1_15, false)); + map(0x11, MINECRAFT_1_15, false), + map(0x10, MINECRAFT_1_16, false)); clientbound.register(AvailableCommands.class, AvailableCommands::new, map(0x11, MINECRAFT_1_13, false), - map(0x12, MINECRAFT_1_15, false)); + map(0x12, MINECRAFT_1_15, false), + map(0x11, MINECRAFT_1_16, false)); clientbound.register(PluginMessage.class, PluginMessage::new, map(0x3F, MINECRAFT_1_8, false), map(0x18, MINECRAFT_1_9, false), map(0x19, MINECRAFT_1_13, false), map(0x18, MINECRAFT_1_14, false), - map(0x19, MINECRAFT_1_15, false)); + map(0x19, MINECRAFT_1_15, false), + map(0x18, MINECRAFT_1_16, false)); clientbound.register(Disconnect.class, Disconnect::new, map(0x40, MINECRAFT_1_8, false), map(0x1A, MINECRAFT_1_9, false), map(0x1B, MINECRAFT_1_13, false), map(0x1A, MINECRAFT_1_14, false), - map(0x1B, MINECRAFT_1_15, false)); + map(0x1B, MINECRAFT_1_15, false), + map(0x1A, MINECRAFT_1_16, false)); clientbound.register(KeepAlive.class, KeepAlive::new, map(0x00, MINECRAFT_1_8, false), map(0x1F, MINECRAFT_1_9, false), map(0x21, MINECRAFT_1_13, false), map(0x20, MINECRAFT_1_14, false), - map(0x21, MINECRAFT_1_15, false)); + map(0x21, MINECRAFT_1_15, false), + map(0x20, MINECRAFT_1_16, false)); clientbound.register(JoinGame.class, JoinGame::new, map(0x01, MINECRAFT_1_8, false), map(0x23, MINECRAFT_1_9, false), map(0x25, MINECRAFT_1_13, false), map(0x25, MINECRAFT_1_14, false), - map(0x26, MINECRAFT_1_15, false)); + map(0x26, MINECRAFT_1_15, false), + map(0x25, MINECRAFT_1_16, false)); clientbound.register(Respawn.class, Respawn::new, map(0x07, MINECRAFT_1_8, true), map(0x33, MINECRAFT_1_9, true), @@ -171,7 +179,8 @@ public enum StateRegistry { map(0x35, MINECRAFT_1_12_1, true), map(0x38, MINECRAFT_1_13, true), map(0x3A, MINECRAFT_1_14, true), - map(0x3B, MINECRAFT_1_15, true)); + map(0x3B, MINECRAFT_1_15, true), + map(0x3A, MINECRAFT_1_16, true)); clientbound.register(ResourcePackRequest.class, ResourcePackRequest::new, map(0x48, MINECRAFT_1_8, true), map(0x32, MINECRAFT_1_9, true), @@ -179,7 +188,8 @@ public enum StateRegistry { map(0x34, MINECRAFT_1_12_1, true), map(0x37, MINECRAFT_1_13, true), map(0x39, MINECRAFT_1_14, true), - map(0x3A, MINECRAFT_1_15, true)); + map(0x3A, MINECRAFT_1_15, true), + map(0x39, MINECRAFT_1_16, true)); clientbound.register(HeaderAndFooter.class, HeaderAndFooter::new, map(0x47, MINECRAFT_1_8, true), map(0x48, MINECRAFT_1_9, true), @@ -188,7 +198,8 @@ public enum StateRegistry { map(0x4A, MINECRAFT_1_12_1, true), map(0x4E, MINECRAFT_1_13, true), map(0x53, MINECRAFT_1_14, true), - map(0x54, MINECRAFT_1_15, true)); + map(0x54, MINECRAFT_1_15, true), + map(0x53, MINECRAFT_1_16, true)); clientbound.register(TitlePacket.class, TitlePacket::new, map(0x45, MINECRAFT_1_8, true), map(0x45, MINECRAFT_1_9, true), @@ -196,14 +207,16 @@ public enum StateRegistry { map(0x48, MINECRAFT_1_12_1, true), map(0x4B, MINECRAFT_1_13, true), map(0x4F, MINECRAFT_1_14, true), - map(0x50, MINECRAFT_1_15, true)); + map(0x50, MINECRAFT_1_15, true), + map(0x4F, MINECRAFT_1_16, true)); clientbound.register(PlayerListItem.class, PlayerListItem::new, map(0x38, MINECRAFT_1_8, false), map(0x2D, MINECRAFT_1_9, false), map(0x2E, MINECRAFT_1_12_1, false), map(0x30, MINECRAFT_1_13, false), map(0x33, MINECRAFT_1_14, false), - map(0x34, MINECRAFT_1_15, false)); + map(0x34, MINECRAFT_1_15, false), + map(0x33, MINECRAFT_1_16, false)); } }, LOGIN {