diff --git a/proxy/src/main/java/com/velocitypowered/proxy/adventure/VelocityBossBarImplementation.java b/proxy/src/main/java/com/velocitypowered/proxy/adventure/VelocityBossBarImplementation.java index e2451188ad..a6acc6484d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/adventure/VelocityBossBarImplementation.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/adventure/VelocityBossBarImplementation.java @@ -69,7 +69,7 @@ public void createDirect(final ConnectedPlayer viewer) { public boolean viewerRemove(final ConnectedPlayer viewer) { if (this.viewers.remove(viewer)) { - viewer.getBossBarManager().remove(this, BossBarPacket.createRemovePacket(this.id, this.bar)); + viewer.getBossBarManager().remove(this, BossBarPacket.createRemovePacket(this.id)); return true; } return false; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java index 6b233b8745..bfa13597ed 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java @@ -208,19 +208,14 @@ public int run(final CommandContext context) { return Command.SINGLE_SUCCESS; } - final TextComponent.Builder listBuilder = Component.text(); - for (int i = 0; i < pluginCount; i++) { - final PluginContainer plugin = plugins.get(i); - listBuilder.append(componentForPlugin(plugin.getDescription())); - if (i + 1 < pluginCount) { - listBuilder.append(Component.text(", ")); - } - } + final Component pluginListComponents = plugins.stream() + .map(container -> componentForPlugin(container.getDescription())) + .collect(Component.toComponent(Component.text(", "))); final TranslatableComponent output = Component.translatable() .key("velocity.command.plugins-list") .color(NamedTextColor.YELLOW) - .arguments(listBuilder.build()) + .arguments(pluginListComponents) .build(); source.sendMessage(output); return Command.SINGLE_SUCCESS; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java index ed30057a40..f6fb8a9734 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java @@ -158,7 +158,7 @@ public boolean handle(StartUpdatePacket packet) { @Override public boolean handle(KeepAlivePacket packet) { - serverConn.getPendingPings().put(packet.getRandomId(), System.nanoTime()); + serverConn.getPendingPings().put(packet.randomId(), System.nanoTime()); return false; // forwards on } @@ -178,10 +178,10 @@ public boolean handle(DisconnectPacket packet) { @Override public boolean handle(BossBarPacket packet) { if (serverConn.getPlayer().getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) { - if (packet.getAction() == BossBarPacket.ADD) { - playerSessionHandler.getServerBossBars().add(packet.getUuid()); - } else if (packet.getAction() == BossBarPacket.REMOVE) { - playerSessionHandler.getServerBossBars().remove(packet.getUuid()); + if (packet.action() == BossBarPacket.ADD) { + playerSessionHandler.getServerBossBars().add(packet.uuid()); + } else if (packet.action() == BossBarPacket.REMOVE) { + playerSessionHandler.getServerBossBars().remove(packet.uuid()); } } return false; // forward @@ -190,13 +190,13 @@ public boolean handle(BossBarPacket packet) { @Override public boolean handle(final ResourcePackRequestPacket packet) { final ResourcePackInfo.Builder builder = new VelocityResourcePackInfo.BuilderImpl( - Preconditions.checkNotNull(packet.getUrl())) - .setId(packet.getId()) - .setPrompt(packet.getPrompt() == null ? null : packet.getPrompt().getComponent()) + Preconditions.checkNotNull(packet.url())) + .setId(packet.id()) + .setPrompt(packet.prompt() == null ? null : packet.prompt().getComponent()) .setShouldForce(packet.isRequired()) .setOrigin(ResourcePackInfo.Origin.DOWNSTREAM_SERVER); - final String hash = packet.getHash(); + final String hash = packet.hash(); if (hash != null && !hash.isEmpty()) { if (PLAUSIBLE_SHA1_HASH.matcher(hash).matches()) { builder.setHash(ByteBufUtil.decodeHexDump(hash)); @@ -222,14 +222,14 @@ public boolean handle(final ResourcePackRequestPacket packet) { // Do not apply a resource pack that has already been applied if (serverConn.getConnection() != null) { serverConn.getConnection().write(new ResourcePackResponsePacket( - packet.getId(), packet.getHash(), PlayerResourcePackStatusEvent.Status.ACCEPTED)); + packet.id(), packet.hash(), PlayerResourcePackStatusEvent.Status.ACCEPTED)); if (serverConn.getConnection().getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { serverConn.getConnection().write(new ResourcePackResponsePacket( - packet.getId(), packet.getHash(), + packet.id(), packet.hash(), PlayerResourcePackStatusEvent.Status.DOWNLOADED)); } serverConn.getConnection().write(new ResourcePackResponsePacket( - packet.getId(), packet.getHash(), + packet.id(), packet.hash(), PlayerResourcePackStatusEvent.Status.SUCCESSFUL)); } if (modifiedPack) { @@ -241,16 +241,16 @@ public boolean handle(final ResourcePackRequestPacket packet) { } } else if (serverConn.getConnection() != null) { serverConn.getConnection().write(new ResourcePackResponsePacket( - packet.getId(), - packet.getHash(), + packet.id(), + packet.hash(), PlayerResourcePackStatusEvent.Status.DECLINED )); } }, playerConnection.eventLoop()).exceptionally((ex) -> { if (serverConn.getConnection() != null) { serverConn.getConnection().write(new ResourcePackResponsePacket( - packet.getId(), - packet.getHash(), + packet.id(), + packet.hash(), PlayerResourcePackStatusEvent.Status.DECLINED )); } @@ -420,7 +420,7 @@ public boolean handle(TransferPacket packet) { @Override public boolean handle(ClientboundStoreCookiePacket packet) { server.getEventManager() - .fire(new CookieStoreEvent(serverConn.getPlayer(), packet.getKey(), packet.getPayload())) + .fire(new CookieStoreEvent(serverConn.getPlayer(), packet.key(), packet.payload())) .thenAcceptAsync(event -> { if (event.getResult().isAllowed()) { final Key resultedKey = event.getResult().getKey() == null diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java index 90bb1a3014..709f43982d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java @@ -141,7 +141,7 @@ public boolean handle(ClientboundServerLinksPacket packet) { @Override public boolean handle(KeepAlivePacket packet) { - serverConn.getPendingPings().put(packet.getRandomId(), System.nanoTime()); + serverConn.getPendingPings().put(packet.randomId(), System.nanoTime()); serverConn.getPlayer().getConnection().write(packet); return true; } @@ -171,11 +171,11 @@ public boolean handle(final ResourcePackRequestPacket packet) { if (serverConn.getConnection() != null) { // We can technically skip these first 2 states, however, for conformity to normal state flow expectations... serverConn.getConnection().write(new ResourcePackResponsePacket( - packet.getId(), packet.getHash(), PlayerResourcePackStatusEvent.Status.ACCEPTED)); + packet.id(), packet.hash(), PlayerResourcePackStatusEvent.Status.ACCEPTED)); serverConn.getConnection().write(new ResourcePackResponsePacket( - packet.getId(), packet.getHash(), PlayerResourcePackStatusEvent.Status.DOWNLOADED)); + packet.id(), packet.hash(), PlayerResourcePackStatusEvent.Status.DOWNLOADED)); serverConn.getConnection().write(new ResourcePackResponsePacket( - packet.getId(), packet.getHash(), PlayerResourcePackStatusEvent.Status.SUCCESSFUL)); + packet.id(), packet.hash(), PlayerResourcePackStatusEvent.Status.SUCCESSFUL)); } if (modifiedPack) { logger.warn("A plugin has tried to modify a ResourcePack provided by the backend server " @@ -187,12 +187,12 @@ public boolean handle(final ResourcePackRequestPacket packet) { } } else if (serverConn.getConnection() != null) { serverConn.getConnection().write(new ResourcePackResponsePacket( - packet.getId(), packet.getHash(), PlayerResourcePackStatusEvent.Status.DECLINED)); + packet.id(), packet.hash(), PlayerResourcePackStatusEvent.Status.DECLINED)); } }, playerConnection.eventLoop()).exceptionally((ex) -> { if (serverConn.getConnection() != null) { serverConn.getConnection().write(new ResourcePackResponsePacket( - packet.getId(), packet.getHash(), PlayerResourcePackStatusEvent.Status.DECLINED)); + packet.id(), packet.hash(), PlayerResourcePackStatusEvent.Status.DECLINED)); } logger.error("Exception while handling resource pack send for {}", playerConnection, ex); return null; @@ -328,7 +328,7 @@ public boolean handle(TransferPacket packet) { @Override public boolean handle(ClientboundStoreCookiePacket packet) { server.getEventManager() - .fire(new CookieStoreEvent(serverConn.getPlayer(), packet.getKey(), packet.getPayload())) + .fire(new CookieStoreEvent(serverConn.getPlayer(), packet.key(), packet.payload())) .thenAcceptAsync(event -> { if (event.getResult().isAllowed()) { final Key resultedKey = event.getResult().getKey() == null diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java index 612e9c25a0..9d3690b1d3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java @@ -157,7 +157,7 @@ public boolean handle(ServerLoginSuccessPacket packet) { if (smc.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) { smc.setActiveSessionHandler(StateRegistry.PLAY, new TransitionSessionHandler(server, serverConn, resultFuture)); } else { - smc.write(new LoginAcknowledgedPacket()); + smc.write(LoginAcknowledgedPacket.INSTANCE); smc.setActiveSessionHandler(StateRegistry.CONFIG, new ConfigSessionHandler(server, serverConn, resultFuture)); ConnectedPlayer player = serverConn.getPlayer(); if (player.getClientSettingsPacket() != null) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java index 71ebd7bc78..eac9bcf682 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java @@ -170,26 +170,26 @@ private void startHandshake() { .orElseGet(() -> registeredServer.getServerInfo().getAddress()) .getHostString(); - HandshakePacket handshake = new HandshakePacket(); - handshake.setIntent(HandshakeIntent.LOGIN); - handshake.setProtocolVersion(protocolVersion); + String serverAddress; if (forwardingMode == PlayerInfoForwarding.LEGACY) { - handshake.setServerAddress(createLegacyForwardingAddress()); + serverAddress = createLegacyForwardingAddress(); } else if (forwardingMode == PlayerInfoForwarding.BUNGEEGUARD) { byte[] secret = server.getConfiguration().getForwardingSecret(); - handshake.setServerAddress(createBungeeGuardForwardingAddress(secret)); + serverAddress = createBungeeGuardForwardingAddress(secret); } else if (proxyPlayer.getConnection().getType() == ConnectionTypes.LEGACY_FORGE) { - handshake.setServerAddress(playerVhost + HANDSHAKE_HOSTNAME_TOKEN); + serverAddress = playerVhost + HANDSHAKE_HOSTNAME_TOKEN; } else if (proxyPlayer.getConnection().getType() instanceof ModernForgeConnectionType) { - handshake.setServerAddress(playerVhost + ((ModernForgeConnectionType) proxyPlayer - .getConnection().getType()).getModernToken()); + serverAddress = playerVhost + ((ModernForgeConnectionType) proxyPlayer + .getConnection().getType()).getModernToken(); } else { - handshake.setServerAddress(playerVhost); + serverAddress = playerVhost; } - handshake.setPort(proxyPlayer.getVirtualHost() + int port = proxyPlayer.getVirtualHost() .orElseGet(() -> registeredServer.getServerInfo().getAddress()) - .getPort()); + .getPort(); + + HandshakePacket handshake = new HandshakePacket(protocolVersion, serverAddress, port, HandshakeIntent.LOGIN); mc.delayedWrite(handshake); mc.setProtocolVersion(protocolVersion); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java index 0aea8d7786..0fa8902032 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java @@ -195,7 +195,7 @@ public boolean handle(LoginAcknowledgedPacket packet) { @Override public boolean handle(ServerboundCookieResponsePacket packet) { server.getEventManager() - .fire(new CookieReceiveEvent(connectedPlayer, packet.getKey(), packet.getPayload())) + .fire(new CookieReceiveEvent(connectedPlayer, packet.key(), packet.payload())) .thenAcceptAsync(event -> { if (event.getResult().isAllowed()) { // The received cookie must have been requested by a proxy plugin in login phase, @@ -230,10 +230,9 @@ private void completeLoginProtocolPhaseAndInitialize(ConnectedPlayer player) { return; } - ServerLoginSuccessPacket success = new ServerLoginSuccessPacket(); - success.setUsername(player.getUsername()); - success.setProperties(player.getGameProfileProperties()); - success.setUuid(player.getUniqueId()); + ServerLoginSuccessPacket success = new ServerLoginSuccessPacket( + player.getUniqueId(), player.getUsername(), player.getGameProfileProperties() + ); mcConnection.write(success); loginState = State.SUCCESS_SENT; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java index 776f99d684..26e601ed58 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java @@ -188,7 +188,7 @@ public boolean handle(KnownPacksPacket packet) { @Override public boolean handle(ServerboundCookieResponsePacket packet) { server.getEventManager() - .fire(new CookieReceiveEvent(player, packet.getKey(), packet.getPayload())) + .fire(new CookieReceiveEvent(player, packet.key(), packet.payload())) .thenAcceptAsync(event -> { if (event.getResult().isAllowed()) { final VelocityServerConnection serverConnection = player.getConnectionInFlight(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index 17eb708b36..c9ced6274e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -387,8 +387,8 @@ public boolean handle(PluginMessagePacket packet) { public boolean handle(ResourcePackResponsePacket packet) { return player.resourcePackHandler().onResourcePackResponse( new ResourcePackResponseBundle(packet.getId(), - packet.getHash(), - packet.getStatus())); + packet.hash(), + packet.status())); } @Override @@ -428,7 +428,7 @@ public boolean handle(ChatAcknowledgementPacket packet) { @Override public boolean handle(ServerboundCookieResponsePacket packet) { server.getEventManager() - .fire(new CookieReceiveEvent(player, packet.getKey(), packet.getPayload())) + .fire(new CookieReceiveEvent(player, packet.key(), packet.payload())) .thenAcceptAsync(event -> { if (event.getResult().isAllowed()) { final VelocityServerConnection serverConnection = player.getConnectedServer(); @@ -586,10 +586,7 @@ public void handleBackendJoinGame(JoinGamePacket joinGame, VelocityServerConnect // Remove previous boss bars. These don't get cleared when sending JoinGame (up until 1.20.2), // thus the need to track them. for (UUID serverBossBar : serverBossBars) { - BossBarPacket deletePacket = new BossBarPacket(); - deletePacket.setUuid(serverBossBar); - deletePacket.setAction(BossBarPacket.REMOVE); - player.getConnection().delayedWrite(deletePacket); + player.getConnection().delayedWrite(BossBarPacket.createRemovePacket(serverBossBar)); } serverBossBars.clear(); } @@ -615,7 +612,7 @@ public void handleBackendJoinGame(JoinGamePacket joinGame, VelocityServerConnect // Clear any title from the previous server. if (player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) { player.getConnection().delayedWrite( - GenericTitlePacket.constructTitlePacket(GenericTitlePacket.ActionType.RESET, + GenericTitlePacket.createClearTitlePacket(GenericTitlePacket.ActionType.RESET, player.getProtocolVersion())); } @@ -634,14 +631,15 @@ private void doFastClientServerSwitch(JoinGamePacket joinGame) { // Most notably, by having the client accept the join game packet, we can work around the need // to perform entity ID rewrites, eliminating potential issues from rewriting packets and // improving compatibility with mods. - final RespawnPacket respawn = RespawnPacket.fromJoinGame(joinGame); - + int dim = joinGame.getDimension(); if (player.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_16)) { // Before Minecraft 1.16, we could not switch to the same dimension without sending an // additional respawn. On older versions of Minecraft this forces the client to perform // garbage collection which adds additional latency. - joinGame.setDimension(joinGame.getDimension() == 0 ? -1 : 0); + dim = joinGame.getDimension() == 0 ? -1 : 0; } + final RespawnPacket respawn = RespawnPacket.fromJoinGame(joinGame, dim); + player.getConnection().delayedWrite(joinGame); player.getConnection().delayedWrite(respawn); } @@ -655,8 +653,7 @@ private void doSafeClientServerSwitch(JoinGamePacket joinGame) { player.getConnection().delayedWrite(joinGame); // Send a respawn packet in a different dimension. - final RespawnPacket fakeSwitchPacket = RespawnPacket.fromJoinGame(joinGame); - fakeSwitchPacket.setDimension(joinGame.getDimension() == 0 ? -1 : 0); + final RespawnPacket fakeSwitchPacket = RespawnPacket.fromJoinGame(joinGame, joinGame.getDimension() == 0 ? -1 : 0); player.getConnection().delayedWrite(fakeSwitchPacket); // Now send a respawn packet in the correct dimension. @@ -728,11 +725,9 @@ private boolean handleCommandTabComplete(TabCompleteRequestPacket packet) { offers.add(new Offer(offer, tooltip)); } - TabCompleteResponsePacket resp = new TabCompleteResponsePacket(); - resp.setTransactionId(packet.getTransactionId()); - resp.setStart(startPos + 1); - resp.setLength(packet.getCommand().length() - startPos - 1); - resp.getOffers().addAll(offers); + TabCompleteResponsePacket resp = new TabCompleteResponsePacket( + packet.transactionId(), startPos + 1, packet.getCommand().length() - startPos - 1, offers + ); player.getConnection().write(resp); } }, player.getConnection().eventLoop()).exceptionally((ex) -> { @@ -778,6 +773,7 @@ private void finishCommandTabComplete(TabCompleteRequestPacket request, .thenAcceptAsync(offers -> { boolean legacy = player.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_13); + List extendedOffers = new ArrayList<>(response.offers()); try { for (Suggestion suggestion : offers.getList()) { String offer = suggestion.getText(); @@ -791,10 +787,10 @@ private void finishCommandTabComplete(TabCompleteRequestPacket request, } else if (suggestion.getTooltip() != null) { tooltip = new ComponentHolder(player.getProtocolVersion(), Component.text(suggestion.getTooltip().getString())); } - response.getOffers().add(new Offer(offer, tooltip)); + extendedOffers.add(new Offer(offer, tooltip)); } - response.getOffers().sort(null); - player.getConnection().write(response); + extendedOffers.sort(null); + player.getConnection().write(response.withOffers(extendedOffers)); } catch (Exception e) { logger.error("Unable to provide tab list completions for {} for command '{}'", player.getUsername(), command, @@ -811,17 +807,17 @@ private void finishCommandTabComplete(TabCompleteRequestPacket request, private void finishRegularTabComplete(TabCompleteRequestPacket request, TabCompleteResponsePacket response) { - List offers = new ArrayList<>(); - for (Offer offer : response.getOffers()) { - offers.add(offer.getText()); + List textOffers = new ArrayList<>(); + for (Offer offer : response.offers()) { + textOffers.add(offer.getText()); } - server.getEventManager().fire(new TabCompleteEvent(player, request.getCommand(), offers)) + server.getEventManager().fire(new TabCompleteEvent(player, request.getCommand(), textOffers)) .thenAcceptAsync(e -> { - response.getOffers().clear(); + List newOffers = new ArrayList<>(); for (String s : e.getSuggestions()) { - response.getOffers().add(new Offer(s)); + newOffers.add(new Offer(s)); } - player.getConnection().write(response); + player.getConnection().write(response.withOffers(newOffers)); }, player.getConnection().eventLoop()).exceptionally((ex) -> { logger.error( "Exception while finishing regular tab completion," diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java index f8d3107cca..e0726f140f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java @@ -38,25 +38,25 @@ public class ClientSettingsWrapper implements PlayerSettings { ClientSettingsWrapper(ClientSettingsPacket settings) { this.settings = settings; - this.parts = new SkinParts((byte) settings.getSkinParts()); + this.parts = new SkinParts((byte) settings.skinParts()); } @Override public Locale getLocale() { if (locale == null) { - locale = Locale.forLanguageTag(settings.getLocale().replaceAll("_", "-")); + locale = Locale.forLanguageTag(settings.locale().replaceAll("_", "-")); } return locale; } @Override public byte getViewDistance() { - return settings.getViewDistance(); + return settings.viewDistance(); } @Override public ChatMode getChatMode() { - return switch (settings.getChatVisibility()) { + return switch (settings.chatVisibility()) { case 1 -> ChatMode.COMMANDS_ONLY; case 2 -> ChatMode.HIDDEN; default -> ChatMode.SHOWN; @@ -65,7 +65,7 @@ public ChatMode getChatMode() { @Override public boolean hasChatColors() { - return settings.isChatColors(); + return settings.chatColors(); } @Override @@ -75,22 +75,22 @@ public SkinParts getSkinParts() { @Override public MainHand getMainHand() { - return settings.getMainHand() == 1 ? MainHand.RIGHT : MainHand.LEFT; + return settings.mainHand() == 1 ? MainHand.RIGHT : MainHand.LEFT; } @Override public boolean isClientListingAllowed() { - return settings.isClientListingAllowed(); + return settings.clientListingAllowed(); } @Override public boolean isTextFilteringEnabled() { - return settings.isTextFilteringEnabled(); + return settings.textFilteringEnabled(); } @Override public ParticleStatus getParticleStatus() { - return switch (settings.getParticleStatus()) { + return switch (settings.particleStatus()) { case 1 -> ParticleStatus.DECREASED; case 2 -> ParticleStatus.MINIMAL; default -> ParticleStatus.ALL; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index 17f4fb5ce9..427e69ae5b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -451,9 +451,9 @@ public void sendActionBar(net.kyori.adventure.text.@NonNull Component message) { ProtocolVersion playerVersion = getProtocolVersion(); if (playerVersion.noLessThan(ProtocolVersion.MINECRAFT_1_11)) { // Use the title packet instead. - GenericTitlePacket pkt = GenericTitlePacket.constructTitlePacket( - GenericTitlePacket.ActionType.SET_ACTION_BAR, playerVersion); - pkt.setComponent(new ComponentHolder(playerVersion, translated)); + GenericTitlePacket pkt = GenericTitlePacket.createComponentTitlePacket( + GenericTitlePacket.ActionType.SET_ACTION_BAR, + new ComponentHolder(playerVersion, translated), playerVersion); connection.write(pkt); } else { // Due to issues with action bar packets, we'll need to convert the text message into a @@ -461,9 +461,7 @@ public void sendActionBar(net.kyori.adventure.text.@NonNull Component message) { JsonObject object = new JsonObject(); object.addProperty("text", LegacyComponentSerializer.legacySection() .serialize(translated)); - LegacyChatPacket legacyChat = new LegacyChatPacket(); - legacyChat.setMessage(object.toString()); - legacyChat.setType(LegacyChatPacket.GAME_INFO_TYPE); + LegacyChatPacket legacyChat = new LegacyChatPacket(object.toString(), LegacyChatPacket.GAME_INFO_TYPE, null); connection.write(legacyChat); } } @@ -504,26 +502,26 @@ public void sendPlayerListHeaderAndFooter(final @NotNull Component header, @Override public void showTitle(net.kyori.adventure.title.@NonNull Title title) { if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - GenericTitlePacket timesPkt = GenericTitlePacket.constructTitlePacket( - GenericTitlePacket.ActionType.SET_TIMES, this.getProtocolVersion()); net.kyori.adventure.title.Title.Times times = title.times(); if (times != null) { - timesPkt.setFadeIn((int) DurationUtils.toTicks(times.fadeIn())); - timesPkt.setStay((int) DurationUtils.toTicks(times.stay())); - timesPkt.setFadeOut((int) DurationUtils.toTicks(times.fadeOut())); + GenericTitlePacket timesPkt = GenericTitlePacket.createTimesTitlePacket( + (int) DurationUtils.toTicks(times.fadeIn()), + (int) DurationUtils.toTicks(times.stay()), + (int) DurationUtils.toTicks(times.fadeOut()), + this.getProtocolVersion()); + connection.delayedWrite(timesPkt); } - connection.delayedWrite(timesPkt); - GenericTitlePacket subtitlePkt = GenericTitlePacket.constructTitlePacket( - GenericTitlePacket.ActionType.SET_SUBTITLE, this.getProtocolVersion()); - subtitlePkt.setComponent(new ComponentHolder( - this.getProtocolVersion(), translateMessage(title.subtitle()))); + GenericTitlePacket subtitlePkt = GenericTitlePacket.createComponentTitlePacket( + GenericTitlePacket.ActionType.SET_SUBTITLE, + new ComponentHolder(this.getProtocolVersion(), translateMessage(title.subtitle())), + this.getProtocolVersion()); connection.delayedWrite(subtitlePkt); - GenericTitlePacket titlePkt = GenericTitlePacket.constructTitlePacket( - GenericTitlePacket.ActionType.SET_TITLE, this.getProtocolVersion()); - titlePkt.setComponent(new ComponentHolder( - this.getProtocolVersion(), translateMessage(title.title()))); + GenericTitlePacket titlePkt = GenericTitlePacket.createComponentTitlePacket( + GenericTitlePacket.ActionType.SET_TITLE, + new ComponentHolder(this.getProtocolVersion(), translateMessage(title.title())), + this.getProtocolVersion()); connection.delayedWrite(titlePkt); connection.flush(); @@ -545,24 +543,24 @@ public void sendTitlePart(@NotNull TitlePart part, @NotNull T value) { } if (part == TitlePart.TITLE) { - GenericTitlePacket titlePkt = GenericTitlePacket.constructTitlePacket( - GenericTitlePacket.ActionType.SET_TITLE, this.getProtocolVersion()); - titlePkt.setComponent(new ComponentHolder( - this.getProtocolVersion(), translateMessage((Component) value))); + GenericTitlePacket titlePkt = GenericTitlePacket.createComponentTitlePacket( + GenericTitlePacket.ActionType.SET_TITLE, + new ComponentHolder(this.getProtocolVersion(), translateMessage((Component) value)), + this.getProtocolVersion()); connection.write(titlePkt); } else if (part == TitlePart.SUBTITLE) { - GenericTitlePacket titlePkt = GenericTitlePacket.constructTitlePacket( - GenericTitlePacket.ActionType.SET_SUBTITLE, this.getProtocolVersion()); - titlePkt.setComponent(new ComponentHolder( - this.getProtocolVersion(), translateMessage((Component) value))); + GenericTitlePacket titlePkt = GenericTitlePacket.createComponentTitlePacket( + GenericTitlePacket.ActionType.SET_SUBTITLE, + new ComponentHolder(this.getProtocolVersion(), translateMessage((Component) value)), + this.getProtocolVersion()); connection.write(titlePkt); } else if (part == TitlePart.TIMES) { Times times = (Times) value; - GenericTitlePacket timesPkt = GenericTitlePacket.constructTitlePacket( - GenericTitlePacket.ActionType.SET_TIMES, this.getProtocolVersion()); - timesPkt.setFadeIn((int) DurationUtils.toTicks(times.fadeIn())); - timesPkt.setStay((int) DurationUtils.toTicks(times.stay())); - timesPkt.setFadeOut((int) DurationUtils.toTicks(times.fadeOut())); + GenericTitlePacket timesPkt = GenericTitlePacket.createTimesTitlePacket( + (int) DurationUtils.toTicks(times.fadeIn()), + (int) DurationUtils.toTicks(times.stay()), + (int) DurationUtils.toTicks(times.fadeOut()), + this.getProtocolVersion()); connection.write(timesPkt); } else { throw new IllegalArgumentException("Title part " + part + " is not valid"); @@ -572,7 +570,7 @@ public void sendTitlePart(@NotNull TitlePart part, @NotNull T value) { @Override public void clearTitle() { if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - connection.write(GenericTitlePacket.constructTitlePacket( + connection.write(GenericTitlePacket.createClearTitlePacket( GenericTitlePacket.ActionType.HIDE, this.getProtocolVersion())); } } @@ -580,7 +578,7 @@ public void clearTitle() { @Override public void resetTitle() { if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - connection.write(GenericTitlePacket.constructTitlePacket( + connection.write(GenericTitlePacket.createClearTitlePacket( GenericTitlePacket.ActionType.RESET, this.getProtocolVersion())); } } @@ -737,7 +735,7 @@ public void handleConnectionException(RegisteredServer server, DisconnectPacket return; } - Component disconnectReason = disconnect.getReason().getComponent(); + Component disconnectReason = disconnect.reason().getComponent(); String plainTextReason = PASS_THRU_TRANSLATE.serialize(disconnectReason); if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) { if (this.server.getConfiguration().isLogPlayerConnections()) { @@ -1262,7 +1260,7 @@ public void sendResourcePacks(@NotNull ResourcePackRequest request) { @Override public void clearResourcePacks() { if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { - connection.write(new RemoveResourcePackPacket()); + connection.write(new RemoveResourcePackPacket(null)); this.resourcePackHandler.clearAppliedResourcePacks(); } } @@ -1333,8 +1331,7 @@ public void removeResourcePacks(@NotNull ResourcePackInfoLike request, @NotNull public void sendKeepAlive() { if (connection.getState() == StateRegistry.PLAY || connection.getState() == StateRegistry.CONFIG) { - KeepAlivePacket keepAlive = new KeepAlivePacket(); - keepAlive.setRandomId(ThreadLocalRandom.current().nextLong()); + KeepAlivePacket keepAlive = new KeepAlivePacket(ThreadLocalRandom.current().nextLong()); connection.write(keepAlive); } } @@ -1352,7 +1349,7 @@ public boolean forwardKeepAlive(final KeepAlivePacket packet) { private boolean sendKeepAliveToBackend(final @Nullable VelocityServerConnection serverConnection, final @NotNull KeepAlivePacket packet) { if (serverConnection != null) { - final Long sentTime = serverConnection.getPendingPings().remove(packet.getRandomId()); + final Long sentTime = serverConnection.getPendingPings().remove(packet.randomId()); if (sentTime != null) { final MinecraftConnection smc = serverConnection.getConnection(); if (smc != null) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java index 80260d2b47..ab6d948a86 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java @@ -87,19 +87,19 @@ public boolean handle(LegacyHandshakePacket packet) { @Override public boolean handle(final HandshakePacket handshake) { - final StateRegistry nextState = getStateForProtocol(handshake.getNextStatus()); + final StateRegistry nextState = getStateForProtocol(handshake.nextStatus()); if (nextState == null) { - LOGGER.error("{} provided invalid protocol {}", this, handshake.getNextStatus()); + LOGGER.error("{} provided invalid protocol {}", this, handshake.nextStatus()); connection.close(true); } else { final InitialInboundConnection ic = new InitialInboundConnection(connection, - cleanVhost(handshake.getServerAddress()), handshake); - if (handshake.getIntent() == HandshakeIntent.TRANSFER + cleanVhost(handshake.serverAddress()), handshake); + if (handshake.intent() == HandshakeIntent.TRANSFER && !server.getConfiguration().isAcceptTransfers()) { ic.disconnect(Component.translatable("multiplayer.disconnect.transfers_disabled")); return true; } - connection.setProtocolVersion(handshake.getProtocolVersion()); + connection.setProtocolVersion(handshake.protocolVersion()); connection.setAssociation(ic); switch (nextState) { @@ -124,7 +124,7 @@ public boolean handle(final HandshakePacket handshake) { } private void handleLogin(HandshakePacket handshake, InitialInboundConnection ic) { - if (!handshake.getProtocolVersion().isSupported()) { + if (!handshake.protocolVersion().isSupported()) { // Bump connection into correct protocol state so that we can send the disconnect packet. connection.setState(StateRegistry.LOGIN); ic.disconnectQuietly(Component.translatable() @@ -147,7 +147,7 @@ private void handleLogin(HandshakePacket handshake, InitialInboundConnection ic) // If the proxy is configured for modern forwarding, we must deny connections from 1.12.2 // and lower, otherwise IP information will never get forwarded. if (server.getConfiguration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN - && handshake.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_13)) { + && handshake.protocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_13)) { // Bump connection into correct protocol state so that we can send the disconnect packet. connection.setState(StateRegistry.LOGIN); ic.disconnectQuietly( @@ -157,21 +157,23 @@ private void handleLogin(HandshakePacket handshake, InitialInboundConnection ic) final LoginInboundConnection lic = new LoginInboundConnection(ic); server.getEventManager().fireAndForget( - new ConnectionHandshakeEvent(lic, handshake.getIntent())); + new ConnectionHandshakeEvent(lic, handshake.intent())); connection.setActiveSessionHandler(StateRegistry.LOGIN, new InitialLoginSessionHandler(server, connection, lic)); } private ConnectionType getHandshakeConnectionType(HandshakePacket handshake) { - if (handshake.getServerAddress().contains(ModernForgeConstants.MODERN_FORGE_TOKEN) - && handshake.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { - return new ModernForgeConnectionType(handshake.getServerAddress()); + final String serverAddress = handshake.serverAddress(); + final ProtocolVersion protocolVersion = handshake.protocolVersion(); + if (serverAddress.contains(ModernForgeConstants.MODERN_FORGE_TOKEN) + && protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { + return new ModernForgeConnectionType(serverAddress); } // Determine if we're using Forge (1.8 to 1.12, may not be the case in 1.13). - if (handshake.getServerAddress().endsWith(LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN) - && handshake.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_13)) { + if (serverAddress.endsWith(LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN) + && protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_13)) { return ConnectionTypes.LEGACY_FORGE; - } else if (handshake.getProtocolVersion().noGreaterThan(ProtocolVersion.MINECRAFT_1_7_6)) { + } else if (protocolVersion.noGreaterThan(ProtocolVersion.MINECRAFT_1_7_6)) { // 1.7 Forge will not notify us during handshake. UNDETERMINED will listen for incoming // forge handshake attempts. Also sends a reset handshake packet on every transition. return ConnectionTypes.UNDETERMINED_17; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java index 35368844f5..4ded1dfe64 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java @@ -61,12 +61,12 @@ public InetSocketAddress getRemoteAddress() { @Override public Optional getVirtualHost() { - return Optional.of(InetSocketAddress.createUnresolved(cleanedAddress, handshake.getPort())); + return Optional.of(InetSocketAddress.createUnresolved(cleanedAddress, handshake.port())); } @Override public Optional getRawVirtualHost() { - return Optional.of(handshake.getServerAddress()); + return Optional.of(handshake.serverAddress()); } @Override @@ -101,7 +101,7 @@ public ProtocolState getProtocolState() { @Override public HandshakeIntent getHandshakeIntent() { - return handshake.getIntent(); + return handshake.intent(); } /** diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java index 00187d3470..8de17ca5ad 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java @@ -91,7 +91,7 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler { public boolean handle(ServerLoginPacket packet) { assertState(LoginState.LOGIN_PACKET_EXPECTED); this.currentState = LoginState.LOGIN_PACKET_RECEIVED; - IdentifiedKey playerKey = packet.getPlayerKey(); + IdentifiedKey playerKey = packet.playerKey(); if (playerKey != null) { if (playerKey.hasExpired()) { inbound.disconnect( @@ -102,7 +102,7 @@ public boolean handle(ServerLoginPacket packet) { boolean isKeyValid; if (playerKey.getKeyRevision() == IdentifiedKey.Revision.LINKED_V2 && playerKey instanceof final IdentifiedKeyImpl keyImpl) { - isKeyValid = keyImpl.internalAddHolder(packet.getHolderUuid()); + isKeyValid = keyImpl.internalAddHolder(packet.holderUuid()); } else { isKeyValid = playerKey.isSignatureValid(); } @@ -120,7 +120,7 @@ public boolean handle(ServerLoginPacket packet) { inbound.setPlayerKey(playerKey); this.login = packet; - final PreLoginEvent event = new PreLoginEvent(inbound, login.getUsername(), login.getHolderUuid()); + final PreLoginEvent event = new PreLoginEvent(inbound, login.username(), login.holderUuid()); server.getEventManager().fire(event).thenRunAsync(() -> { if (mcConnection.isClosed()) { // The player was disconnected @@ -146,13 +146,13 @@ public boolean handle(ServerLoginPacket packet) { && (server.getConfiguration().isOnlineMode() || result.isOnlineModeAllowed())) { // Request encryption. EncryptionRequestPacket request = generateEncryptionRequest(); - this.verify = Arrays.copyOf(request.getVerifyToken(), 4); + this.verify = Arrays.copyOf(request.verifyToken(), 4); mcConnection.write(request); this.currentState = LoginState.ENCRYPTION_REQUEST_SENT; } else { mcConnection.setActiveSessionHandler(StateRegistry.LOGIN, new AuthSessionHandler(server, inbound, - GameProfile.forOfflinePlayer(login.getUsername()), false)); + GameProfile.forOfflinePlayer(login.username()), false)); } }); }); @@ -203,7 +203,7 @@ public boolean handle(EncryptionResponsePacket packet) { String playerIp = ((InetSocketAddress) mcConnection.getRemoteAddress()).getHostString(); String url = String.format(MOJANG_HASJOINED_URL, - urlFormParameterEscaper().escape(login.getUsername()), serverId); + urlFormParameterEscaper().escape(login.username()), serverId); if (server.getConfiguration().shouldPreventClientProxyConnections()) { url += "&ip=" + urlFormParameterEscaper().escape(playerIp); @@ -263,7 +263,7 @@ public boolean handle(EncryptionResponsePacket packet) { // Something else went wrong logger.error( "Got an unexpected error code {} whilst contacting Mojang to log in {} ({})", - response.statusCode(), login.getUsername(), playerIp); + response.statusCode(), login.username(), playerIp); inbound.disconnect(Component.translatable("multiplayer.disconnect.authservers_down")); } }, mcConnection.eventLoop()) @@ -289,9 +289,8 @@ private EncryptionRequestPacket generateEncryptionRequest() { byte[] verify = new byte[4]; ThreadLocalRandom.current().nextBytes(verify); - EncryptionRequestPacket request = new EncryptionRequestPacket(); - request.setPublicKey(server.getServerKeyPair().getPublic().getEncoded()); - request.setVerifyToken(verify); + EncryptionRequestPacket request = new EncryptionRequestPacket("", + server.getServerKeyPair().getPublic().getEncoded(), verify, true); return request; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java index 9922c5095e..8ae1e913f9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java @@ -133,10 +133,10 @@ void cleanup() { } void handleLoginPluginResponse(final LoginPluginResponsePacket response) { - final MessageConsumer consumer = this.outstandingResponses.remove(response.getId()); + final MessageConsumer consumer = this.outstandingResponses.remove(response.id()); if (consumer != null) { try { - consumer.onMessageResponse(response.isSuccess() ? ByteBufUtil.getBytes(response.content()) + consumer.onMessageResponse(response.success() ? ByteBufUtil.getBytes(response.content()) : null); } finally { final Runnable onAllMessagesHandled = this.onAllMessagesHandled; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java index 854a52d749..7b065d23a0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java @@ -84,9 +84,9 @@ static List readModList(PluginMessagePacket message) { * @return A copy of the reset packet */ static PluginMessagePacket resetPacket() { - PluginMessagePacket msg = new PluginMessagePacket(); - msg.setChannel(FORGE_LEGACY_HANDSHAKE_CHANNEL); - msg.replace(Unpooled.wrappedBuffer(FORGE_LEGACY_HANDSHAKE_RESET_DATA.clone())); - return msg; + return new PluginMessagePacket( + FORGE_LEGACY_HANDSHAKE_CHANNEL, + Unpooled.wrappedBuffer(FORGE_LEGACY_HANDSHAKE_RESET_DATA.clone()) + ); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ResourcePackHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ResourcePackHandler.java index b384388534..c99585fd2a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ResourcePackHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ResourcePackHandler.java @@ -101,17 +101,15 @@ public void queueResourcePack(final @NotNull ResourcePackRequest request) { } protected void sendResourcePackRequestPacket(final @NotNull ResourcePackInfo queued) { - final ResourcePackRequestPacket request = new ResourcePackRequestPacket(); - request.setId(queued.getId()); - request.setUrl(queued.getUrl()); - if (queued.getHash() != null) { - request.setHash(ByteBufUtil.hexDump(queued.getHash())); - } else { - request.setHash(""); - } - request.setRequired(queued.getShouldForce()); - request.setPrompt(queued.getPrompt() == null ? null : - new ComponentHolder(player.getProtocolVersion(), player.translateMessage(queued.getPrompt()))); + final String hash = queued.getHash() != null ? ByteBufUtil.hexDump(queued.getHash()) : ""; + final ComponentHolder prompt = queued.getPrompt() == null ? null : + new ComponentHolder(player.getProtocolVersion(), player.translateMessage(queued.getPrompt())); + final ResourcePackRequestPacket request = new ResourcePackRequestPacket( + queued.getId(), + queued.getUrl(), + hash, + queued.getShouldForce(), + prompt); player.getConnection().write(request); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java index d7c2675892..7b11b21bfa 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java @@ -63,11 +63,11 @@ public static Impl forDisconnect(Component component, RegisteredServer server) { } public static Impl forDisconnect(DisconnectPacket disconnect, RegisteredServer server) { - return forDisconnect(disconnect.getReason().getComponent(), server); + return forDisconnect(disconnect.reason().getComponent(), server); } public static Impl forUnsafeDisconnect(DisconnectPacket disconnect, RegisteredServer server) { - return new Impl(Status.SERVER_DISCONNECTED, disconnect.getReason().getComponent(), server, + return new Impl(Status.SERVER_DISCONNECTED, disconnect.reason().getComponent(), server, false); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/MinecraftPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/MinecraftPacket.java index 1722f42bf3..6f3a8a0784 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/MinecraftPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/MinecraftPacket.java @@ -17,33 +17,23 @@ package com.velocitypowered.proxy.protocol; -import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; -import io.netty.buffer.ByteBuf; /** - * Represents a Minecraft packet. + * Represents a Minecraft packet. Packets are immutable data holders that use separate + * {@link PacketCodec} implementations for encoding and decoding. + * + * @see PacketCodec + * @see PacketEncoder + * @see PacketDecoder */ public interface MinecraftPacket { - void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion); - - void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion); - + /** + * Handles this packet using the visitor pattern. + * + * @param handler the session handler + * @return true if the packet was handled + */ boolean handle(MinecraftSessionHandler handler); - - default int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion version) { - return -1; - } - - default int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion version) { - return 0; - } - - default int encodeSizeHint(ProtocolUtils.Direction direction, - ProtocolVersion version) { - return -1; - } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/PacketCodec.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/PacketCodec.java new file mode 100644 index 0000000000..6abf58f31d --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/PacketCodec.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2018-2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol; + +/** + * A combined codec that can both encode and decode packets. + * + * @param the packet type + */ +public interface PacketCodec + extends PacketEncoder, PacketDecoder { +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/PacketDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/PacketDecoder.java new file mode 100644 index 0000000000..b0c517939d --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/PacketDecoder.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2018-2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol; + +import com.velocitypowered.api.network.ProtocolVersion; +import io.netty.buffer.ByteBuf; + +/** + * Decodes a packet from a ByteBuf. + * + * @param the packet type + */ +public interface PacketDecoder { + + /** + * Decodes a packet from the provided ByteBuf. + * + * @param buf the buffer to read from + * @param direction the direction of the packet + * @param protocolVersion the protocol version + * @return the decoded packet instance + */ + T decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion); + + /** + * Returns the expected maximum length of the packet. This is used for validation during + * decoding. Return -1 if no maximum is enforced. + * + * @param buf the buffer containing the packet + * @param direction the direction of the packet + * @param version the protocol version + * @return the maximum expected length, or -1 if no limit + */ + default int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + return -1; + } + + /** + * Returns the expected minimum length of the packet. This is used for validation during + * decoding. Return 0 if no minimum is enforced. + * + * @param buf the buffer containing the packet + * @param direction the direction of the packet + * @param version the protocol version + * @return the minimum expected length + */ + default int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + return 0; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/PacketEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/PacketEncoder.java new file mode 100644 index 0000000000..7009aa9eb3 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/PacketEncoder.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018-2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol; + +import com.velocitypowered.api.network.ProtocolVersion; +import io.netty.buffer.ByteBuf; + +/** + * Encodes a packet into a ByteBuf. + * + * @param the packet type + */ +public interface PacketEncoder { + + /** + * Encodes the given packet into the provided ByteBuf. + * + * @param packet the packet to encode + * @param buf the buffer to write to + * @param direction the direction of the packet + * @param protocolVersion the protocol version + */ + void encode(T packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion); + + /** + * Returns a hint for the expected size of the encoded packet. This is used to pre-allocate + * buffers for better performance. Return -1 if no hint is available. + * + * @param packet the packet to encode + * @param direction the direction of the packet + * @param version the protocol version + * @return the size hint, or -1 if unknown + */ + default int encodeSizeHint(T packet, ProtocolUtils.Direction direction, + ProtocolVersion version) { + return -1; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolStates.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolStates.java new file mode 100644 index 0000000000..9b17295089 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolStates.java @@ -0,0 +1,797 @@ +/* + * Copyright (C) 2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol; + +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_11; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12_1; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14_4; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_15; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_15_2; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_1; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_2; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_4; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_17; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_17_1; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_18; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_18_2; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_1; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_3; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_4; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_2; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_3; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_5; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_2; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_4; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_5; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_6; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_9; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9_1; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9_4; + +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; +import com.velocitypowered.proxy.protocol.packet.AvailableCommandsPacket; +import com.velocitypowered.proxy.protocol.packet.BossBarPacket; +import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket; +import com.velocitypowered.proxy.protocol.packet.ClientSettingsPacket; +import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket; +import com.velocitypowered.proxy.protocol.packet.ClientboundSoundEntityPacket; +import com.velocitypowered.proxy.protocol.packet.ClientboundStopSoundPacket; +import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket; +import com.velocitypowered.proxy.protocol.packet.DialogClearPacket; +import com.velocitypowered.proxy.protocol.packet.DialogShowPacket; +import com.velocitypowered.proxy.protocol.packet.DisconnectPacket; +import com.velocitypowered.proxy.protocol.packet.EncryptionRequestPacket; +import com.velocitypowered.proxy.protocol.packet.EncryptionResponsePacket; +import com.velocitypowered.proxy.protocol.packet.HandshakePacket; +import com.velocitypowered.proxy.protocol.packet.HeaderAndFooterPacket; +import com.velocitypowered.proxy.protocol.packet.JoinGamePacket; +import com.velocitypowered.proxy.protocol.packet.KeepAlivePacket; +import com.velocitypowered.proxy.protocol.packet.LegacyPlayerListItemPacket; +import com.velocitypowered.proxy.protocol.packet.LoginAcknowledgedPacket; +import com.velocitypowered.proxy.protocol.packet.LoginPluginMessagePacket; +import com.velocitypowered.proxy.protocol.packet.LoginPluginResponsePacket; +import com.velocitypowered.proxy.protocol.packet.PingIdentifyPacket; +import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket; +import com.velocitypowered.proxy.protocol.packet.RemovePlayerInfoPacket; +import com.velocitypowered.proxy.protocol.packet.RemoveResourcePackPacket; +import com.velocitypowered.proxy.protocol.packet.ResourcePackRequestPacket; +import com.velocitypowered.proxy.protocol.packet.ResourcePackResponsePacket; +import com.velocitypowered.proxy.protocol.packet.RespawnPacket; +import com.velocitypowered.proxy.protocol.packet.ServerDataPacket; +import com.velocitypowered.proxy.protocol.packet.ServerLoginPacket; +import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccessPacket; +import com.velocitypowered.proxy.protocol.packet.ServerboundCookieResponsePacket; +import com.velocitypowered.proxy.protocol.packet.ServerboundCustomClickActionPacket; +import com.velocitypowered.proxy.protocol.packet.SetCompressionPacket; +import com.velocitypowered.proxy.protocol.packet.StatusPingPacket; +import com.velocitypowered.proxy.protocol.packet.StatusRequestPacket; +import com.velocitypowered.proxy.protocol.packet.StatusResponsePacket; +import com.velocitypowered.proxy.protocol.packet.TabCompleteRequestPacket; +import com.velocitypowered.proxy.protocol.packet.TabCompleteResponsePacket; +import com.velocitypowered.proxy.protocol.packet.TransferPacket; +import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket; +import com.velocitypowered.proxy.protocol.packet.chat.ChatAcknowledgementPacket; +import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletionPacket; +import com.velocitypowered.proxy.protocol.packet.chat.SystemChatPacket; +import com.velocitypowered.proxy.protocol.packet.chat.keyed.KeyedPlayerChatPacket; +import com.velocitypowered.proxy.protocol.packet.chat.keyed.KeyedPlayerCommandPacket; +import com.velocitypowered.proxy.protocol.packet.chat.legacy.LegacyChatPacket; +import com.velocitypowered.proxy.protocol.packet.chat.session.SessionPlayerChatPacket; +import com.velocitypowered.proxy.protocol.packet.chat.session.SessionPlayerCommandPacket; +import com.velocitypowered.proxy.protocol.packet.chat.session.UnsignedPlayerCommandPacket; +import com.velocitypowered.proxy.protocol.packet.config.ActiveFeaturesPacket; +import com.velocitypowered.proxy.protocol.packet.config.ClientboundCustomReportDetailsPacket; +import com.velocitypowered.proxy.protocol.packet.config.ClientboundServerLinksPacket; +import com.velocitypowered.proxy.protocol.packet.config.CodeOfConductAcceptPacket; +import com.velocitypowered.proxy.protocol.packet.config.CodeOfConductPacket; +import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket; +import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket; +import com.velocitypowered.proxy.protocol.packet.config.RegistrySyncPacket; +import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket; +import com.velocitypowered.proxy.protocol.packet.config.TagsUpdatePacket; +import com.velocitypowered.proxy.protocol.packet.title.LegacyTitlePacket; +import com.velocitypowered.proxy.protocol.packet.title.TitleActionbarPacket; +import com.velocitypowered.proxy.protocol.packet.title.TitleClearPacket; +import com.velocitypowered.proxy.protocol.packet.title.TitleSubtitlePacket; +import com.velocitypowered.proxy.protocol.packet.title.TitleTextPacket; +import com.velocitypowered.proxy.protocol.packet.title.TitleTimesPacket; +import com.velocitypowered.proxy.protocol.registry.MultiVersionPacketRegistry; +import com.velocitypowered.proxy.protocol.registry.MultiVersionPacketRegistry.VersionRange; +import com.velocitypowered.proxy.protocol.registry.ProtocolToPacketRegistry; +import com.velocitypowered.proxy.protocol.registry.SimplePacketRegistry; + +/** + * Defines the packet mappings for all protocol states and versions supported by Velocity. + */ +public class ProtocolStates { + + public static final ProtocolToPacketRegistry HANDSHAKE_CLIENTBOUND; + public static final ProtocolToPacketRegistry HANDSHAKE_SERVERBOUND; + public static final ProtocolToPacketRegistry STATUS_SERVERBOUND; + public static final ProtocolToPacketRegistry STATUS_CLIENTBOUND; + public static final ProtocolToPacketRegistry CONFIG_SERVERBOUND; + public static final ProtocolToPacketRegistry CONFIG_CLIENTBOUND; + public static final ProtocolToPacketRegistry PLAY_SERVERBOUND; + public static final ProtocolToPacketRegistry PLAY_CLIENTBOUND; + public static final ProtocolToPacketRegistry LOGIN_SERVERBOUND; + public static final ProtocolToPacketRegistry LOGIN_CLIENTBOUND; + + static { + HANDSHAKE_CLIENTBOUND = new SimplePacketRegistry(Direction.CLIENTBOUND); + + SimplePacketRegistry handshakeServerbound = new SimplePacketRegistry(Direction.SERVERBOUND); + handshakeServerbound.register(0x00, HandshakePacket.class, HandshakePacket.Codec.INSTANCE); + HANDSHAKE_SERVERBOUND = handshakeServerbound; + + SimplePacketRegistry statusServerbound = new SimplePacketRegistry(Direction.SERVERBOUND); + statusServerbound.register(0x00, + StatusRequestPacket.class, StatusRequestPacket.Codec.INSTANCE); + statusServerbound.register(0x01, StatusPingPacket.class, StatusPingPacket.Codec.INSTANCE); + STATUS_SERVERBOUND = statusServerbound; + + SimplePacketRegistry statusClientbound = new SimplePacketRegistry(Direction.CLIENTBOUND); + statusClientbound.register(0x00, + StatusResponsePacket.class, StatusResponsePacket.Codec.INSTANCE); + statusClientbound.register(0x01, StatusPingPacket.class, StatusPingPacket.Codec.INSTANCE); + STATUS_CLIENTBOUND = statusClientbound; + + CONFIG_SERVERBOUND = MultiVersionPacketRegistry.builder(Direction.SERVERBOUND) + .register(ClientSettingsPacket.class, ClientSettingsPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, 0x00)) + .register(ServerboundCookieResponsePacket.class, + ServerboundCookieResponsePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, 0x01)) + .register(PluginMessagePacket.class, PluginMessagePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x01), + VersionRange.of(MINECRAFT_1_20_5, 0x02)) + .register(FinishedUpdatePacket.class, FinishedUpdatePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x02), + VersionRange.of(MINECRAFT_1_20_5, 0x03)) + .register(KeepAlivePacket.class, KeepAlivePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x03), + VersionRange.of(MINECRAFT_1_20_5, 0x04)) + .register(PingIdentifyPacket.class, PingIdentifyPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x04), + VersionRange.of(MINECRAFT_1_20_5, 0x05)) + .register(ResourcePackResponsePacket.class, ResourcePackResponsePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x05), + VersionRange.of(MINECRAFT_1_20_5, 0x06)) + .register(KnownPacksPacket.class, KnownPacksPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, 0x07)) + .register(ServerboundCustomClickActionPacket.class, + ServerboundCustomClickActionPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_21_6, 0x08)) + .register(CodeOfConductAcceptPacket.class, CodeOfConductAcceptPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_21_9, 0x09)) + .build(); + + CONFIG_CLIENTBOUND = MultiVersionPacketRegistry.builder(Direction.CLIENTBOUND) + .register(ClientboundCookieRequestPacket.class, + ClientboundCookieRequestPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, 0x00)) + .register(PluginMessagePacket.class, PluginMessagePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x00), + VersionRange.of(MINECRAFT_1_20_5, 0x01)) + .register(DisconnectPacket.class, new DisconnectPacket.Codec(StateRegistry.CONFIG), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x01), + VersionRange.of(MINECRAFT_1_20_5, 0x02)) + .register(FinishedUpdatePacket.class, FinishedUpdatePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x02), + VersionRange.of(MINECRAFT_1_20_5, 0x03)) + .register(KeepAlivePacket.class, KeepAlivePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x03), + VersionRange.of(MINECRAFT_1_20_5, 0x04)) + .register(PingIdentifyPacket.class, PingIdentifyPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x04), + VersionRange.of(MINECRAFT_1_20_5, 0x05)) + .register(RegistrySyncPacket.class, RegistrySyncPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x05), + VersionRange.of(MINECRAFT_1_20_5, 0x07)) + .register(RemoveResourcePackPacket.class, RemoveResourcePackPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x06), + VersionRange.of(MINECRAFT_1_20_5, 0x08)) + .register(ResourcePackRequestPacket.class, ResourcePackRequestPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x06), + VersionRange.of(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x07), + VersionRange.of(MINECRAFT_1_20_5, 0x09)) + .register(ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, 0x0A)) + .register(TransferPacket.class, TransferPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, 0x0B)) + .register(ActiveFeaturesPacket.class, ActiveFeaturesPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x07), + VersionRange.of(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x08), + VersionRange.of(MINECRAFT_1_20_5, 0x0C)) + .register(TagsUpdatePacket.class, TagsUpdatePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x08), + VersionRange.of(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x09), + VersionRange.of(MINECRAFT_1_20_5, 0x0D)) + .register(KnownPacksPacket.class, KnownPacksPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, 0x0E)) + .register(ClientboundCustomReportDetailsPacket.class, + ClientboundCustomReportDetailsPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_21, 0x0F)) + .register(ClientboundServerLinksPacket.class, ClientboundServerLinksPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_21, 0x10)) + .register(DialogClearPacket.class, DialogClearPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_21_6, 0x11)) + .register(DialogShowPacket.class, new DialogShowPacket.Codec(StateRegistry.CONFIG), + VersionRange.of(MINECRAFT_1_21_6, 0x12)) + .register(CodeOfConductPacket.class, CodeOfConductPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_21_9, 0x13)) + .build(); + + LOGIN_SERVERBOUND = MultiVersionPacketRegistry.builder(Direction.SERVERBOUND) + .register(ServerLoginPacket.class, ServerLoginPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, 0x00)) + .register(EncryptionResponsePacket.class, EncryptionResponsePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, 0x01)) + .register(LoginPluginResponsePacket.class, LoginPluginResponsePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_13, 0x02)) + .register(LoginAcknowledgedPacket.class, LoginAcknowledgedPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, 0x03)) + .register(ServerboundCookieResponsePacket.class, + ServerboundCookieResponsePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, 0x04)) + .build(); + + LOGIN_CLIENTBOUND = MultiVersionPacketRegistry.builder(Direction.CLIENTBOUND) + .register(DisconnectPacket.class, new DisconnectPacket.Codec(StateRegistry.LOGIN), + VersionRange.of(MINECRAFT_1_7_2, 0x00)) + .register(EncryptionRequestPacket.class, EncryptionRequestPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, 0x01)) + .register(ServerLoginSuccessPacket.class, ServerLoginSuccessPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, 0x02)) + .register(SetCompressionPacket.class, SetCompressionPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_8, 0x03)) + .register(LoginPluginMessagePacket.class, LoginPluginMessagePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_13, 0x04)) + .register(ClientboundCookieRequestPacket.class, + ClientboundCookieRequestPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, 0x05)) + .build(); + + // PLAY state is much larger, will continue below + PLAY_SERVERBOUND = buildPlayServerbound(); + PLAY_CLIENTBOUND = buildPlayClientbound(); + } + + private static ProtocolToPacketRegistry buildPlayServerbound() { + return MultiVersionPacketRegistry.builder(Direction.SERVERBOUND) + .register(TabCompleteRequestPacket.class, TabCompleteRequestPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x14), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_11, 0x01), + VersionRange.of(MINECRAFT_1_12, MINECRAFT_1_12, 0x02), + VersionRange.of(MINECRAFT_1_12_1, MINECRAFT_1_12_1, 0x01), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_13, 0x05), + VersionRange.of(MINECRAFT_1_14, MINECRAFT_1_18_2, 0x06), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x08), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x09), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x08), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x09), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x0A), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x0B), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x0D), + VersionRange.of(MINECRAFT_1_21_6, 0x0E)) + .register(LegacyChatPacket.class, LegacyChatPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x01), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_11, 0x02), + VersionRange.of(MINECRAFT_1_12, MINECRAFT_1_12, 0x03), + VersionRange.of(MINECRAFT_1_12_1, MINECRAFT_1_13, 0x02), + VersionRange.of(MINECRAFT_1_14, MINECRAFT_1_18_2, 0x03)) + .register(ChatAcknowledgementPacket.class, ChatAcknowledgementPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_21, 0x03), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x04), + VersionRange.of(MINECRAFT_1_21_6, 0x05)) + .register(KeyedPlayerCommandPacket.class, KeyedPlayerCommandPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x03), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x04)) + .register(KeyedPlayerChatPacket.class, KeyedPlayerChatPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x04), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x05)) + .register(SessionPlayerCommandPacket.class, SessionPlayerCommandPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_20_3, 0x04), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x05), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x06), + VersionRange.of(MINECRAFT_1_21_6, 0x07)) + .register(UnsignedPlayerCommandPacket.class, UnsignedPlayerCommandPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x04), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x05), + VersionRange.of(MINECRAFT_1_21_6, 0x06)) + .register(SessionPlayerChatPacket.class, SessionPlayerChatPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_20_3, 0x05), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x06), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x07), + VersionRange.of(MINECRAFT_1_21_6, 0x08)) + .register(ClientSettingsPacket.class, ClientSettingsPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x15), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_11, 0x04), + VersionRange.of(MINECRAFT_1_12, MINECRAFT_1_12, 0x05), + VersionRange.of(MINECRAFT_1_12_1, MINECRAFT_1_13, 0x04), + VersionRange.of(MINECRAFT_1_14, MINECRAFT_1_18_2, 0x05), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x07), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x08), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x07), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x08), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x09), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x0A), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x0C), + VersionRange.of(MINECRAFT_1_21_6, 0x0D)) + .register(ServerboundCookieResponsePacket.class, + ServerboundCookieResponsePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x11), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x13), + VersionRange.of(MINECRAFT_1_21_6, 0x14)) + .register(PluginMessagePacket.class, PluginMessagePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x17), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_11, 0x09), + VersionRange.of(MINECRAFT_1_12, MINECRAFT_1_12, 0x0A), + VersionRange.of(MINECRAFT_1_12_1, MINECRAFT_1_12_1, 0x09), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_13, 0x0A), + VersionRange.of(MINECRAFT_1_14, MINECRAFT_1_16_4, 0x0B), + VersionRange.of(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x0A), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x0C), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x0D), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x0C), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x0D), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x0F), + VersionRange.of(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x10), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x12), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x14), + VersionRange.of(MINECRAFT_1_21_6, 0x15)) + .register(KeepAlivePacket.class, KeepAlivePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x00), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_11, 0x0B), + VersionRange.of(MINECRAFT_1_12, MINECRAFT_1_12, 0x0C), + VersionRange.of(MINECRAFT_1_12_1, MINECRAFT_1_12_1, 0x0B), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_13, 0x0E), + VersionRange.of(MINECRAFT_1_14, MINECRAFT_1_15_2, 0x0F), + VersionRange.of(MINECRAFT_1_16, MINECRAFT_1_16_4, 0x10), + VersionRange.of(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x0F), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x11), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x12), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x11), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x12), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x14), + VersionRange.of(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x15), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x18), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x1A), + VersionRange.of(MINECRAFT_1_21_6, 0x1B)) + .register(ResourcePackResponsePacket.class, ResourcePackResponsePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_8, MINECRAFT_1_8, 0x19), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_11, 0x16), + VersionRange.of(MINECRAFT_1_12, MINECRAFT_1_12, 0x18), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_13, 0x1D), + VersionRange.of(MINECRAFT_1_14, MINECRAFT_1_15_2, 0x1F), + VersionRange.of(MINECRAFT_1_16, MINECRAFT_1_16_1, 0x20), + VersionRange.of(MINECRAFT_1_16_2, MINECRAFT_1_18_2, 0x21), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x23), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_20, 0x24), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x27), + VersionRange.of(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x28), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x2B), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_2, 0x2D), + VersionRange.of(MINECRAFT_1_21_4, MINECRAFT_1_21_4, 0x2F), + VersionRange.of(MINECRAFT_1_21_6, 0x30)) + .register(FinishedUpdatePacket.class, FinishedUpdatePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x0B), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x0C), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x0E), + VersionRange.of(MINECRAFT_1_21_6, 0x0F)) + .build(); + } + + private static ProtocolToPacketRegistry buildPlayClientbound() { + return MultiVersionPacketRegistry.builder(Direction.CLIENTBOUND) + .register(BossBarPacket.class, BossBarPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_14, 0x0C), + VersionRange.of(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x0D), + VersionRange.of(MINECRAFT_1_16, MINECRAFT_1_16_4, 0x0C), + VersionRange.of(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x0D), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19_3, 0x0A), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x0B), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_21_4, 0x0A), + VersionRange.of(MINECRAFT_1_21_5, 0x09)) + .register(LegacyChatPacket.class, LegacyChatPacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x02), + VersionRange.encodeOnly(MINECRAFT_1_9, MINECRAFT_1_12, 0x0F), + VersionRange.encodeOnly(MINECRAFT_1_13, MINECRAFT_1_13, 0x0E), + VersionRange.encodeOnly(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x0F), + VersionRange.encodeOnly(MINECRAFT_1_16, MINECRAFT_1_16_4, 0x0E), + VersionRange.encodeOnly(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x0F)) + .register(TabCompleteResponsePacket.class, TabCompleteResponsePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x3A), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_12, 0x0E), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_14, 0x10), + VersionRange.of(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x11), + VersionRange.of(MINECRAFT_1_16, MINECRAFT_1_16_1, 0x10), + VersionRange.of(MINECRAFT_1_16_2, MINECRAFT_1_16_4, 0x0F), + VersionRange.of(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x11), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19_1, 0x0E), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x0D), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x0F), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_21_4, 0x10), + VersionRange.of(MINECRAFT_1_21_5, 0x0F)) + .register(AvailableCommandsPacket.class, AvailableCommandsPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_14, 0x11), + VersionRange.of(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x12), + VersionRange.of(MINECRAFT_1_16, MINECRAFT_1_16_1, 0x11), + VersionRange.of(MINECRAFT_1_16_2, MINECRAFT_1_16_4, 0x10), + VersionRange.of(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x12), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19_1, 0x0F), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x0E), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x10), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_21_4, 0x11), + VersionRange.of(MINECRAFT_1_21_5, 0x10)) + .register(ClientboundCookieRequestPacket.class, + ClientboundCookieRequestPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21_4, 0x16), + VersionRange.of(MINECRAFT_1_21_5, 0x15)) + .register(ClientboundSoundEntityPacket.class, ClientboundSoundEntityPacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x5D), + VersionRange.encodeOnly(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x61), + VersionRange.encodeOnly(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x63), + VersionRange.encodeOnly(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x65), + VersionRange.encodeOnly(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x67), + VersionRange.encodeOnly(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x6E), + VersionRange.encodeOnly(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x6D), + VersionRange.encodeOnly(MINECRAFT_1_21_9, 0x72)) + .register(ClientboundStopSoundPacket.class, ClientboundStopSoundPacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x5F), + VersionRange.encodeOnly(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x63), + VersionRange.encodeOnly(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x66), + VersionRange.encodeOnly(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x68), + VersionRange.encodeOnly(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x6A), + VersionRange.encodeOnly(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x71), + VersionRange.encodeOnly(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x70), + VersionRange.encodeOnly(MINECRAFT_1_21_9, 0x75)) + .register(PluginMessagePacket.class, PluginMessagePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x3F), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_12, 0x18), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_13, 0x19), + VersionRange.of(MINECRAFT_1_14, MINECRAFT_1_14, 0x18), + VersionRange.of(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x19), + VersionRange.of(MINECRAFT_1_16, MINECRAFT_1_16_1, 0x18), + VersionRange.of(MINECRAFT_1_16_2, MINECRAFT_1_16_4, 0x17), + VersionRange.of(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x18), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x15), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x16), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x15), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x17), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x18), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21_4, 0x19), + VersionRange.of(MINECRAFT_1_21_5, 0x18)) + .register(DisconnectPacket.class, new DisconnectPacket.Codec(StateRegistry.PLAY), + VersionRange.of(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x40), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_12, 0x1A), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_13, 0x1B), + VersionRange.of(MINECRAFT_1_14, MINECRAFT_1_14, 0x1A), + VersionRange.of(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x1B), + VersionRange.of(MINECRAFT_1_16, MINECRAFT_1_16_1, 0x1A), + VersionRange.of(MINECRAFT_1_16_2, MINECRAFT_1_16_4, 0x19), + VersionRange.of(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x1A), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x17), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x19), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x17), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x1A), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x1B), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21_4, 0x1D), + VersionRange.of(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x1C), + VersionRange.of(MINECRAFT_1_21_9, 0x20)) + .register(KeepAlivePacket.class, KeepAlivePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x00), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_12, 0x1F), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_13, 0x21), + VersionRange.of(MINECRAFT_1_14, MINECRAFT_1_14_4, 0x20), + VersionRange.of(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x21), + VersionRange.of(MINECRAFT_1_16, MINECRAFT_1_16_1, 0x20), + VersionRange.of(MINECRAFT_1_16_2, MINECRAFT_1_16_4, 0x1F), + VersionRange.of(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x21), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x1E), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x20), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x1F), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x23), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x24), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x26), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x27), + VersionRange.of(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x26), + VersionRange.of(MINECRAFT_1_21_9, 0x2B)) + .register(JoinGamePacket.class, JoinGamePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x01), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_12, 0x23), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_14, 0x25), + VersionRange.of(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x26), + VersionRange.of(MINECRAFT_1_16, MINECRAFT_1_16_1, 0x25), + VersionRange.of(MINECRAFT_1_16_2, MINECRAFT_1_16_4, 0x24), + VersionRange.of(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x26), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x23), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x25), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x24), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x28), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x29), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x2B), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x2C), + VersionRange.of(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x2B), + VersionRange.of(MINECRAFT_1_21_9, 0x30)) + .register(RespawnPacket.class, RespawnPacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x07), + VersionRange.encodeOnly(MINECRAFT_1_9, MINECRAFT_1_11, 0x33), + VersionRange.encodeOnly(MINECRAFT_1_12, MINECRAFT_1_12, 0x34), + VersionRange.encodeOnly(MINECRAFT_1_12_1, MINECRAFT_1_12_1, 0x35), + VersionRange.encodeOnly(MINECRAFT_1_13, MINECRAFT_1_13, 0x38), + VersionRange.encodeOnly(MINECRAFT_1_14, MINECRAFT_1_14, 0x3A), + VersionRange.encodeOnly(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x3B), + VersionRange.encodeOnly(MINECRAFT_1_16, MINECRAFT_1_16_1, 0x3A), + VersionRange.encodeOnly(MINECRAFT_1_16_2, MINECRAFT_1_16_4, 0x39), + VersionRange.encodeOnly(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x3D), + VersionRange.encodeOnly(MINECRAFT_1_19, MINECRAFT_1_19, 0x3B), + VersionRange.encodeOnly(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x3E), + VersionRange.encodeOnly(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x3D), + VersionRange.encodeOnly(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x41), + VersionRange.encodeOnly(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x43), + VersionRange.encodeOnly(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x45), + VersionRange.encodeOnly(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x47), + VersionRange.encodeOnly(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x4C), + VersionRange.encodeOnly(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x4B), + VersionRange.encodeOnly(MINECRAFT_1_21_9, 0x50)) + .register(RemoveResourcePackPacket.class, RemoveResourcePackPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x43), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x45), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x4A), + VersionRange.of(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x49), + VersionRange.of(MINECRAFT_1_21_9, 0x4E)) + .register(ResourcePackRequestPacket.class, ResourcePackRequestPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_8, MINECRAFT_1_8, 0x48), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_11, 0x32), + VersionRange.of(MINECRAFT_1_12, MINECRAFT_1_12, 0x33), + VersionRange.of(MINECRAFT_1_12_1, MINECRAFT_1_12_1, 0x34), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_13, 0x37), + VersionRange.of(MINECRAFT_1_14, MINECRAFT_1_14, 0x39), + VersionRange.of(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x3A), + VersionRange.of(MINECRAFT_1_16, MINECRAFT_1_16_1, 0x39), + VersionRange.of(MINECRAFT_1_16_2, MINECRAFT_1_16_4, 0x38), + VersionRange.of(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x3C), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x3A), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x3D), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x3C), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x40), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x42), + VersionRange.of(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x44), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x46), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x4B), + VersionRange.of(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x4A), + VersionRange.of(MINECRAFT_1_21_9, 0x4F)) + .register(HeaderAndFooterPacket.class, HeaderAndFooterPacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_8, MINECRAFT_1_8, 0x47), + VersionRange.encodeOnly(MINECRAFT_1_9, MINECRAFT_1_9_1, 0x48), + VersionRange.encodeOnly(MINECRAFT_1_9_4, MINECRAFT_1_11, 0x47), + VersionRange.encodeOnly(MINECRAFT_1_12, MINECRAFT_1_12, 0x49), + VersionRange.encodeOnly(MINECRAFT_1_12_1, MINECRAFT_1_12_1, 0x4A), + VersionRange.encodeOnly(MINECRAFT_1_13, MINECRAFT_1_13, 0x4E), + VersionRange.encodeOnly(MINECRAFT_1_14, MINECRAFT_1_14, 0x53), + VersionRange.encodeOnly(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x54), + VersionRange.encodeOnly(MINECRAFT_1_16, MINECRAFT_1_16_4, 0x53), + VersionRange.encodeOnly(MINECRAFT_1_17, MINECRAFT_1_17_1, 0x5E), + VersionRange.encodeOnly(MINECRAFT_1_18, MINECRAFT_1_18_2, 0x5F), + VersionRange.encodeOnly(MINECRAFT_1_19, MINECRAFT_1_19, 0x60), + VersionRange.encodeOnly(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x63), + VersionRange.encodeOnly(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x61), + VersionRange.encodeOnly(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x65), + VersionRange.encodeOnly(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x68), + VersionRange.encodeOnly(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x6A), + VersionRange.encodeOnly(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x6D), + VersionRange.encodeOnly(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x74), + VersionRange.encodeOnly(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x73), + VersionRange.encodeOnly(MINECRAFT_1_21_9, 0x78)) + .register(LegacyTitlePacket.class, LegacyTitlePacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_8, MINECRAFT_1_11, 0x45), + VersionRange.encodeOnly(MINECRAFT_1_12, MINECRAFT_1_12, 0x47), + VersionRange.encodeOnly(MINECRAFT_1_12_1, MINECRAFT_1_12_1, 0x48), + VersionRange.encodeOnly(MINECRAFT_1_13, MINECRAFT_1_13, 0x4B), + VersionRange.encodeOnly(MINECRAFT_1_14, MINECRAFT_1_14_4, 0x4F), + VersionRange.encodeOnly(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x50), + VersionRange.encodeOnly(MINECRAFT_1_16, MINECRAFT_1_16_4, 0x4F)) + .register(TitleSubtitlePacket.class, TitleSubtitlePacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_17, MINECRAFT_1_17_1, 0x57), + VersionRange.encodeOnly(MINECRAFT_1_18, MINECRAFT_1_18_2, 0x58), + VersionRange.encodeOnly(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x5B), + VersionRange.encodeOnly(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x59), + VersionRange.encodeOnly(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x5D), + VersionRange.encodeOnly(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x5F), + VersionRange.encodeOnly(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x61), + VersionRange.encodeOnly(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x63), + VersionRange.encodeOnly(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x6A), + VersionRange.encodeOnly(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x69), + VersionRange.encodeOnly(MINECRAFT_1_21_9, 0x6E)) + .register(TitleTextPacket.class, TitleTextPacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_17, MINECRAFT_1_17_1, 0x59), + VersionRange.encodeOnly(MINECRAFT_1_18, MINECRAFT_1_18_2, 0x5A), + VersionRange.encodeOnly(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x5D), + VersionRange.encodeOnly(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x5B), + VersionRange.encodeOnly(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x5F), + VersionRange.encodeOnly(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x61), + VersionRange.encodeOnly(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x63), + VersionRange.encodeOnly(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x65), + VersionRange.encodeOnly(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x6C), + VersionRange.encodeOnly(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x6B), + VersionRange.encodeOnly(MINECRAFT_1_21_9, 0x70)) + .register(TitleActionbarPacket.class, TitleActionbarPacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x41), + VersionRange.encodeOnly(MINECRAFT_1_19, MINECRAFT_1_19, 0x40), + VersionRange.encodeOnly(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x43), + VersionRange.encodeOnly(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x42), + VersionRange.encodeOnly(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x46), + VersionRange.encodeOnly(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x48), + VersionRange.encodeOnly(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x4A), + VersionRange.encodeOnly(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x4C), + VersionRange.encodeOnly(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x51), + VersionRange.encodeOnly(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x50), + VersionRange.encodeOnly(MINECRAFT_1_21_9, 0x55)) + .register(TitleTimesPacket.class, TitleTimesPacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_17, MINECRAFT_1_17_1, 0x5A), + VersionRange.encodeOnly(MINECRAFT_1_18, MINECRAFT_1_18_2, 0x5B), + VersionRange.encodeOnly(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x5E), + VersionRange.encodeOnly(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x5C), + VersionRange.encodeOnly(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x60), + VersionRange.encodeOnly(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x62), + VersionRange.encodeOnly(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x64), + VersionRange.encodeOnly(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x66), + VersionRange.encodeOnly(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x6D), + VersionRange.encodeOnly(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x6C), + VersionRange.encodeOnly(MINECRAFT_1_21_9, 0x71)) + .register(TitleClearPacket.class, TitleClearPacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x10), + VersionRange.encodeOnly(MINECRAFT_1_19, MINECRAFT_1_19_1, 0x0D), + VersionRange.encodeOnly(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x0C), + VersionRange.encodeOnly(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x0E), + VersionRange.encodeOnly(MINECRAFT_1_20_2, MINECRAFT_1_21_4, 0x0F), + VersionRange.encodeOnly(MINECRAFT_1_21_5, 0x0E)) + .register(LegacyPlayerListItemPacket.class, LegacyPlayerListItemPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_7_2, MINECRAFT_1_8, 0x38), + VersionRange.of(MINECRAFT_1_9, MINECRAFT_1_12, 0x2D), + VersionRange.of(MINECRAFT_1_12_1, MINECRAFT_1_12_1, 0x2E), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_13, 0x30), + VersionRange.of(MINECRAFT_1_14, MINECRAFT_1_14, 0x33), + VersionRange.of(MINECRAFT_1_15, MINECRAFT_1_15_2, 0x34), + VersionRange.of(MINECRAFT_1_16, MINECRAFT_1_16_1, 0x33), + VersionRange.of(MINECRAFT_1_16_2, MINECRAFT_1_16_4, 0x32), + VersionRange.of(MINECRAFT_1_17, MINECRAFT_1_18_2, 0x36), + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x34), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x37)) + .register(RemovePlayerInfoPacket.class, RemovePlayerInfoPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x35), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x39), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x3B), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x3D), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x3F), + VersionRange.of(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x3E), + VersionRange.of(MINECRAFT_1_21_9, 0x43)) + .register(UpsertPlayerInfoPacket.class, UpsertPlayerInfoPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x36), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x3A), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x3C), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x3E), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x40), + VersionRange.of(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x3F), + VersionRange.of(MINECRAFT_1_21_9, 0x44)) + .register(ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x6B), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x72), + VersionRange.of(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x71), + VersionRange.of(MINECRAFT_1_21_9, 0x76)) + .register(SystemChatPacket.class, SystemChatPacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_19, MINECRAFT_1_19, 0x5F), + VersionRange.encodeOnly(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x62), + VersionRange.encodeOnly(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x60), + VersionRange.encodeOnly(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x64), + VersionRange.encodeOnly(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x67), + VersionRange.encodeOnly(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x69), + VersionRange.encodeOnly(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x6C), + VersionRange.encodeOnly(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x73), + VersionRange.encodeOnly(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x72), + VersionRange.encodeOnly(MINECRAFT_1_21_9, 0x77)) + .register(PlayerChatCompletionPacket.class, PlayerChatCompletionPacket.Codec.INSTANCE, + VersionRange.encodeOnly(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x15), + VersionRange.encodeOnly(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x14), + VersionRange.encodeOnly(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x16), + VersionRange.encodeOnly(MINECRAFT_1_20_2, MINECRAFT_1_20_3, 0x17), + VersionRange.encodeOnly(MINECRAFT_1_20_5, MINECRAFT_1_21_4, 0x18), + VersionRange.encodeOnly(MINECRAFT_1_21_5, 0x17)) + .register(ServerDataPacket.class, ServerDataPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_19, MINECRAFT_1_19, 0x3F), + VersionRange.of(MINECRAFT_1_19_1, MINECRAFT_1_19_1, 0x42), + VersionRange.of(MINECRAFT_1_19_3, MINECRAFT_1_19_3, 0x41), + VersionRange.of(MINECRAFT_1_19_4, MINECRAFT_1_20, 0x45), + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x47), + VersionRange.of(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x49), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x4B), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x50), + VersionRange.of(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x4F), + VersionRange.of(MINECRAFT_1_21_9, 0x54)) + .register(StartUpdatePacket.class, StartUpdatePacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_2, MINECRAFT_1_20_2, 0x65), + VersionRange.of(MINECRAFT_1_20_3, MINECRAFT_1_20_3, 0x67), + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x69), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_4, 0x70), + VersionRange.of(MINECRAFT_1_21_5, MINECRAFT_1_21_6, 0x6F), + VersionRange.of(MINECRAFT_1_21_9, 0x74)) + .register(BundleDelimiterPacket.class, BundleDelimiterPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_19_4, 0x00)) + .register(TransferPacket.class, TransferPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_20_5, MINECRAFT_1_21, 0x73), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_6, 0x7A), + VersionRange.of(MINECRAFT_1_21_9, 0x7F)) + .register(ClientboundCustomReportDetailsPacket.class, + ClientboundCustomReportDetailsPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_21, MINECRAFT_1_21, 0x7A), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_6, 0x81), + VersionRange.of(MINECRAFT_1_21_9, 0x86)) + .register(ClientboundServerLinksPacket.class, ClientboundServerLinksPacket.Codec.INSTANCE, + VersionRange.of(MINECRAFT_1_21, MINECRAFT_1_21, 0x7B), + VersionRange.of(MINECRAFT_1_21_2, MINECRAFT_1_21_6, 0x82), + VersionRange.of(MINECRAFT_1_21_9, 0x87)) + .build(); + } + + public static ProtocolToPacketRegistry handshake(Direction direction) { + return direction == Direction.SERVERBOUND ? HANDSHAKE_SERVERBOUND : HANDSHAKE_CLIENTBOUND; + } + + public static ProtocolToPacketRegistry status(Direction direction) { + return direction == Direction.SERVERBOUND ? STATUS_SERVERBOUND : STATUS_CLIENTBOUND; + } + + public static ProtocolToPacketRegistry login(Direction direction) { + return direction == Direction.SERVERBOUND ? LOGIN_SERVERBOUND : LOGIN_CLIENTBOUND; + } + + public static ProtocolToPacketRegistry configuration(Direction direction) { + return direction == Direction.SERVERBOUND ? CONFIG_SERVERBOUND : CONFIG_CLIENTBOUND; + } + + public static ProtocolToPacketRegistry play(Direction direction) { + return direction == Direction.SERVERBOUND ? PLAY_SERVERBOUND : PLAY_CLIENTBOUND; + } + + /** + * Looks up the packet registry for the specified state and direction. + * + * @param registry the protocol state + * @param direction the packet direction + * @return the packet registry for the specified state and direction + */ + public static ProtocolToPacketRegistry lookup(StateRegistry registry, Direction direction) { + return switch (registry) { + case HANDSHAKE -> handshake(direction); + case STATUS -> status(direction); + case LOGIN -> login(direction); + case CONFIG -> configuration(direction); + case PLAY -> play(direction); + }; + } +} 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 67b010879a..50eaa7c04b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -17,844 +17,23 @@ package com.velocitypowered.proxy.protocol; -import static com.google.common.collect.Iterables.getLast; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12_1; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_15; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_2; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_4; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_17; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_18; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_18_2; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_1; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_3; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_4; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_2; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_3; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_5; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_2; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_4; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_5; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_6; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_9; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; -import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9_4; -import static com.velocitypowered.api.network.ProtocolVersion.MINIMUM_VERSION; -import static com.velocitypowered.api.network.ProtocolVersion.SUPPORTED_VERSIONS; -import static com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; -import static com.velocitypowered.proxy.protocol.ProtocolUtils.Direction.CLIENTBOUND; -import static com.velocitypowered.proxy.protocol.ProtocolUtils.Direction.SERVERBOUND; import com.velocitypowered.api.network.ProtocolState; -import com.velocitypowered.api.network.ProtocolVersion; -import com.velocitypowered.proxy.protocol.packet.AvailableCommandsPacket; -import com.velocitypowered.proxy.protocol.packet.BossBarPacket; -import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket; -import com.velocitypowered.proxy.protocol.packet.ClientSettingsPacket; -import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket; -import com.velocitypowered.proxy.protocol.packet.ClientboundSoundEntityPacket; -import com.velocitypowered.proxy.protocol.packet.ClientboundStopSoundPacket; -import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket; -import com.velocitypowered.proxy.protocol.packet.DialogClearPacket; -import com.velocitypowered.proxy.protocol.packet.DialogShowPacket; -import com.velocitypowered.proxy.protocol.packet.DisconnectPacket; -import com.velocitypowered.proxy.protocol.packet.EncryptionRequestPacket; -import com.velocitypowered.proxy.protocol.packet.EncryptionResponsePacket; -import com.velocitypowered.proxy.protocol.packet.HandshakePacket; -import com.velocitypowered.proxy.protocol.packet.HeaderAndFooterPacket; -import com.velocitypowered.proxy.protocol.packet.JoinGamePacket; -import com.velocitypowered.proxy.protocol.packet.KeepAlivePacket; -import com.velocitypowered.proxy.protocol.packet.LegacyPlayerListItemPacket; -import com.velocitypowered.proxy.protocol.packet.LoginAcknowledgedPacket; -import com.velocitypowered.proxy.protocol.packet.LoginPluginMessagePacket; -import com.velocitypowered.proxy.protocol.packet.LoginPluginResponsePacket; -import com.velocitypowered.proxy.protocol.packet.PingIdentifyPacket; -import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket; -import com.velocitypowered.proxy.protocol.packet.RemovePlayerInfoPacket; -import com.velocitypowered.proxy.protocol.packet.RemoveResourcePackPacket; -import com.velocitypowered.proxy.protocol.packet.ResourcePackRequestPacket; -import com.velocitypowered.proxy.protocol.packet.ResourcePackResponsePacket; -import com.velocitypowered.proxy.protocol.packet.RespawnPacket; -import com.velocitypowered.proxy.protocol.packet.ServerDataPacket; -import com.velocitypowered.proxy.protocol.packet.ServerLoginPacket; -import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccessPacket; -import com.velocitypowered.proxy.protocol.packet.ServerboundCookieResponsePacket; -import com.velocitypowered.proxy.protocol.packet.ServerboundCustomClickActionPacket; -import com.velocitypowered.proxy.protocol.packet.SetCompressionPacket; -import com.velocitypowered.proxy.protocol.packet.StatusPingPacket; -import com.velocitypowered.proxy.protocol.packet.StatusRequestPacket; -import com.velocitypowered.proxy.protocol.packet.StatusResponsePacket; -import com.velocitypowered.proxy.protocol.packet.TabCompleteRequestPacket; -import com.velocitypowered.proxy.protocol.packet.TabCompleteResponsePacket; -import com.velocitypowered.proxy.protocol.packet.TransferPacket; -import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket; -import com.velocitypowered.proxy.protocol.packet.chat.ChatAcknowledgementPacket; -import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletionPacket; -import com.velocitypowered.proxy.protocol.packet.chat.SystemChatPacket; -import com.velocitypowered.proxy.protocol.packet.chat.keyed.KeyedPlayerChatPacket; -import com.velocitypowered.proxy.protocol.packet.chat.keyed.KeyedPlayerCommandPacket; -import com.velocitypowered.proxy.protocol.packet.chat.legacy.LegacyChatPacket; -import com.velocitypowered.proxy.protocol.packet.chat.session.SessionPlayerChatPacket; -import com.velocitypowered.proxy.protocol.packet.chat.session.SessionPlayerCommandPacket; -import com.velocitypowered.proxy.protocol.packet.chat.session.UnsignedPlayerCommandPacket; -import com.velocitypowered.proxy.protocol.packet.config.ActiveFeaturesPacket; -import com.velocitypowered.proxy.protocol.packet.config.ClientboundCustomReportDetailsPacket; -import com.velocitypowered.proxy.protocol.packet.config.ClientboundServerLinksPacket; -import com.velocitypowered.proxy.protocol.packet.config.CodeOfConductAcceptPacket; -import com.velocitypowered.proxy.protocol.packet.config.CodeOfConductPacket; -import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket; -import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket; -import com.velocitypowered.proxy.protocol.packet.config.RegistrySyncPacket; -import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket; -import com.velocitypowered.proxy.protocol.packet.config.TagsUpdatePacket; -import com.velocitypowered.proxy.protocol.packet.title.LegacyTitlePacket; -import com.velocitypowered.proxy.protocol.packet.title.TitleActionbarPacket; -import com.velocitypowered.proxy.protocol.packet.title.TitleClearPacket; -import com.velocitypowered.proxy.protocol.packet.title.TitleSubtitlePacket; -import com.velocitypowered.proxy.protocol.packet.title.TitleTextPacket; -import com.velocitypowered.proxy.protocol.packet.title.TitleTimesPacket; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.netty.util.collection.IntObjectHashMap; -import io.netty.util.collection.IntObjectMap; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import java.util.Collections; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.Map; -import java.util.Objects; -import java.util.function.Supplier; -import org.checkerframework.checker.nullness.qual.Nullable; /** * Registry of all Minecraft protocol states and the packets for each state. */ public enum StateRegistry { - HANDSHAKE { - { - serverbound.register(HandshakePacket.class, HandshakePacket::new, - map(0x00, MINECRAFT_1_7_2, false)); - } - }, - STATUS { - { - serverbound.register( - StatusRequestPacket.class, () -> StatusRequestPacket.INSTANCE, - map(0x00, MINECRAFT_1_7_2, false)); - serverbound.register(StatusPingPacket.class, StatusPingPacket::new, - map(0x01, MINECRAFT_1_7_2, false)); - - clientbound.register( - StatusResponsePacket.class, StatusResponsePacket::new, - map(0x00, MINECRAFT_1_7_2, false)); - clientbound.register(StatusPingPacket.class, StatusPingPacket::new, - map(0x01, MINECRAFT_1_7_2, false)); - } - }, - CONFIG { - { - serverbound.register( - ClientSettingsPacket.class, ClientSettingsPacket::new, - map(0x00, MINECRAFT_1_20_2, false)); - serverbound.register( - ServerboundCookieResponsePacket.class, ServerboundCookieResponsePacket::new, - map(0x01, MINECRAFT_1_20_5, false)); - serverbound.register( - PluginMessagePacket.class, PluginMessagePacket::new, - map(0x01, MINECRAFT_1_20_2, false), - map(0x02, MINECRAFT_1_20_5, false)); - serverbound.register( - FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE, - map(0x02, MINECRAFT_1_20_2, false), - map(0x03, MINECRAFT_1_20_5, false)); - serverbound.register(KeepAlivePacket.class, KeepAlivePacket::new, - map(0x03, MINECRAFT_1_20_2, false), - map(0x04, MINECRAFT_1_20_5, false)); - serverbound.register( - PingIdentifyPacket.class, PingIdentifyPacket::new, - map(0x04, MINECRAFT_1_20_2, false), - map(0x05, MINECRAFT_1_20_5, false)); - serverbound.register( - ResourcePackResponsePacket.class, - ResourcePackResponsePacket::new, - map(0x05, MINECRAFT_1_20_2, false), - map(0x06, MINECRAFT_1_20_5, false)); - serverbound.register( - KnownPacksPacket.class, - KnownPacksPacket::new, - map(0x07, MINECRAFT_1_20_5, false)); - serverbound.register(ServerboundCustomClickActionPacket.class, ServerboundCustomClickActionPacket::new, - map(0x08, MINECRAFT_1_21_6, false)); - serverbound.register( - CodeOfConductAcceptPacket.class, - () -> CodeOfConductAcceptPacket.INSTANCE, - map(0x09, MINECRAFT_1_21_9, false)); - - clientbound.register( - ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new, - map(0x00, MINECRAFT_1_20_5, false)); - clientbound.register( - PluginMessagePacket.class, PluginMessagePacket::new, - map(0x00, MINECRAFT_1_20_2, false), - map(0x01, MINECRAFT_1_20_5, false)); - clientbound.register( - DisconnectPacket.class, () -> new DisconnectPacket(this), - map(0x01, MINECRAFT_1_20_2, false), - map(0x02, MINECRAFT_1_20_5, false)); - clientbound.register( - FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE, - map(0x02, MINECRAFT_1_20_2, false), - map(0x03, MINECRAFT_1_20_5, false)); - clientbound.register(KeepAlivePacket.class, KeepAlivePacket::new, - map(0x03, MINECRAFT_1_20_2, false), - map(0x04, MINECRAFT_1_20_5, false)); - clientbound.register( - PingIdentifyPacket.class, PingIdentifyPacket::new, - map(0x04, MINECRAFT_1_20_2, false), - map(0x05, MINECRAFT_1_20_5, false)); - clientbound.register( - RegistrySyncPacket.class, RegistrySyncPacket::new, - map(0x05, MINECRAFT_1_20_2, false), - map(0x07, MINECRAFT_1_20_5, false)); - clientbound.register( - RemoveResourcePackPacket.class, RemoveResourcePackPacket::new, - map(0x06, MINECRAFT_1_20_3, false), - map(0x08, MINECRAFT_1_20_5, false)); - clientbound.register(ResourcePackRequestPacket.class, ResourcePackRequestPacket::new, - map(0x06, MINECRAFT_1_20_2, false), - map(0x07, MINECRAFT_1_20_3, false), - map(0x09, MINECRAFT_1_20_5, false)); - clientbound.register( - ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new, - map(0x0A, MINECRAFT_1_20_5, false)); - clientbound.register(TransferPacket.class, TransferPacket::new, - map(0x0B, MINECRAFT_1_20_5, false)); - clientbound.register(ActiveFeaturesPacket.class, ActiveFeaturesPacket::new, - map(0x07, MINECRAFT_1_20_2, false), - map(0x08, MINECRAFT_1_20_3, false), - map(0x0C, MINECRAFT_1_20_5, false)); - clientbound.register(TagsUpdatePacket.class, TagsUpdatePacket::new, - map(0x08, MINECRAFT_1_20_2, false), - map(0x09, MINECRAFT_1_20_3, false), - map(0x0D, MINECRAFT_1_20_5, false)); - clientbound.register(KnownPacksPacket.class, KnownPacksPacket::new, - map(0x0E, MINECRAFT_1_20_5, false)); - clientbound.register(ClientboundCustomReportDetailsPacket.class, ClientboundCustomReportDetailsPacket::new, - map(0x0F, MINECRAFT_1_21, false)); - clientbound.register(ClientboundServerLinksPacket.class, ClientboundServerLinksPacket::new, - map(0x10, MINECRAFT_1_21, false)); - clientbound.register(DialogClearPacket.class, () -> DialogClearPacket.INSTANCE, - map(0x11, MINECRAFT_1_21_6, false)); - clientbound.register(DialogShowPacket.class, () -> new DialogShowPacket(this), - map(0x12, MINECRAFT_1_21_6, false)); - clientbound.register(CodeOfConductPacket.class, CodeOfConductPacket::new, - map(0x13, MINECRAFT_1_21_9, false)); - } - }, - PLAY { - { - serverbound.fallback = false; - clientbound.fallback = false; - - serverbound.register(TabCompleteRequestPacket.class, TabCompleteRequestPacket::new, - map(0x14, MINECRAFT_1_7_2, false), - map(0x01, MINECRAFT_1_9, false), - map(0x02, MINECRAFT_1_12, false), - map(0x01, MINECRAFT_1_12_1, false), - map(0x05, MINECRAFT_1_13, false), - map(0x06, MINECRAFT_1_14, false), - map(0x08, MINECRAFT_1_19, false), - map(0x09, MINECRAFT_1_19_1, false), - map(0x08, MINECRAFT_1_19_3, false), - map(0x09, MINECRAFT_1_19_4, false), - map(0x0A, MINECRAFT_1_20_2, false), - map(0x0B, MINECRAFT_1_20_5, false), - map(0x0D, MINECRAFT_1_21_2, false), - map(0x0E, MINECRAFT_1_21_6, false)); - serverbound.register( - LegacyChatPacket.class, - LegacyChatPacket::new, - map(0x01, MINECRAFT_1_7_2, false), - map(0x02, MINECRAFT_1_9, false), - map(0x03, MINECRAFT_1_12, false), - map(0x02, MINECRAFT_1_12_1, false), - map(0x03, MINECRAFT_1_14, MINECRAFT_1_18_2, false)); - serverbound.register( - ChatAcknowledgementPacket.class, - ChatAcknowledgementPacket::new, - map(0x03, MINECRAFT_1_19_3, false), - map(0x04, MINECRAFT_1_21_2, false), - map(0x05, MINECRAFT_1_21_6, false)); - serverbound.register(KeyedPlayerCommandPacket.class, KeyedPlayerCommandPacket::new, - map(0x03, MINECRAFT_1_19, false), - map(0x04, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false)); - serverbound.register(KeyedPlayerChatPacket.class, KeyedPlayerChatPacket::new, - map(0x04, MINECRAFT_1_19, false), - map(0x05, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false)); - serverbound.register(SessionPlayerCommandPacket.class, SessionPlayerCommandPacket::new, - map(0x04, MINECRAFT_1_19_3, false), - map(0x05, MINECRAFT_1_20_5, false), - map(0x06, MINECRAFT_1_21_2, false), - map(0x07, MINECRAFT_1_21_6, false)); - serverbound.register(UnsignedPlayerCommandPacket.class, UnsignedPlayerCommandPacket::new, - map(0x04, MINECRAFT_1_20_5, false), - map(0x05, MINECRAFT_1_21_2, false), - map(0x06, MINECRAFT_1_21_6, false)); - serverbound.register( - SessionPlayerChatPacket.class, - SessionPlayerChatPacket::new, - map(0x05, MINECRAFT_1_19_3, false), - map(0x06, MINECRAFT_1_20_5, false), - map(0x07, MINECRAFT_1_21_2, false), - map(0x08, MINECRAFT_1_21_6, false)); - serverbound.register( - ClientSettingsPacket.class, - ClientSettingsPacket::new, - map(0x15, MINECRAFT_1_7_2, false), - map(0x04, MINECRAFT_1_9, false), - map(0x05, MINECRAFT_1_12, false), - map(0x04, MINECRAFT_1_12_1, false), - map(0x05, MINECRAFT_1_14, false), - map(0x07, MINECRAFT_1_19, false), - map(0x08, MINECRAFT_1_19_1, false), - map(0x07, MINECRAFT_1_19_3, false), - map(0x08, MINECRAFT_1_19_4, false), - map(0x09, MINECRAFT_1_20_2, false), - map(0x0A, MINECRAFT_1_20_5, false), - map(0x0C, MINECRAFT_1_21_2, false), - map(0x0D, MINECRAFT_1_21_6, false)); - serverbound.register( - ServerboundCookieResponsePacket.class, ServerboundCookieResponsePacket::new, - map(0x11, MINECRAFT_1_20_5, false), - map(0x13, MINECRAFT_1_21_2, false), - map(0x14, MINECRAFT_1_21_6, false)); - serverbound.register( - PluginMessagePacket.class, - PluginMessagePacket::new, - map(0x17, MINECRAFT_1_7_2, false), - map(0x09, MINECRAFT_1_9, false), - map(0x0A, MINECRAFT_1_12, false), - map(0x09, MINECRAFT_1_12_1, false), - map(0x0A, MINECRAFT_1_13, false), - map(0x0B, MINECRAFT_1_14, false), - map(0x0A, MINECRAFT_1_17, false), - map(0x0C, MINECRAFT_1_19, false), - map(0x0D, MINECRAFT_1_19_1, false), - map(0x0C, MINECRAFT_1_19_3, false), - map(0x0D, MINECRAFT_1_19_4, false), - map(0x0F, MINECRAFT_1_20_2, false), - map(0x10, MINECRAFT_1_20_3, false), - map(0x12, MINECRAFT_1_20_5, false), - map(0x14, MINECRAFT_1_21_2, false), - map(0x15, MINECRAFT_1_21_6, false)); - serverbound.register( - KeepAlivePacket.class, - KeepAlivePacket::new, - map(0x00, MINECRAFT_1_7_2, false), - map(0x0B, MINECRAFT_1_9, false), - map(0x0C, MINECRAFT_1_12, false), - map(0x0B, MINECRAFT_1_12_1, false), - map(0x0E, MINECRAFT_1_13, false), - map(0x0F, MINECRAFT_1_14, false), - map(0x10, MINECRAFT_1_16, false), - map(0x0F, MINECRAFT_1_17, false), - map(0x11, MINECRAFT_1_19, false), - map(0x12, MINECRAFT_1_19_1, false), - map(0x11, MINECRAFT_1_19_3, false), - map(0x12, MINECRAFT_1_19_4, false), - map(0x14, MINECRAFT_1_20_2, false), - map(0x15, MINECRAFT_1_20_3, false), - map(0x18, MINECRAFT_1_20_5, false), - map(0x1A, MINECRAFT_1_21_2, false), - map(0x1B, MINECRAFT_1_21_6, false)); - serverbound.register( - ResourcePackResponsePacket.class, - ResourcePackResponsePacket::new, - map(0x19, MINECRAFT_1_8, false), - map(0x16, MINECRAFT_1_9, false), - map(0x18, MINECRAFT_1_12, false), - map(0x1D, MINECRAFT_1_13, false), - map(0x1F, MINECRAFT_1_14, false), - map(0x20, MINECRAFT_1_16, false), - map(0x21, MINECRAFT_1_16_2, false), - map(0x23, MINECRAFT_1_19, false), - map(0x24, MINECRAFT_1_19_1, false), - map(0x27, MINECRAFT_1_20_2, false), - map(0x28, MINECRAFT_1_20_3, false), - map(0x2B, MINECRAFT_1_20_5, false), - map(0x2D, MINECRAFT_1_21_2, false), - map(0x2F, MINECRAFT_1_21_4, false), - map(0x30, MINECRAFT_1_21_6, false)); - serverbound.register( - FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE, - map(0x0B, MINECRAFT_1_20_2, false), - map(0x0C, MINECRAFT_1_20_5, false), - map(0x0E, MINECRAFT_1_21_2, false), - map(0x0F, MINECRAFT_1_21_6, false)); - - clientbound.register( - BossBarPacket.class, - BossBarPacket::new, - map(0x0C, MINECRAFT_1_9, false), - map(0x0D, MINECRAFT_1_15, false), - map(0x0C, MINECRAFT_1_16, false), - map(0x0D, MINECRAFT_1_17, false), - map(0x0A, MINECRAFT_1_19, false), - map(0x0B, MINECRAFT_1_19_4, false), - map(0x0A, MINECRAFT_1_20_2, false), - map(0x09, MINECRAFT_1_21_5, false)); - clientbound.register( - LegacyChatPacket.class, - LegacyChatPacket::new, - map(0x02, MINECRAFT_1_7_2, true), - map(0x0F, MINECRAFT_1_9, true), - map(0x0E, MINECRAFT_1_13, true), - map(0x0F, MINECRAFT_1_15, true), - map(0x0E, MINECRAFT_1_16, true), - map(0x0F, MINECRAFT_1_17, MINECRAFT_1_18_2, true)); - clientbound.register(TabCompleteResponsePacket.class, TabCompleteResponsePacket::new, - map(0x3A, MINECRAFT_1_7_2, false), - map(0x0E, MINECRAFT_1_9, false), - map(0x10, MINECRAFT_1_13, false), - map(0x11, MINECRAFT_1_15, false), - map(0x10, MINECRAFT_1_16, false), - map(0x0F, MINECRAFT_1_16_2, false), - map(0x11, MINECRAFT_1_17, false), - map(0x0E, MINECRAFT_1_19, false), - map(0x0D, MINECRAFT_1_19_3, false), - map(0x0F, MINECRAFT_1_19_4, false), - map(0x10, MINECRAFT_1_20_2, false), - map(0x0F, MINECRAFT_1_21_5, false)); - clientbound.register( - AvailableCommandsPacket.class, - AvailableCommandsPacket::new, - map(0x11, MINECRAFT_1_13, false), - map(0x12, MINECRAFT_1_15, false), - map(0x11, MINECRAFT_1_16, false), - map(0x10, MINECRAFT_1_16_2, false), - map(0x12, MINECRAFT_1_17, false), - map(0x0F, MINECRAFT_1_19, false), - map(0x0E, MINECRAFT_1_19_3, false), - map(0x10, MINECRAFT_1_19_4, false), - map(0x11, MINECRAFT_1_20_2, false), - map(0x10, MINECRAFT_1_21_5, false)); - clientbound.register( - ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new, - map(0x16, MINECRAFT_1_20_5, false), - map(0x15, MINECRAFT_1_21_5, false)); - clientbound.register( - ClientboundSoundEntityPacket.class, ClientboundSoundEntityPacket::new, - map(0x5D, MINECRAFT_1_19_3, true), - map(0x61, MINECRAFT_1_19_4, true), - map(0x63, MINECRAFT_1_20_2, true), - map(0x65, MINECRAFT_1_20_3, true), - map(0x67, MINECRAFT_1_20_5, true), - map(0x6E, MINECRAFT_1_21_2, true), - map(0x6D, MINECRAFT_1_21_5, true), - map(0x72, MINECRAFT_1_21_9, true)); - clientbound.register( - ClientboundStopSoundPacket.class, ClientboundStopSoundPacket::new, - map(0x5F, MINECRAFT_1_19_3, true), - map(0x63, MINECRAFT_1_19_4, true), - map(0x66, MINECRAFT_1_20_2, true), - map(0x68, MINECRAFT_1_20_3, true), - map(0x6A, MINECRAFT_1_20_5, true), - map(0x71, MINECRAFT_1_21_2, true), - map(0x70, MINECRAFT_1_21_5, true), - map(0x75, MINECRAFT_1_21_9, true)); - clientbound.register( - PluginMessagePacket.class, - PluginMessagePacket::new, - map(0x3F, MINECRAFT_1_7_2, 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(0x18, MINECRAFT_1_16, false), - map(0x17, MINECRAFT_1_16_2, false), - map(0x18, MINECRAFT_1_17, false), - map(0x15, MINECRAFT_1_19, false), - map(0x16, MINECRAFT_1_19_1, false), - map(0x15, MINECRAFT_1_19_3, false), - map(0x17, MINECRAFT_1_19_4, false), - map(0x18, MINECRAFT_1_20_2, false), - map(0x19, MINECRAFT_1_20_5, false), - map(0x18, MINECRAFT_1_21_5, false)); - clientbound.register( - DisconnectPacket.class, - () -> new DisconnectPacket(this), - map(0x40, MINECRAFT_1_7_2, 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(0x1A, MINECRAFT_1_16, false), - map(0x19, MINECRAFT_1_16_2, false), - map(0x1A, MINECRAFT_1_17, false), - map(0x17, MINECRAFT_1_19, false), - map(0x19, MINECRAFT_1_19_1, false), - map(0x17, MINECRAFT_1_19_3, false), - map(0x1A, MINECRAFT_1_19_4, false), - map(0x1B, MINECRAFT_1_20_2, false), - map(0x1D, MINECRAFT_1_20_5, false), - map(0x1C, MINECRAFT_1_21_5, false), - map(0x20, MINECRAFT_1_21_9, false)); - clientbound.register( - KeepAlivePacket.class, - KeepAlivePacket::new, - map(0x00, MINECRAFT_1_7_2, 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(0x20, MINECRAFT_1_16, false), - map(0x1F, MINECRAFT_1_16_2, false), - map(0x21, MINECRAFT_1_17, false), - map(0x1E, MINECRAFT_1_19, false), - map(0x20, MINECRAFT_1_19_1, false), - map(0x1F, MINECRAFT_1_19_3, false), - map(0x23, MINECRAFT_1_19_4, false), - map(0x24, MINECRAFT_1_20_2, false), - map(0x26, MINECRAFT_1_20_5, false), - map(0x27, MINECRAFT_1_21_2, false), - map(0x26, MINECRAFT_1_21_5, false), - map(0x2B, MINECRAFT_1_21_9, false)); - clientbound.register( - JoinGamePacket.class, - JoinGamePacket::new, - map(0x01, MINECRAFT_1_7_2, 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(0x25, MINECRAFT_1_16, false), - map(0x24, MINECRAFT_1_16_2, false), - map(0x26, MINECRAFT_1_17, false), - map(0x23, MINECRAFT_1_19, false), - map(0x25, MINECRAFT_1_19_1, false), - map(0x24, MINECRAFT_1_19_3, false), - map(0x28, MINECRAFT_1_19_4, false), - map(0x29, MINECRAFT_1_20_2, false), - map(0x2B, MINECRAFT_1_20_5, false), - map(0x2C, MINECRAFT_1_21_2, false), - map(0x2B, MINECRAFT_1_21_5, false), - map(0x30, MINECRAFT_1_21_9, false)); - clientbound.register( - RespawnPacket.class, - RespawnPacket::new, - map(0x07, MINECRAFT_1_7_2, true), - map(0x33, MINECRAFT_1_9, true), - map(0x34, MINECRAFT_1_12, true), - 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(0x3A, MINECRAFT_1_16, true), - map(0x39, MINECRAFT_1_16_2, true), - map(0x3D, MINECRAFT_1_17, true), - map(0x3B, MINECRAFT_1_19, true), - map(0x3E, MINECRAFT_1_19_1, true), - map(0x3D, MINECRAFT_1_19_3, true), - map(0x41, MINECRAFT_1_19_4, true), - map(0x43, MINECRAFT_1_20_2, true), - map(0x45, MINECRAFT_1_20_3, true), - map(0x47, MINECRAFT_1_20_5, true), - map(0x4C, MINECRAFT_1_21_2, true), - map(0x4B, MINECRAFT_1_21_5, true), - map(0x50, MINECRAFT_1_21_9, true)); - clientbound.register( - RemoveResourcePackPacket.class, - RemoveResourcePackPacket::new, - map(0x43, MINECRAFT_1_20_3, false), - map(0x45, MINECRAFT_1_20_5, false), - map(0x4A, MINECRAFT_1_21_2, false), - map(0x49, MINECRAFT_1_21_5, false), - map(0x4E, MINECRAFT_1_21_9, false)); - clientbound.register( - ResourcePackRequestPacket.class, - ResourcePackRequestPacket::new, - map(0x48, MINECRAFT_1_8, false), - map(0x32, MINECRAFT_1_9, false), - map(0x33, MINECRAFT_1_12, false), - map(0x34, MINECRAFT_1_12_1, false), - map(0x37, MINECRAFT_1_13, false), - map(0x39, MINECRAFT_1_14, false), - map(0x3A, MINECRAFT_1_15, false), - map(0x39, MINECRAFT_1_16, false), - map(0x38, MINECRAFT_1_16_2, false), - map(0x3C, MINECRAFT_1_17, false), - map(0x3A, MINECRAFT_1_19, false), - map(0x3D, MINECRAFT_1_19_1, false), - map(0x3C, MINECRAFT_1_19_3, false), - map(0x40, MINECRAFT_1_19_4, false), - map(0x42, MINECRAFT_1_20_2, false), - map(0x44, MINECRAFT_1_20_3, false), - map(0x46, MINECRAFT_1_20_5, false), - map(0x4B, MINECRAFT_1_21_2, false), - map(0x4A, MINECRAFT_1_21_5, false), - map(0x4F, MINECRAFT_1_21_9, false)); - clientbound.register( - HeaderAndFooterPacket.class, - HeaderAndFooterPacket::new, - map(0x47, MINECRAFT_1_8, true), - map(0x48, MINECRAFT_1_9, true), - map(0x47, MINECRAFT_1_9_4, true), - map(0x49, MINECRAFT_1_12, true), - 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(0x53, MINECRAFT_1_16, true), - map(0x5E, MINECRAFT_1_17, true), - map(0x5F, MINECRAFT_1_18, true), - map(0x60, MINECRAFT_1_19, true), - map(0x63, MINECRAFT_1_19_1, true), - map(0x61, MINECRAFT_1_19_3, true), - map(0x65, MINECRAFT_1_19_4, true), - map(0x68, MINECRAFT_1_20_2, true), - map(0x6A, MINECRAFT_1_20_3, true), - map(0x6D, MINECRAFT_1_20_5, true), - map(0x74, MINECRAFT_1_21_2, true), - map(0x73, MINECRAFT_1_21_5, true), - map(0x78, MINECRAFT_1_21_9, true)); - clientbound.register( - LegacyTitlePacket.class, - LegacyTitlePacket::new, - map(0x45, MINECRAFT_1_8, true), - map(0x45, MINECRAFT_1_9, true), - map(0x47, MINECRAFT_1_12, true), - 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(0x4F, MINECRAFT_1_16, MINECRAFT_1_16_4, true)); - clientbound.register(TitleSubtitlePacket.class, TitleSubtitlePacket::new, - map(0x57, MINECRAFT_1_17, true), - map(0x58, MINECRAFT_1_18, true), - map(0x5B, MINECRAFT_1_19_1, true), - map(0x59, MINECRAFT_1_19_3, true), - map(0x5D, MINECRAFT_1_19_4, true), - map(0x5F, MINECRAFT_1_20_2, true), - map(0x61, MINECRAFT_1_20_3, true), - map(0x63, MINECRAFT_1_20_5, true), - map(0x6A, MINECRAFT_1_21_2, true), - map(0x69, MINECRAFT_1_21_5, true), - map(0x6E, MINECRAFT_1_21_9, true)); - clientbound.register( - TitleTextPacket.class, - TitleTextPacket::new, - map(0x59, MINECRAFT_1_17, true), - map(0x5A, MINECRAFT_1_18, true), - map(0x5D, MINECRAFT_1_19_1, true), - map(0x5B, MINECRAFT_1_19_3, true), - map(0x5F, MINECRAFT_1_19_4, true), - map(0x61, MINECRAFT_1_20_2, true), - map(0x63, MINECRAFT_1_20_3, true), - map(0x65, MINECRAFT_1_20_5, true), - map(0x6C, MINECRAFT_1_21_2, true), - map(0x6B, MINECRAFT_1_21_5, true), - map(0x70, MINECRAFT_1_21_9, true)); - clientbound.register( - TitleActionbarPacket.class, - TitleActionbarPacket::new, - map(0x41, MINECRAFT_1_17, true), - map(0x40, MINECRAFT_1_19, true), - map(0x43, MINECRAFT_1_19_1, true), - map(0x42, MINECRAFT_1_19_3, true), - map(0x46, MINECRAFT_1_19_4, true), - map(0x48, MINECRAFT_1_20_2, true), - map(0x4A, MINECRAFT_1_20_3, true), - map(0x4C, MINECRAFT_1_20_5, true), - map(0x51, MINECRAFT_1_21_2, true), - map(0x50, MINECRAFT_1_21_5, true), - map(0x55, MINECRAFT_1_21_9, true)); - clientbound.register( - TitleTimesPacket.class, - TitleTimesPacket::new, - map(0x5A, MINECRAFT_1_17, true), - map(0x5B, MINECRAFT_1_18, true), - map(0x5E, MINECRAFT_1_19_1, true), - map(0x5C, MINECRAFT_1_19_3, true), - map(0x60, MINECRAFT_1_19_4, true), - map(0x62, MINECRAFT_1_20_2, true), - map(0x64, MINECRAFT_1_20_3, true), - map(0x66, MINECRAFT_1_20_5, true), - map(0x6D, MINECRAFT_1_21_2, true), - map(0x6C, MINECRAFT_1_21_5, true), - map(0x71, MINECRAFT_1_21_9, true)); - clientbound.register( - TitleClearPacket.class, - TitleClearPacket::new, - map(0x10, MINECRAFT_1_17, true), - map(0x0D, MINECRAFT_1_19, true), - map(0x0C, MINECRAFT_1_19_3, true), - map(0x0E, MINECRAFT_1_19_4, true), - map(0x0F, MINECRAFT_1_20_2, true), - map(0x0E, MINECRAFT_1_21_5, true)); - clientbound.register( - LegacyPlayerListItemPacket.class, - LegacyPlayerListItemPacket::new, - map(0x38, MINECRAFT_1_7_2, 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(0x33, MINECRAFT_1_16, false), - map(0x32, MINECRAFT_1_16_2, false), - map(0x36, MINECRAFT_1_17, false), - map(0x34, MINECRAFT_1_19, false), - map(0x37, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false)); - clientbound.register(RemovePlayerInfoPacket.class, RemovePlayerInfoPacket::new, - map(0x35, MINECRAFT_1_19_3, false), - map(0x39, MINECRAFT_1_19_4, false), - map(0x3B, MINECRAFT_1_20_2, false), - map(0x3D, MINECRAFT_1_20_5, false), - map(0x3F, MINECRAFT_1_21_2, false), - map(0x3E, MINECRAFT_1_21_5, false), - map(0x43, MINECRAFT_1_21_9, false)); - clientbound.register( - UpsertPlayerInfoPacket.class, - UpsertPlayerInfoPacket::new, - map(0x36, MINECRAFT_1_19_3, false), - map(0x3A, MINECRAFT_1_19_4, false), - map(0x3C, MINECRAFT_1_20_2, false), - map(0x3E, MINECRAFT_1_20_5, false), - map(0x40, MINECRAFT_1_21_2, false), - map(0x3F, MINECRAFT_1_21_5, false), - map(0x44, MINECRAFT_1_21_9, false)); - clientbound.register( - ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new, - map(0x6B, MINECRAFT_1_20_5, false), - map(0x72, MINECRAFT_1_21_2, false), - map(0x71, MINECRAFT_1_21_5, false), - map(0x76, MINECRAFT_1_21_9, false)); - clientbound.register( - SystemChatPacket.class, - SystemChatPacket::new, - map(0x5F, MINECRAFT_1_19, true), - map(0x62, MINECRAFT_1_19_1, true), - map(0x60, MINECRAFT_1_19_3, true), - map(0x64, MINECRAFT_1_19_4, true), - map(0x67, MINECRAFT_1_20_2, true), - map(0x69, MINECRAFT_1_20_3, true), - map(0x6C, MINECRAFT_1_20_5, true), - map(0x73, MINECRAFT_1_21_2, true), - map(0x72, MINECRAFT_1_21_5, true), - map(0x77, MINECRAFT_1_21_9, true)); - clientbound.register( - PlayerChatCompletionPacket.class, - PlayerChatCompletionPacket::new, - map(0x15, MINECRAFT_1_19_1, true), - map(0x14, MINECRAFT_1_19_3, true), - map(0x16, MINECRAFT_1_19_4, true), - map(0x17, MINECRAFT_1_20_2, true), - map(0x18, MINECRAFT_1_20_5, true), - map(0x17, MINECRAFT_1_21_5, true)); - clientbound.register( - ServerDataPacket.class, - ServerDataPacket::new, - map(0x3F, MINECRAFT_1_19, false), - map(0x42, MINECRAFT_1_19_1, false), - map(0x41, MINECRAFT_1_19_3, false), - map(0x45, MINECRAFT_1_19_4, false), - map(0x47, MINECRAFT_1_20_2, false), - map(0x49, MINECRAFT_1_20_3, false), - map(0x4B, MINECRAFT_1_20_5, false), - map(0x50, MINECRAFT_1_21_2, false), - map(0x4F, MINECRAFT_1_21_5, false), - map(0x54, MINECRAFT_1_21_9, false)); - clientbound.register( - StartUpdatePacket.class, - () -> StartUpdatePacket.INSTANCE, - map(0x65, MINECRAFT_1_20_2, false), - map(0x67, MINECRAFT_1_20_3, false), - map(0x69, MINECRAFT_1_20_5, false), - map(0x70, MINECRAFT_1_21_2, false), - map(0x6F, MINECRAFT_1_21_5, false), - map(0x74, MINECRAFT_1_21_9, false)); - clientbound.register( - BundleDelimiterPacket.class, - () -> BundleDelimiterPacket.INSTANCE, - map(0x00, MINECRAFT_1_19_4, false)); - clientbound.register( - TransferPacket.class, - TransferPacket::new, - map(0x73, MINECRAFT_1_20_5, false), - map(0x7A, MINECRAFT_1_21_2, false), - map(0x7F, MINECRAFT_1_21_9, false)); - clientbound.register( - ClientboundCustomReportDetailsPacket.class, - ClientboundCustomReportDetailsPacket::new, - map(0x7A, MINECRAFT_1_21, false), - map(0x81, MINECRAFT_1_21_2, false), - map(0x86, MINECRAFT_1_21_9, false)); - clientbound.register( - ClientboundServerLinksPacket.class, - ClientboundServerLinksPacket::new, - map(0x7B, MINECRAFT_1_21, false), - map(0x82, MINECRAFT_1_21_2, false), - map(0x87, MINECRAFT_1_21_9, false)); - } - }, - LOGIN { - { - serverbound.register(ServerLoginPacket.class, - ServerLoginPacket::new, - map(0x00, MINECRAFT_1_7_2, false)); - serverbound.register( - EncryptionResponsePacket.class, EncryptionResponsePacket::new, - map(0x01, MINECRAFT_1_7_2, false)); - serverbound.register( - LoginPluginResponsePacket.class, LoginPluginResponsePacket::new, - map(0x02, MINECRAFT_1_13, false)); - serverbound.register( - LoginAcknowledgedPacket.class, LoginAcknowledgedPacket::new, - map(0x03, MINECRAFT_1_20_2, false)); - serverbound.register( - ServerboundCookieResponsePacket.class, ServerboundCookieResponsePacket::new, - map(0x04, MINECRAFT_1_20_5, false)); - - clientbound.register( - DisconnectPacket.class, () -> new DisconnectPacket(this), - map(0x00, MINECRAFT_1_7_2, false)); - clientbound.register( - EncryptionRequestPacket.class, EncryptionRequestPacket::new, - map(0x01, MINECRAFT_1_7_2, false)); - clientbound.register( - ServerLoginSuccessPacket.class, ServerLoginSuccessPacket::new, - map(0x02, MINECRAFT_1_7_2, false)); - clientbound.register( - SetCompressionPacket.class, SetCompressionPacket::new, - map(0x03, MINECRAFT_1_8, false)); - clientbound.register( - LoginPluginMessagePacket.class, - LoginPluginMessagePacket::new, - map(0x04, MINECRAFT_1_13, false)); - clientbound.register( - ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new, - map(0x05, MINECRAFT_1_20_5, false)); - } - }; + HANDSHAKE, + STATUS, + CONFIG, + PLAY, + LOGIN; public static final int STATUS_ID = 1; public static final int LOGIN_ID = 2; public static final int TRANSFER_ID = 3; - protected final PacketRegistry clientbound = new PacketRegistry(CLIENTBOUND, this); - protected final PacketRegistry serverbound = new PacketRegistry(SERVERBOUND, this); - - public StateRegistry.PacketRegistry.ProtocolRegistry getProtocolRegistry(Direction direction, - ProtocolVersion version) { - return (direction == SERVERBOUND ? serverbound : clientbound).getProtocolRegistry(version); - } /** * Gets the API representation of the StateRegistry. @@ -870,244 +49,4 @@ public ProtocolState toProtocolState() { case PLAY -> ProtocolState.PLAY; }; } - - /** - * Packet registry. - */ - public static class PacketRegistry { - - private final Direction direction; - private final StateRegistry registry; - private final Map versions; - private boolean fallback = true; - - PacketRegistry(Direction direction, StateRegistry registry) { - this.direction = direction; - this.registry = registry; - - Map mutableVersions = new EnumMap<>(ProtocolVersion.class); - for (ProtocolVersion version : ProtocolVersion.values()) { - if (!version.isLegacy() && !version.isUnknown()) { - mutableVersions.put(version, new ProtocolRegistry(version)); - } - } - - this.versions = Collections.unmodifiableMap(mutableVersions); - } - - ProtocolRegistry getProtocolRegistry(final ProtocolVersion version) { - ProtocolRegistry registry = versions.get(version); - if (registry == null) { - if (fallback) { - return getProtocolRegistry(MINIMUM_VERSION); - } - throw new IllegalArgumentException("Could not find data for protocol version " + version); - } - return registry; - } - -

void register(Class

clazz, Supplier

packetSupplier, - PacketMapping... mappings) { - if (mappings.length == 0) { - throw new IllegalArgumentException("At least one mapping must be provided."); - } - - for (int i = 0; i < mappings.length; i++) { - PacketMapping current = mappings[i]; - PacketMapping next = (i + 1 < mappings.length) ? mappings[i + 1] : current; - - ProtocolVersion from = current.protocolVersion; - ProtocolVersion lastValid = current.lastValidProtocolVersion; - if (lastValid != null) { - if (next != current) { - throw new IllegalArgumentException("Cannot add a mapping after last valid mapping"); - } - if (from.greaterThan(lastValid)) { - throw new IllegalArgumentException( - "Last mapping version cannot be higher than highest mapping version"); - } - } - ProtocolVersion to = current == next ? lastValid != null - ? lastValid : getLast(SUPPORTED_VERSIONS) : next.protocolVersion; - - ProtocolVersion lastInList = lastValid != null ? lastValid : getLast(SUPPORTED_VERSIONS); - - if (from.noLessThan(to) && from != lastInList) { - throw new IllegalArgumentException(String.format( - "Next mapping version (%s) should be lower then current (%s)", to, from)); - } - - for (ProtocolVersion protocol : EnumSet.range(from, to)) { - if (protocol == to && next != current) { - break; - } - ProtocolRegistry registry = this.versions.get(protocol); - if (registry == null) { - throw new IllegalArgumentException( - "Unknown protocol version " + current.protocolVersion); - } - - if (registry.packetIdToSupplier.containsKey(current.id)) { - throw new IllegalArgumentException( - "Can not register class " - + clazz.getSimpleName() - + " with id " - + current.id - + " for " - + registry.version - + " because another packet is already registered"); - } - - if (registry.packetClassToId.containsKey(clazz)) { - throw new IllegalArgumentException( - clazz.getSimpleName() + " is already registered for version " + registry.version); - } - - if (!current.encodeOnly) { - registry.packetIdToSupplier.put(current.id, packetSupplier); - } - registry.packetClassToId.put(clazz, current.id); - } - } - } - - /** - * Protocol registry. - */ - public class ProtocolRegistry { - - public final ProtocolVersion version; - final IntObjectMap> packetIdToSupplier = - new IntObjectHashMap<>(16, 0.5f); - final Object2IntMap> packetClassToId = - new Object2IntOpenHashMap<>(16, 0.5f); - - ProtocolRegistry(final ProtocolVersion version) { - this.version = version; - this.packetClassToId.defaultReturnValue(Integer.MIN_VALUE); - } - - /** - * Attempts to create a packet from the specified {@code id}. - * - * @param id the packet ID - * @return the packet instance, or {@code null} if the ID is not registered - */ - public @Nullable MinecraftPacket createPacket(final int id) { - final Supplier supplier = this.packetIdToSupplier.get(id); - if (supplier == null) { - return null; - } - return supplier.get(); - } - - /** - * Attempts to look up the packet ID for an {@code packet}. - * - * @param packet the packet to look up - * @return the packet ID - * @throws IllegalArgumentException if the packet ID is not found - */ - public int getPacketId(final MinecraftPacket packet) { - final int id = this.packetClassToId.getInt(packet.getClass()); - if (id == Integer.MIN_VALUE) { - throw new IllegalArgumentException(String.format( - "Unable to find id for packet of type %s in %s protocol %s phase %s", - packet.getClass().getName(), PacketRegistry.this.direction, - this.version, PacketRegistry.this.registry - )); - } - return id; - } - - /** - * Checks if the registry contains a packet with the specified {@code id}. - * - * @param packet the packet to check - * @return {@code true} if the packet is registered, {@code false} otherwise - */ - public boolean containsPacket(final MinecraftPacket packet) { - return this.packetClassToId.containsKey(packet.getClass()); - } - } - } - - /** - * Packet mapping. - */ - public static final class PacketMapping { - - private final int id; - private final ProtocolVersion protocolVersion; - private final boolean encodeOnly; - private final @Nullable ProtocolVersion lastValidProtocolVersion; - - PacketMapping(int id, ProtocolVersion protocolVersion, - @Nullable ProtocolVersion lastValidProtocolVersion, - boolean packetDecoding) { - this.id = id; - this.protocolVersion = protocolVersion; - this.lastValidProtocolVersion = lastValidProtocolVersion; - this.encodeOnly = packetDecoding; - } - - @Override - public String toString() { - return "PacketMapping{" - + "id=" - + id - + ", protocolVersion=" - + protocolVersion - + ", encodeOnly=" - + encodeOnly - + '}'; - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - PacketMapping that = (PacketMapping) o; - return id == that.id - && protocolVersion == that.protocolVersion - && encodeOnly == that.encodeOnly; - } - - @Override - public int hashCode() { - return Objects.hash(id, protocolVersion, encodeOnly); - } - } - - /** - * Creates a PacketMapping using the provided arguments. - * - * @param id Packet Id - * @param version Protocol version - * @param encodeOnly When true packet decoding will be disabled - * @return PacketMapping with the provided arguments - */ - @SuppressFBWarnings({"UPM_UNCALLED_PRIVATE_METHOD"}) - private static PacketMapping map(int id, ProtocolVersion version, boolean encodeOnly) { - return map(id, version, null, encodeOnly); - } - - /** - * Creates a PacketMapping using the provided arguments. - * - * @param id Packet Id - * @param version Protocol version - * @param encodeOnly When true packet decoding will be disabled - * @param lastValidProtocolVersion Last version this Mapping is valid at - * @return PacketMapping with the provided arguments - */ - private static PacketMapping map(int id, ProtocolVersion version, - ProtocolVersion lastValidProtocolVersion, boolean encodeOnly) { - return new PacketMapping(id, version, lastValidProtocolVersion, encodeOnly); - } - } \ No newline at end of file diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/LegacyPingDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/LegacyPingDecoder.java index a6c2c065f5..ae99ed4cf9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/LegacyPingDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/LegacyPingDecoder.java @@ -66,7 +66,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) t out.add(readExtended16Data(in)); } else if (first == 0x02 && in.isReadable()) { in.skipBytes(in.readableBytes()); - out.add(new LegacyHandshakePacket()); + out.add(LegacyHandshakePacket.INSTANCE); } else { in.readerIndex(originalReaderIndex); ctx.pipeline().remove(this); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java index 35ddccd5e1..10b22b65af 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java @@ -20,8 +20,10 @@ import com.google.common.base.Preconditions; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolStates; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.protocol.registry.PacketRegistry; import com.velocitypowered.proxy.util.except.QuietRuntimeException; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; @@ -39,8 +41,9 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter { + "information, launch Velocity with -Dvelocity.packet-decode-logging=true to see more."); private final ProtocolUtils.Direction direction; + private ProtocolVersion version; private StateRegistry state; - private StateRegistry.PacketRegistry.ProtocolRegistry registry; + private PacketRegistry registry; /** * Creates a new {@code MinecraftDecoder} decoding packets from the specified {@code direction}. @@ -49,8 +52,8 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter { */ public MinecraftDecoder(ProtocolUtils.Direction direction) { this.direction = Preconditions.checkNotNull(direction, "direction"); - this.registry = StateRegistry.HANDSHAKE.getProtocolRegistry( - direction, ProtocolVersion.MINIMUM_VERSION); + this.version = ProtocolVersion.MINIMUM_VERSION; + this.registry = ProtocolStates.handshake(direction).forVersion(ProtocolVersion.MINIMUM_VERSION); this.state = StateRegistry.HANDSHAKE; } @@ -71,18 +74,20 @@ private void tryDecode(ChannelHandlerContext ctx, ByteBuf buf) throws Exception int originalReaderIndex = buf.readerIndex(); int packetId = ProtocolUtils.readVarInt(buf); - MinecraftPacket packet = this.registry.createPacket(packetId); - if (packet == null) { + com.velocitypowered.proxy.protocol.PacketCodec codec = + this.registry.getCodec(packetId); + if (codec == null) { buf.readerIndex(originalReaderIndex); ctx.fireChannelRead(buf); } else { try { - doLengthSanityChecks(buf, packet); + doLengthSanityChecks(buf, codec); + MinecraftPacket packet; try { - packet.decode(buf, direction, registry.version); + packet = codec.decode(buf, direction, this.version); } catch (Exception e) { - throw handleDecodeFailure(e, packet, packetId); + throw handleDecodeFailure(e, codec, packetId); } if (buf.isReadable()) { @@ -95,14 +100,16 @@ private void tryDecode(ChannelHandlerContext ctx, ByteBuf buf) throws Exception } } - private void doLengthSanityChecks(ByteBuf buf, MinecraftPacket packet) throws Exception { - int expectedMinLen = packet.decodeExpectedMinLength(buf, direction, registry.version); - int expectedMaxLen = packet.decodeExpectedMaxLength(buf, direction, registry.version); + private void doLengthSanityChecks(ByteBuf buf, + com.velocitypowered.proxy.protocol.PacketCodec codec) + throws Exception { + int expectedMinLen = codec.decodeExpectedMinLength(buf, direction, this.version); + int expectedMaxLen = codec.decodeExpectedMaxLength(buf, direction, this.version); if (expectedMaxLen != -1 && buf.readableBytes() > expectedMaxLen) { - throw handleOverflow(packet, expectedMaxLen, buf.readableBytes()); + throw handleOverflow(codec, expectedMaxLen, buf.readableBytes()); } if (buf.readableBytes() < expectedMinLen) { - throw handleUnderflow(packet, expectedMaxLen, buf.readableBytes()); + throw handleUnderflow(codec, expectedMaxLen, buf.readableBytes()); } } @@ -115,36 +122,52 @@ private Exception handleOverflow(MinecraftPacket packet, int expected, int actua } } - private Exception handleUnderflow(MinecraftPacket packet, int expected, int actual) { + private Exception handleOverflow( + com.velocitypowered.proxy.protocol.PacketCodec codec, + int expected, int actual) { if (DEBUG) { - return new CorruptedFrameException("Packet sent for " + packet.getClass() + " was too " + return new CorruptedFrameException("Packet sent for " + codec.getClass() + " was too " + + "big (expected " + expected + " bytes, got " + actual + " bytes)"); + } else { + return DECODE_FAILED; + } + } + + private Exception handleUnderflow( + com.velocitypowered.proxy.protocol.PacketCodec codec, + int expected, int actual) { + if (DEBUG) { + return new CorruptedFrameException("Packet sent for " + codec.getClass() + " was too " + "small (expected " + expected + " bytes, got " + actual + " bytes)"); } else { return DECODE_FAILED; } } - private Exception handleDecodeFailure(Exception cause, MinecraftPacket packet, int packetId) { + private Exception handleDecodeFailure(Exception cause, + com.velocitypowered.proxy.protocol.PacketCodec codec, + int packetId) { if (DEBUG) { return new CorruptedFrameException( - "Error decoding " + packet.getClass() + " " + getExtraConnectionDetail(packetId), cause); + "Error decoding " + codec.getClass() + " " + getExtraConnectionDetail(packetId), cause); } else { return DECODE_FAILED; } } private String getExtraConnectionDetail(int packetId) { - return "Direction " + direction + " Protocol " + registry.version + " State " + state + return "Direction " + direction + " Protocol " + this.version + " State " + state + " ID 0x" + Integer.toHexString(packetId); } public void setProtocolVersion(ProtocolVersion protocolVersion) { - this.registry = state.getProtocolRegistry(direction, protocolVersion); + this.version = protocolVersion; + this.registry = ProtocolStates.lookup(this.state, this.direction).forVersion(protocolVersion); } public void setState(StateRegistry state) { this.state = state; - this.setProtocolVersion(registry.version); + this.registry = ProtocolStates.lookup(this.state, this.direction).forVersion(this.version); } public ProtocolUtils.Direction getDirection() { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java index 4af366cec0..66863c8acb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java @@ -20,8 +20,11 @@ import com.google.common.base.Preconditions; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; +import com.velocitypowered.proxy.protocol.ProtocolStates; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.protocol.registry.PacketRegistry; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; @@ -32,8 +35,9 @@ public class MinecraftEncoder extends MessageToByteEncoder { private final ProtocolUtils.Direction direction; + private ProtocolVersion version; private StateRegistry state; - private StateRegistry.PacketRegistry.ProtocolRegistry registry; + private PacketRegistry registry; /** * Creates a new {@code MinecraftEncoder} encoding packets for the specified {@code direction}. @@ -42,22 +46,37 @@ public class MinecraftEncoder extends MessageToByteEncoder { */ public MinecraftEncoder(ProtocolUtils.Direction direction) { this.direction = Preconditions.checkNotNull(direction, "direction"); - this.registry = StateRegistry.HANDSHAKE.getProtocolRegistry( - direction, ProtocolVersion.MINIMUM_VERSION); + this.version = ProtocolVersion.MINIMUM_VERSION; + this.registry = ProtocolStates.handshake(direction).forVersion(ProtocolVersion.MINIMUM_VERSION); this.state = StateRegistry.HANDSHAKE; } @Override + @SuppressWarnings("unchecked") protected void encode(ChannelHandlerContext ctx, MinecraftPacket msg, ByteBuf out) { + PacketCodec codec = (PacketCodec) this.registry.getCodec(msg.getClass()); + if (codec == null) { + throw new IllegalArgumentException("No codec found for packet: " + msg.getClass()); + } + int packetId = this.registry.getPacketId(msg); ProtocolUtils.writeVarInt(out, packetId); - msg.encode(out, direction, registry.version); + codec.encode(msg, out, direction, version); } @Override + @SuppressWarnings("unchecked") protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, MinecraftPacket msg, boolean preferDirect) throws Exception { - int hint = msg.encodeSizeHint(direction, registry.version); + PacketCodec codec = + (PacketCodec) + this.registry.getCodec(msg.getClass()); + + int hint = -1; + if (codec != null) { + hint = codec.encodeSizeHint(msg, direction, version); + } + if (hint < 0) { return super.allocateBuffer(ctx, msg, preferDirect); } @@ -68,12 +87,13 @@ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, MinecraftPacket msg, } public void setProtocolVersion(final ProtocolVersion protocolVersion) { - this.registry = state.getProtocolRegistry(direction, protocolVersion); + this.version = protocolVersion; + this.registry = ProtocolStates.lookup(this.state, this.direction).forVersion(protocolVersion); } public void setState(StateRegistry state) { this.state = state; - this.setProtocolVersion(registry.version); + this.registry = ProtocolStates.lookup(state, this.direction).forVersion(version); } public ProtocolUtils.Direction getDirection() { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java index 7d4d8d6d9f..0cedb7aacb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java @@ -20,9 +20,11 @@ import static io.netty.util.ByteProcessor.FIND_NON_NUL; import com.velocitypowered.api.network.ProtocolVersion; -import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; +import com.velocitypowered.proxy.protocol.ProtocolStates; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.protocol.registry.PacketRegistry; import com.velocitypowered.proxy.util.except.QuietDecoderException; import com.velocitypowered.proxy.util.except.QuietRuntimeException; import io.netty.buffer.ByteBuf; @@ -50,7 +52,7 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { new QuietDecoderException("Unknown packet"); private final ProtocolUtils.Direction direction; - private final StateRegistry.PacketRegistry.ProtocolRegistry registry; + private final PacketRegistry registry; private StateRegistry state; /** @@ -60,8 +62,7 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { */ public MinecraftVarintFrameDecoder(ProtocolUtils.Direction direction) { this.direction = direction; - this.registry = StateRegistry.HANDSHAKE.getProtocolRegistry( - direction, ProtocolVersion.MINIMUM_VERSION); + this.registry = ProtocolStates.handshake(direction).forVersion(ProtocolVersion.MINIMUM_VERSION); this.state = StateRegistry.HANDSHAKE; } @@ -110,9 +111,6 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) } private boolean validateServerboundHandshakePacket(ByteBuf in, int length) throws Exception { - StateRegistry.PacketRegistry.ProtocolRegistry registry = - state.getProtocolRegistry(direction, ProtocolVersion.MINIMUM_VERSION); - final int index = in.readerIndex(); final int packetId = readRawVarInt21(in); // Index hasn't changed, we've read nothing @@ -122,22 +120,21 @@ private boolean validateServerboundHandshakePacket(ByteBuf in, int length) throw } final int payloadLength = length - ProtocolUtils.varIntBytes(packetId); - MinecraftPacket packet = registry.createPacket(packetId); - + PacketCodec codec = registry.getCodec(packetId); // We handle every packet in this phase, if you said something we don't know, something is really wrong - if (packet == null) { + if (codec == null) { throw UNKNOWN_PACKET; } // We 'technically' have the incoming bytes of a payload here, and so, these can actually parse // the packet if needed, so, we'll take advantage of the existing methods - int expectedMinLen = packet.decodeExpectedMinLength(in, direction, registry.version); - int expectedMaxLen = packet.decodeExpectedMaxLength(in, direction, registry.version); + int expectedMinLen = codec.decodeExpectedMinLength(in, direction, ProtocolVersion.MINIMUM_VERSION); + int expectedMaxLen = codec.decodeExpectedMaxLength(in, direction, ProtocolVersion.MINIMUM_VERSION); if (expectedMaxLen != -1 && payloadLength > expectedMaxLen) { - throw handleOverflow(packet, expectedMaxLen, in.readableBytes()); + throw handleOverflow(expectedMaxLen, in.readableBytes()); } if (payloadLength < expectedMinLen) { - throw handleUnderflow(packet, expectedMaxLen, in.readableBytes()); + throw handleUnderflow(expectedMaxLen, in.readableBytes()); } in.readerIndex(index); @@ -224,18 +221,18 @@ private static int readRawVarintSmallBuf(ByteBuf buffer) { return result | (tmp & 0x7F) << 14; } - private Exception handleOverflow(MinecraftPacket packet, int expected, int actual) { + private Exception handleOverflow(int expected, int actual) { if (MinecraftDecoder.DEBUG) { - return new CorruptedFrameException("Packet sent for " + packet.getClass() + " was too " + return new CorruptedFrameException("Packet sent for handshake was too " + "big (expected " + expected + " bytes, got " + actual + " bytes)"); } else { return FRAME_DECODER_FAILED; } } - private Exception handleUnderflow(MinecraftPacket packet, int expected, int actual) { + private Exception handleUnderflow(int expected, int actual) { if (MinecraftDecoder.DEBUG) { - return new CorruptedFrameException("Packet sent for " + packet.getClass() + " was too " + return new CorruptedFrameException("Packet sent for handshake was too " + "small (expected " + expected + " bytes, got " + actual + " bytes)"); } else { return FRAME_DECODER_FAILED; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueInboundHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueInboundHandler.java index 1affc13bcd..5d76d6ebad 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueInboundHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueInboundHandler.java @@ -19,8 +19,9 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolStates; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.protocol.registry.PacketRegistry; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.util.ReferenceCountUtil; @@ -41,7 +42,7 @@ */ public class PlayPacketQueueInboundHandler extends ChannelDuplexHandler { - private final StateRegistry.PacketRegistry.ProtocolRegistry registry; + private final PacketRegistry registry; private final Queue queue = new ArrayDeque<>(); /** @@ -50,7 +51,7 @@ public class PlayPacketQueueInboundHandler extends ChannelDuplexHandler { * @param version the protocol version */ public PlayPacketQueueInboundHandler(ProtocolVersion version, ProtocolUtils.Direction direction) { - this.registry = StateRegistry.CONFIG.getProtocolRegistry(direction, version); + this.registry = ProtocolStates.configuration(direction).forVersion(version); } @Override @@ -58,7 +59,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception if (msg instanceof final MinecraftPacket packet) { // If the packet exists in the CONFIG state, we want to always // ensure that it gets handled by the current handler - if (this.registry.containsPacket(packet)) { + if (this.registry.canDecodePacket(packet)) { ctx.fireChannelRead(msg); return; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueOutboundHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueOutboundHandler.java index c572710401..14f23c7a84 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueOutboundHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueOutboundHandler.java @@ -19,8 +19,9 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolStates; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.protocol.registry.PacketRegistry; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; @@ -42,7 +43,7 @@ */ public class PlayPacketQueueOutboundHandler extends ChannelDuplexHandler { - private final StateRegistry.PacketRegistry.ProtocolRegistry registry; + private final PacketRegistry registry; private final Queue queue = new ArrayDeque<>(); /** @@ -51,7 +52,7 @@ public class PlayPacketQueueOutboundHandler extends ChannelDuplexHandler { * @param version the protocol version */ public PlayPacketQueueOutboundHandler(ProtocolVersion version, ProtocolUtils.Direction direction) { - this.registry = StateRegistry.CONFIG.getProtocolRegistry(direction, version); + this.registry = ProtocolStates.configuration(direction).forVersion(version); } @Override @@ -63,7 +64,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) // If the packet exists in the CONFIG state, we want to always // ensure that it gets sent out to the client - if (this.registry.containsPacket(packet)) { + if (this.registry.canDecodePacket(packet)) { ctx.write(msg, promise); return; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java index d3fc3a8298..d03a95f1d5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java @@ -37,6 +37,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentPropertyRegistry; @@ -54,7 +55,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; -public class AvailableCommandsPacket implements MinecraftPacket { +public final class AvailableCommandsPacket implements MinecraftPacket { private static final Command PLACEHOLDER_COMMAND = source -> 0; private static final Predicate PLACEHOLDER_REQUIREMENT = source -> true; @@ -69,160 +70,171 @@ public class AvailableCommandsPacket implements MinecraftPacket { private static final byte FLAG_HAS_SUGGESTIONS = 0x10; private static final byte FLAG_IS_RESTRICTED = 0x20; - private @MonotonicNonNull RootCommandNode rootNode; + private final RootCommandNode rootNode; + + public AvailableCommandsPacket(RootCommandNode rootNode) { + this.rootNode = rootNode; + } - /** - * Returns the root node. - * - * @return the root node - */ public RootCommandNode getRootNode() { - if (rootNode == null) { - throw new IllegalStateException("Packet not yet deserialized"); - } return rootNode; } @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - int commands = ProtocolUtils.readVarInt(buf); - WireNode[] wireNodes = new WireNode[commands]; - for (int i = 0; i < commands; i++) { - wireNodes[i] = deserializeNode(buf, i, protocolVersion); - } + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } - // Iterate over the deserialized nodes and attempt to form a graph. We also resolve any cycles - // that exist. - Queue nodeQueue = new ArrayDeque<>(Arrays.asList(wireNodes)); - while (!nodeQueue.isEmpty()) { - boolean cycling = false; - - for (Iterator it = nodeQueue.iterator(); it.hasNext(); ) { - WireNode node = it.next(); - if (node.toNode(wireNodes)) { - cycling = true; - it.remove(); - } - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - if (!cycling) { - // Uh-oh. We can't cycle. This is bad. - throw new IllegalStateException("Stopped cycling; the root node can't be built."); + @Override + public AvailableCommandsPacket decode(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + int commands = ProtocolUtils.readVarInt(buf); + WireNode[] wireNodes = new WireNode[commands]; + for (int i = 0; i < commands; i++) { + wireNodes[i] = deserializeNode(buf, i, protocolVersion); } - } - int rootIdx = ProtocolUtils.readVarInt(buf); - rootNode = (RootCommandNode) wireNodes[rootIdx].built; - } + // Iterate over the deserialized nodes and attempt to form a graph. We also resolve any cycles + // that exist. + Queue nodeQueue = new ArrayDeque<>(Arrays.asList(wireNodes)); + while (!nodeQueue.isEmpty()) { + boolean cycling = false; + + for (Iterator it = nodeQueue.iterator(); it.hasNext(); ) { + WireNode node = it.next(); + if (node.toNode(wireNodes)) { + cycling = true; + it.remove(); + } + } - @Override - public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - // Assign all the children an index. - Deque> childrenQueue = new ArrayDeque<>(ImmutableList.of(rootNode)); - Object2IntMap> idMappings = new Object2IntLinkedOpenCustomHashMap<>( - IdentityHashStrategy.instance()); - while (!childrenQueue.isEmpty()) { - CommandNode child = childrenQueue.poll(); - if (!idMappings.containsKey(child)) { - idMappings.put(child, idMappings.size()); - childrenQueue.addAll(child.getChildren()); - if (child.getRedirect() != null) { - childrenQueue.add(child.getRedirect()); + if (!cycling) { + // Uh-oh. We can't cycle. This is bad. + throw new IllegalStateException("Stopped cycling; the root node can't be built."); } } - } - // Now serialize the children. - ProtocolUtils.writeVarInt(buf, idMappings.size()); - for (CommandNode child : idMappings.keySet()) { - serializeNode(child, buf, idMappings, protocolVersion); + int rootIdx = ProtocolUtils.readVarInt(buf); + RootCommandNode rootNode = (RootCommandNode) wireNodes[rootIdx].built; + return new AvailableCommandsPacket(rootNode); } - ProtocolUtils.writeVarInt(buf, idMappings.getInt(rootNode)); - } - private static void serializeNode(CommandNode node, ByteBuf buf, - Object2IntMap> idMappings, ProtocolVersion protocolVersion) { - byte flags = 0; - if (node.getRedirect() != null) { - flags |= FLAG_IS_REDIRECT; - } - if (node.getCommand() != null) { - flags |= FLAG_EXECUTABLE; - } - if (node.getRequirement() == PLACEHOLDER_REQUIREMENT) { - flags |= FLAG_IS_RESTRICTED; - } + @Override + public void encode(AvailableCommandsPacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + // Assign all the children an index. + Deque> childrenQueue = new ArrayDeque<>(ImmutableList.of(packet.rootNode)); + Object2IntMap> idMappings = new Object2IntLinkedOpenCustomHashMap<>( + IdentityHashStrategy.instance()); + while (!childrenQueue.isEmpty()) { + CommandNode child = childrenQueue.poll(); + if (!idMappings.containsKey(child)) { + idMappings.put(child, idMappings.size()); + childrenQueue.addAll(child.getChildren()); + if (child.getRedirect() != null) { + childrenQueue.add(child.getRedirect()); + } + } + } - if (node instanceof LiteralCommandNode) { - flags |= NODE_TYPE_LITERAL; - } else if (node instanceof ArgumentCommandNode) { - flags |= NODE_TYPE_ARGUMENT; - if (((ArgumentCommandNode) node).getCustomSuggestions() != null) { - flags |= FLAG_HAS_SUGGESTIONS; + // Now serialize the children. + ProtocolUtils.writeVarInt(buf, idMappings.size()); + for (CommandNode child : idMappings.keySet()) { + serializeNode(child, buf, idMappings, protocolVersion); } - } else if (!(node instanceof RootCommandNode)) { - throw new IllegalArgumentException("Unknown node type " + node.getClass().getName()); + ProtocolUtils.writeVarInt(buf, idMappings.getInt(packet.rootNode)); } - buf.writeByte(flags); - ProtocolUtils.writeVarInt(buf, node.getChildren().size()); - for (CommandNode child : node.getChildren()) { - ProtocolUtils.writeVarInt(buf, idMappings.getInt(child)); - } - if (node.getRedirect() != null) { - ProtocolUtils.writeVarInt(buf, idMappings.getInt(node.getRedirect())); - } + private static void serializeNode(CommandNode node, ByteBuf buf, + Object2IntMap> idMappings, ProtocolVersion protocolVersion) { + byte flags = 0; + if (node.getRedirect() != null) { + flags |= FLAG_IS_REDIRECT; + } + if (node.getCommand() != null) { + flags |= FLAG_EXECUTABLE; + } + if (node.getRequirement() == PLACEHOLDER_REQUIREMENT) { + flags |= FLAG_IS_RESTRICTED; + } - if (node instanceof ArgumentCommandNode) { - ProtocolUtils.writeString(buf, node.getName()); - ArgumentPropertyRegistry.serialize(buf, - ((ArgumentCommandNode) node).getType(), protocolVersion); - - if (((ArgumentCommandNode) node).getCustomSuggestions() != null) { - SuggestionProvider provider = ((ArgumentCommandNode) node) - .getCustomSuggestions(); - String name = "minecraft:ask_server"; - if (provider instanceof ProtocolSuggestionProvider) { - name = ((ProtocolSuggestionProvider) provider).name; + if (node instanceof LiteralCommandNode) { + flags |= NODE_TYPE_LITERAL; + } else if (node instanceof ArgumentCommandNode) { + flags |= NODE_TYPE_ARGUMENT; + if (((ArgumentCommandNode) node).getCustomSuggestions() != null) { + flags |= FLAG_HAS_SUGGESTIONS; } - ProtocolUtils.writeString(buf, name); + } else if (!(node instanceof RootCommandNode)) { + throw new IllegalArgumentException("Unknown node type " + node.getClass().getName()); + } + + buf.writeByte(flags); + ProtocolUtils.writeVarInt(buf, node.getChildren().size()); + for (CommandNode child : node.getChildren()) { + ProtocolUtils.writeVarInt(buf, idMappings.getInt(child)); + } + if (node.getRedirect() != null) { + ProtocolUtils.writeVarInt(buf, idMappings.getInt(node.getRedirect())); + } + + if (node instanceof ArgumentCommandNode) { + ProtocolUtils.writeString(buf, node.getName()); + ArgumentPropertyRegistry.serialize(buf, + ((ArgumentCommandNode) node).getType(), protocolVersion); + + if (((ArgumentCommandNode) node).getCustomSuggestions() != null) { + SuggestionProvider provider = ((ArgumentCommandNode) node) + .getCustomSuggestions(); + String name = "minecraft:ask_server"; + if (provider instanceof ProtocolSuggestionProvider) { + name = ((ProtocolSuggestionProvider) provider).name; + } + ProtocolUtils.writeString(buf, name); + } + } else if (node instanceof LiteralCommandNode) { + ProtocolUtils.writeString(buf, node.getName()); } - } else if (node instanceof LiteralCommandNode) { - ProtocolUtils.writeString(buf, node.getName()); } - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } + private static WireNode deserializeNode(ByteBuf buf, int idx, ProtocolVersion version) { + byte flags = buf.readByte(); + int[] children = ProtocolUtils.readIntegerArray(buf); + int redirectTo = -1; + if ((flags & FLAG_IS_REDIRECT) > 0) { + redirectTo = ProtocolUtils.readVarInt(buf); + } - private static WireNode deserializeNode(ByteBuf buf, int idx, ProtocolVersion version) { - byte flags = buf.readByte(); - int[] children = ProtocolUtils.readIntegerArray(buf); - int redirectTo = -1; - if ((flags & FLAG_IS_REDIRECT) > 0) { - redirectTo = ProtocolUtils.readVarInt(buf); + switch (flags & FLAG_NODE_TYPE) { + case NODE_TYPE_ROOT: + return new WireNode(idx, flags, children, redirectTo, null); + case NODE_TYPE_LITERAL: + return new WireNode(idx, flags, children, redirectTo, LiteralArgumentBuilder + .literal(ProtocolUtils.readString(buf))); + case NODE_TYPE_ARGUMENT: + String name = ProtocolUtils.readString(buf); + ArgumentType argumentType = ArgumentPropertyRegistry.deserialize(buf, version); + + RequiredArgumentBuilder argumentBuilder = RequiredArgumentBuilder + .argument(name, argumentType); + if ((flags & FLAG_HAS_SUGGESTIONS) != 0) { + argumentBuilder.suggests(new ProtocolSuggestionProvider(ProtocolUtils.readString(buf))); + } + return new WireNode(idx, flags, children, redirectTo, argumentBuilder); + default: + throw new IllegalArgumentException("Unknown node type " + (flags & FLAG_NODE_TYPE)); + } } - switch (flags & FLAG_NODE_TYPE) { - case NODE_TYPE_ROOT: - return new WireNode(idx, flags, children, redirectTo, null); - case NODE_TYPE_LITERAL: - return new WireNode(idx, flags, children, redirectTo, LiteralArgumentBuilder - .literal(ProtocolUtils.readString(buf))); - case NODE_TYPE_ARGUMENT: - String name = ProtocolUtils.readString(buf); - ArgumentType argumentType = ArgumentPropertyRegistry.deserialize(buf, version); - - RequiredArgumentBuilder argumentBuilder = RequiredArgumentBuilder - .argument(name, argumentType); - if ((flags & FLAG_HAS_SUGGESTIONS) != 0) { - argumentBuilder.suggests(new ProtocolSuggestionProvider(ProtocolUtils.readString(buf))); - } - return new WireNode(idx, flags, children, redirectTo, argumentBuilder); - default: - throw new IllegalArgumentException("Unknown node type " + (flags & FLAG_NODE_TYPE)); + @Override + public int encodeSizeHint(AvailableCommandsPacket packet, Direction direction, ProtocolVersion version) { + // This is a very complex packet to encode. Paper 1.21.10 + Velocity with Spark has a size of + // 30,334, but this is likely on the lower side. We'll use 128KiB as a more realistically-sized + // amount. + return 128 * 1024; } } @@ -362,12 +374,4 @@ public CompletableFuture getSuggestions(CommandContext COLORS_TO_PROTOCOL = new Enum2IntMap.Builder<>(BossBar.Color.class) @@ -62,43 +64,23 @@ public class BossBarPacket implements MinecraftPacket { public static final int UPDATE_NAME = 3; public static final int UPDATE_STYLE = 4; public static final int UPDATE_PROPERTIES = 5; - private @Nullable UUID uuid; - private int action; - private @Nullable ComponentHolder name; - private float percent; - private int color; - private int overlay; - private short flags; public static BossBarPacket createAddPacket( final UUID id, final BossBar bar, final ComponentHolder name ) { - final BossBarPacket packet = new BossBarPacket(); - packet.setUuid(id); - packet.setAction(BossBarPacket.ADD); - packet.setName(name); - packet.setColor(COLORS_TO_PROTOCOL.get(bar.color())); - packet.setOverlay(OVERLAY_TO_PROTOCOL.get(bar.overlay())); - packet.setPercent(bar.progress()); - packet.setFlags(serializeFlags(bar.flags())); - return packet; + return new BossBarPacket(id, ADD, name, bar.progress(), + COLORS_TO_PROTOCOL.get(bar.color()), OVERLAY_TO_PROTOCOL.get(bar.overlay()), + serializeFlags(bar.flags())); } - public static BossBarPacket createRemovePacket(final UUID id, final BossBar bar) { - final BossBarPacket packet = new BossBarPacket(); - packet.setUuid(id); - packet.setAction(REMOVE); - return packet; + public static BossBarPacket createRemovePacket(final UUID id) { + return new BossBarPacket(id, REMOVE, null, 0, 0, 0, (short) 0); } public static BossBarPacket createUpdateProgressPacket(final UUID id, final BossBar bar) { - final BossBarPacket packet = new BossBarPacket(); - packet.setUuid(id); - packet.setAction(UPDATE_PERCENT); - packet.setPercent(bar.progress()); - return packet; + return new BossBarPacket(id, UPDATE_PERCENT, null, bar.progress(), 0, 0, (short) 0); } public static BossBarPacket createUpdateNamePacket( @@ -106,177 +88,20 @@ public static BossBarPacket createUpdateNamePacket( final BossBar bar, final ComponentHolder name ) { - final BossBarPacket packet = new BossBarPacket(); - packet.setUuid(id); - packet.setAction(UPDATE_NAME); - packet.setName(name); - return packet; + return new BossBarPacket(id, UPDATE_NAME, name, 0, 0, 0, (short) 0); } public static BossBarPacket createUpdateStylePacket(final UUID id, final BossBar bar) { - final BossBarPacket packet = new BossBarPacket(); - packet.setUuid(id); - packet.setAction(UPDATE_STYLE); - packet.setColor(COLORS_TO_PROTOCOL.get(bar.color())); - packet.setOverlay(OVERLAY_TO_PROTOCOL.get(bar.overlay())); - return packet; + return new BossBarPacket(id, UPDATE_STYLE, null, 0, + COLORS_TO_PROTOCOL.get(bar.color()), OVERLAY_TO_PROTOCOL.get(bar.overlay()), (short) 0); } public static BossBarPacket createUpdatePropertiesPacket(final UUID id, final BossBar bar) { - final BossBarPacket packet = new BossBarPacket(); - packet.setUuid(id); - packet.setAction(UPDATE_PROPERTIES); - packet.setFlags(serializeFlags(bar.flags())); - return packet; + return new BossBarPacket(id, UPDATE_PROPERTIES, null, 0, 0, 0, serializeFlags(bar.flags())); } - public UUID getUuid() { - if (uuid == null) { - throw new IllegalStateException("No boss bar UUID specified"); - } - return uuid; - } - - public void setUuid(UUID uuid) { - this.uuid = uuid; - } - - public int getAction() { - return action; - } - - public void setAction(int action) { - this.action = action; - } - - public @Nullable ComponentHolder getName() { - return name; - } - - public void setName(ComponentHolder name) { - this.name = name; - } - - public float getPercent() { - return percent; - } - - public void setPercent(float percent) { - this.percent = percent; - } - - public int getColor() { - return color; - } - - public void setColor(int color) { - this.color = color; - } - - public int getOverlay() { - return overlay; - } - - public void setOverlay(int overlay) { - this.overlay = overlay; - } - - public short getFlags() { - return flags; - } - - public void setFlags(short flags) { - this.flags = flags; - } - - @Override - public String toString() { - return "BossBar{" - + "uuid=" + uuid - + ", action=" + action - + ", name='" + name + '\'' - + ", percent=" + percent - + ", color=" + color - + ", overlay=" + overlay - + ", flags=" + flags - + '}'; - } - - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - this.uuid = ProtocolUtils.readUuid(buf); - this.action = ProtocolUtils.readVarInt(buf); - switch (action) { - case ADD: - this.name = ComponentHolder.read(buf, version); - this.percent = buf.readFloat(); - this.color = ProtocolUtils.readVarInt(buf); - this.overlay = ProtocolUtils.readVarInt(buf); - this.flags = buf.readUnsignedByte(); - break; - case REMOVE: - break; - case UPDATE_PERCENT: - this.percent = buf.readFloat(); - break; - case UPDATE_NAME: - this.name = ComponentHolder.read(buf, version); - break; - case UPDATE_STYLE: - this.color = ProtocolUtils.readVarInt(buf); - this.overlay = ProtocolUtils.readVarInt(buf); - break; - case UPDATE_PROPERTIES: - this.flags = buf.readUnsignedByte(); - break; - default: - throw new UnsupportedOperationException("Unknown action " + action); - } - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (uuid == null) { - throw new IllegalStateException("No boss bar UUID specified"); - } - ProtocolUtils.writeUuid(buf, uuid); - ProtocolUtils.writeVarInt(buf, action); - switch (action) { - case ADD: - if (name == null) { - throw new IllegalStateException("No name specified!"); - } - name.write(buf); - buf.writeFloat(percent); - ProtocolUtils.writeVarInt(buf, color); - ProtocolUtils.writeVarInt(buf, overlay); - buf.writeByte(flags); - break; - case REMOVE: - break; - case UPDATE_PERCENT: - buf.writeFloat(percent); - break; - case UPDATE_NAME: - if (name == null) { - throw new IllegalStateException("No name specified!"); - } - name.write(buf); - break; - case UPDATE_STYLE: - ProtocolUtils.writeVarInt(buf, color); - ProtocolUtils.writeVarInt(buf, overlay); - break; - case UPDATE_PROPERTIES: - buf.writeByte(flags); - break; - default: - throw new UnsupportedOperationException("Unknown action " + action); - } - } - - private static byte serializeFlags(Set flags) { - byte val = 0x0; + private static short serializeFlags(Set flags) { + short val = 0x0; for (BossBar.Flag flag : flags) { val |= FLAG_BITS_TO_PROTOCOL.get(flag); } @@ -287,4 +112,88 @@ private static byte serializeFlags(Set flags) { public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public BossBarPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + UUID uuid = ProtocolUtils.readUuid(buf); + int action = ProtocolUtils.readVarInt(buf); + ComponentHolder name = null; + float percent = 0; + int color = 0; + int overlay = 0; + short flags = 0; + + switch (action) { + case ADD: + name = ComponentHolder.read(buf, version); + percent = buf.readFloat(); + color = ProtocolUtils.readVarInt(buf); + overlay = ProtocolUtils.readVarInt(buf); + flags = buf.readUnsignedByte(); + break; + case REMOVE: + break; + case UPDATE_PERCENT: + percent = buf.readFloat(); + break; + case UPDATE_NAME: + name = ComponentHolder.read(buf, version); + break; + case UPDATE_STYLE: + color = ProtocolUtils.readVarInt(buf); + overlay = ProtocolUtils.readVarInt(buf); + break; + case UPDATE_PROPERTIES: + flags = buf.readUnsignedByte(); + break; + default: + throw new UnsupportedOperationException("Unknown action " + action); + } + + return new BossBarPacket(uuid, action, name, percent, color, overlay, flags); + } + + @Override + public void encode(BossBarPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + ProtocolUtils.writeUuid(buf, packet.uuid); + ProtocolUtils.writeVarInt(buf, packet.action); + switch (packet.action) { + case ADD: + if (packet.name == null) { + throw new IllegalStateException("No name specified!"); + } + packet.name.write(buf); + buf.writeFloat(packet.percent); + ProtocolUtils.writeVarInt(buf, packet.color); + ProtocolUtils.writeVarInt(buf, packet.overlay); + buf.writeByte(packet.flags); + break; + case REMOVE: + break; + case UPDATE_PERCENT: + buf.writeFloat(packet.percent); + break; + case UPDATE_NAME: + if (packet.name == null) { + throw new IllegalStateException("No name specified!"); + } + packet.name.write(buf); + break; + case UPDATE_STYLE: + ProtocolUtils.writeVarInt(buf, packet.color); + ProtocolUtils.writeVarInt(buf, packet.overlay); + break; + case UPDATE_PROPERTIES: + buf.writeByte(packet.flags); + break; + default: + throw new UnsupportedOperationException("Unknown action " + packet.action); + } + } + } } \ No newline at end of file diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/BundleDelimiterPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/BundleDelimiterPacket.java index 4da691c763..6e110d45b4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/BundleDelimiterPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/BundleDelimiterPacket.java @@ -20,6 +20,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; @@ -30,17 +31,22 @@ private BundleDelimiterPacket() { } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - } + @Override + public BundleDelimiterPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return BundleDelimiterPacket.INSTANCE; + } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + @Override + public void encode(BundleDelimiterPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java index 39e6fde02c..5d8f17ba0b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java @@ -20,34 +20,46 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import java.util.Objects; import org.checkerframework.checker.nullness.qual.Nullable; -public class ClientSettingsPacket implements MinecraftPacket { - private @Nullable String locale; - private byte viewDistance; - private int chatVisibility; - private boolean chatColors; - private byte difficulty; // 1.7 Protocol - private short skinParts; - private int mainHand; - private boolean textFilteringEnabled; // Added in 1.17 - private boolean clientListingAllowed; // Added in 1.18, overwrites server-list "anonymous" mode - private int particleStatus; // Added in 1.21.2 +public final class ClientSettingsPacket implements MinecraftPacket { + private final @Nullable String locale; + private final byte viewDistance; + private final int chatVisibility; + private final boolean chatColors; + private final byte difficulty; // 1.7 Protocol + private final short skinParts; + private final int mainHand; + private final boolean textFilteringEnabled; // Added in 1.17 + private final boolean clientListingAllowed; // Added in 1.18, overwrites server-list "anonymous" mode + private final int particleStatus; // Added in 1.21.2 public ClientSettingsPacket() { + this(null, (byte) 0, 0, false, (byte) 0, (short) 0, 0, false, false, 0); } - public ClientSettingsPacket(String locale, byte viewDistance, int chatVisibility, boolean chatColors, - short skinParts, int mainHand, boolean textFilteringEnabled, boolean clientListingAllowed, - int particleStatus) { + public ClientSettingsPacket(String locale, byte viewDistance, int chatVisibility, + boolean chatColors, short skinParts, int mainHand, + boolean textFilteringEnabled, boolean clientListingAllowed, + int particleStatus) { + this(locale, viewDistance, chatVisibility, chatColors, (byte) 0, skinParts, mainHand, + textFilteringEnabled, clientListingAllowed, particleStatus); + } + + public ClientSettingsPacket(String locale, byte viewDistance, int chatVisibility, + boolean chatColors, byte difficulty, short skinParts, int mainHand, + boolean textFilteringEnabled, boolean clientListingAllowed, + int particleStatus) { this.locale = locale; this.viewDistance = viewDistance; this.chatVisibility = chatVisibility; this.chatColors = chatColors; + this.difficulty = difficulty; this.skinParts = skinParts; this.mainHand = mainHand; this.textFilteringEnabled = textFilteringEnabled; @@ -55,150 +67,51 @@ public ClientSettingsPacket(String locale, byte viewDistance, int chatVisibility this.particleStatus = particleStatus; } - public String getLocale() { + public String locale() { if (locale == null) { throw new IllegalStateException("No locale specified"); } return locale; } - public void setLocale(String locale) { - this.locale = locale; - } - - public byte getViewDistance() { + public byte viewDistance() { return viewDistance; } - public void setViewDistance(byte viewDistance) { - this.viewDistance = viewDistance; - } - - public int getChatVisibility() { + public int chatVisibility() { return chatVisibility; } - public void setChatVisibility(int chatVisibility) { - this.chatVisibility = chatVisibility; - } - - public boolean isChatColors() { + public boolean chatColors() { return chatColors; } - public void setChatColors(boolean chatColors) { - this.chatColors = chatColors; + public byte difficulty() { + return difficulty; } - public short getSkinParts() { + public short skinParts() { return skinParts; } - public void setSkinParts(short skinParts) { - this.skinParts = skinParts; - } - - public int getMainHand() { + public int mainHand() { return mainHand; } - public void setMainHand(int mainHand) { - this.mainHand = mainHand; - } - - public boolean isTextFilteringEnabled() { + public boolean textFilteringEnabled() { return textFilteringEnabled; } - public void setTextFilteringEnabled(boolean textFilteringEnabled) { - this.textFilteringEnabled = textFilteringEnabled; - } - - public boolean isClientListingAllowed() { + public boolean clientListingAllowed() { return clientListingAllowed; } - public void setClientListingAllowed(boolean clientListingAllowed) { - this.clientListingAllowed = clientListingAllowed; - } - - public int getParticleStatus() { + public int particleStatus() { return particleStatus; } - public void setParticleStatus(int particleStatus) { - this.particleStatus = particleStatus; - } - - @Override - public String toString() { - return "ClientSettings{" + "locale='" + locale + '\'' + ", viewDistance=" + viewDistance + - ", chatVisibility=" + chatVisibility + ", chatColors=" + chatColors + ", skinParts=" + - skinParts + ", mainHand=" + mainHand + ", chatFilteringEnabled=" + textFilteringEnabled + - ", clientListingAllowed=" + clientListingAllowed + ", particleStatus=" + particleStatus + '}'; - } - - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - this.locale = ProtocolUtils.readString(buf, 16); - this.viewDistance = buf.readByte(); - this.chatVisibility = ProtocolUtils.readVarInt(buf); - this.chatColors = buf.readBoolean(); - - if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_7_6)) { - this.difficulty = buf.readByte(); - } - - this.skinParts = buf.readUnsignedByte(); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9)) { - this.mainHand = ProtocolUtils.readVarInt(buf); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { - this.textFilteringEnabled = buf.readBoolean(); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) { - this.clientListingAllowed = buf.readBoolean(); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { - this.particleStatus = ProtocolUtils.readVarInt(buf); - } - } - } - } - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (locale == null) { - throw new IllegalStateException("No locale specified"); - } - ProtocolUtils.writeString(buf, locale); - buf.writeByte(viewDistance); - ProtocolUtils.writeVarInt(buf, chatVisibility); - buf.writeBoolean(chatColors); - - if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_7_6)) { - buf.writeByte(difficulty); - } - - buf.writeByte(skinParts); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9)) { - ProtocolUtils.writeVarInt(buf, mainHand); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { - buf.writeBoolean(textFilteringEnabled); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) { - buf.writeBoolean(clientListingAllowed); - } - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { - ProtocolUtils.writeVarInt(buf, particleStatus); - } - } - } + public String getLocale() { + return locale(); } @Override @@ -227,18 +140,81 @@ public boolean equals(@Nullable final Object o) { && Objects.equals(locale, that.locale); } - @Override - public int hashCode() { - return Objects.hash( - locale, - viewDistance, - chatVisibility, - chatColors, - difficulty, - skinParts, - mainHand, - textFilteringEnabled, - clientListingAllowed, - particleStatus); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public ClientSettingsPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + String locale = ProtocolUtils.readString(buf, 16); + byte viewDistance = buf.readByte(); + int chatVisibility = ProtocolUtils.readVarInt(buf); + boolean chatColors = buf.readBoolean(); + byte difficulty = 0; + + if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_7_6)) { + difficulty = buf.readByte(); + } + + short skinParts = buf.readUnsignedByte(); + int mainHand = 0; + boolean textFilteringEnabled = false; + boolean clientListingAllowed = false; + int particleStatus = 0; + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9)) { + mainHand = ProtocolUtils.readVarInt(buf); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { + textFilteringEnabled = buf.readBoolean(); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) { + clientListingAllowed = buf.readBoolean(); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + particleStatus = ProtocolUtils.readVarInt(buf); + } + } + } + } + + return new ClientSettingsPacket(locale, viewDistance, chatVisibility, chatColors, + difficulty, skinParts, mainHand, textFilteringEnabled, clientListingAllowed, + particleStatus); + } + + @Override + public void encode(ClientSettingsPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + if (packet.locale == null) { + throw new IllegalStateException("No locale specified"); + } + ProtocolUtils.writeString(buf, packet.locale); + buf.writeByte(packet.viewDistance); + ProtocolUtils.writeVarInt(buf, packet.chatVisibility); + buf.writeBoolean(packet.chatColors); + + if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_7_6)) { + buf.writeByte(packet.difficulty); + } + + buf.writeByte(packet.skinParts); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9)) { + ProtocolUtils.writeVarInt(buf, packet.mainHand); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { + buf.writeBoolean(packet.textFilteringEnabled); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) { + buf.writeBoolean(packet.clientListingAllowed); + } + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + ProtocolUtils.writeVarInt(buf, packet.particleStatus); + } + } + } + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundCookieRequestPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundCookieRequestPacket.java index fd558b29ca..82368d7fa4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundCookieRequestPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundCookieRequestPacket.java @@ -20,38 +20,36 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; import net.kyori.adventure.key.Key; -public class ClientboundCookieRequestPacket implements MinecraftPacket { - - private Key key; +public record ClientboundCookieRequestPacket(Key key) implements MinecraftPacket { public Key getKey() { return key; } - public ClientboundCookieRequestPacket() { - } - - public ClientboundCookieRequestPacket(final Key key) { - this.key = key; - } - @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - this.key = ProtocolUtils.readKey(buf); + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeKey(buf, key); - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + @Override + public ClientboundCookieRequestPacket decode(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + return new ClientboundCookieRequestPacket(ProtocolUtils.readKey(buf)); + } + + @Override + public void encode(ClientboundCookieRequestPacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + ProtocolUtils.writeKey(buf, packet.key); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundSoundEntityPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundSoundEntityPacket.java index 459f143010..2abfabb9f8 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundSoundEntityPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundSoundEntityPacket.java @@ -20,82 +20,52 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; +import java.util.concurrent.ThreadLocalRandom; import net.kyori.adventure.sound.Sound; import org.jetbrains.annotations.Nullable; -import java.util.Random; -public class ClientboundSoundEntityPacket implements MinecraftPacket { - - private static final Random SEEDS_RANDOM = new Random(); - - private Sound sound; - private @Nullable Float fixedRange; - private int emitterEntityId; - - public ClientboundSoundEntityPacket() {} - - public ClientboundSoundEntityPacket(Sound sound, @Nullable Float fixedRange, int emitterEntityId) { - this.sound = sound; - this.fixedRange = fixedRange; - this.emitterEntityId = emitterEntityId; - } +public record ClientboundSoundEntityPacket(Sound sound, @Nullable Float fixedRange, + int emitterEntityId) implements MinecraftPacket { @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - throw new UnsupportedOperationException("Decode is not implemented"); + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeVarInt(buf, 0); // version-dependent, hardcoded sound ID - - ProtocolUtils.writeMinimalKey(buf, sound.name()); - - buf.writeBoolean(fixedRange != null); - if (fixedRange != null) - buf.writeFloat(fixedRange); - - ProtocolUtils.writeSoundSource(buf, protocolVersion, sound.source()); - - ProtocolUtils.writeVarInt(buf, emitterEntityId); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - buf.writeFloat(sound.volume()); + @Override + public ClientboundSoundEntityPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + throw new UnsupportedOperationException("Decode is not implemented"); + } - buf.writeFloat(sound.pitch()); + @Override + public void encode(ClientboundSoundEntityPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, 0); // version-dependent, hardcoded sound ID - buf.writeLong(sound.seed().orElse(SEEDS_RANDOM.nextLong())); - } - - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } + ProtocolUtils.writeMinimalKey(buf, packet.sound.name()); - public Sound getSound() { - return sound; - } + buf.writeBoolean(packet.fixedRange != null); + if (packet.fixedRange != null) { + buf.writeFloat(packet.fixedRange); + } - public void setSound(Sound sound) { - this.sound = sound; - } + ProtocolUtils.writeSoundSource(buf, protocolVersion, packet.sound.source()); - public @Nullable Float getFixedRange() { - return fixedRange; - } + ProtocolUtils.writeVarInt(buf, packet.emitterEntityId); - public void setFixedRange(@Nullable Float fixedRange) { - this.fixedRange = fixedRange; - } + buf.writeFloat(packet.sound.volume()); - public int getEmitterEntityId() { - return emitterEntityId; - } + buf.writeFloat(packet.sound.pitch()); - public void setEmitterEntityId(int emitterEntityId) { - this.emitterEntityId = emitterEntityId; + buf.writeLong(packet.sound.seed().orElse(ThreadLocalRandom.current().nextLong())); + } } - } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStopSoundPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStopSoundPacket.java index 3e085d38f7..9d85af8277 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStopSoundPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStopSoundPacket.java @@ -20,6 +20,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import net.kyori.adventure.key.Key; @@ -28,82 +29,60 @@ import javax.annotation.Nullable; -public class ClientboundStopSoundPacket implements MinecraftPacket { - - private @Nullable Sound.Source source; - private @Nullable Key soundName; - - public ClientboundStopSoundPacket() {} +public record ClientboundStopSoundPacket(@Nullable Sound.Source source, + @Nullable Key soundName) implements MinecraftPacket { public ClientboundStopSoundPacket(SoundStop soundStop) { this(soundStop.source(), soundStop.sound()); } - public ClientboundStopSoundPacket(@Nullable Sound.Source source, @Nullable Key soundName) { - this.source = source; - this.soundName = soundName; + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - int flagsBitmask = buf.readByte(); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - if ((flagsBitmask & 1) != 0) { - source = ProtocolUtils.readSoundSource(buf, protocolVersion); - } else { - source = null; - } + @Override + public ClientboundStopSoundPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + int flagsBitmask = buf.readByte(); - if ((flagsBitmask & 2) != 0) { - soundName = ProtocolUtils.readKey(buf); - } else { - soundName = null; - } - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - int flagsBitmask = 0; - if (source != null && soundName == null) { - flagsBitmask |= 1; - } else if (soundName != null && source == null) { - flagsBitmask |= 2; - } else if (source != null /*&& sound != null*/) { - flagsBitmask |= 3; - } + Sound.Source source = null; + if ((flagsBitmask & 1) != 0) { + source = ProtocolUtils.readSoundSource(buf, protocolVersion); + } - buf.writeByte(flagsBitmask); + Key soundName = null; + if ((flagsBitmask & 2) != 0) { + soundName = ProtocolUtils.readKey(buf); + } - if (source != null) { - ProtocolUtils.writeSoundSource(buf, protocolVersion, source); + return new ClientboundStopSoundPacket(source, soundName); } - if (soundName != null) { - ProtocolUtils.writeMinimalKey(buf, soundName); + @Override + public void encode(ClientboundStopSoundPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + int flagsBitmask = 0; + if (packet.source != null && packet.soundName == null) { + flagsBitmask |= 1; + } else if (packet.soundName != null && packet.source == null) { + flagsBitmask |= 2; + } else if (packet.source != null /*&& sound != null*/) { + flagsBitmask |= 3; + } + + buf.writeByte(flagsBitmask); + + if (packet.source != null) { + ProtocolUtils.writeSoundSource(buf, protocolVersion, packet.source); + } + + if (packet.soundName != null) { + ProtocolUtils.writeMinimalKey(buf, packet.soundName); + } } } - - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } - - @Nullable - public Sound.Source getSource() { - return source; - } - - public void setSource(@Nullable Sound.Source source) { - this.source = source; - } - - @Nullable - public Key getSoundName() { - return soundName; - } - - public void setSoundName(@Nullable Key soundName) { - this.soundName = soundName; - } - } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStoreCookiePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStoreCookiePacket.java index 7823b55841..a071ddbf78 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStoreCookiePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStoreCookiePacket.java @@ -20,46 +20,34 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; import net.kyori.adventure.key.Key; -public class ClientboundStoreCookiePacket implements MinecraftPacket { - - private Key key; - private byte[] payload; - - public Key getKey() { - return key; - } - - public byte[] getPayload() { - return payload; - } - - public ClientboundStoreCookiePacket() { - } - - public ClientboundStoreCookiePacket(final Key key, final byte[] payload) { - this.key = key; - this.payload = payload; - } - - @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - this.key = ProtocolUtils.readKey(buf); - this.payload = ProtocolUtils.readByteArray(buf, 5120); - } - - @Override - public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeKey(buf, key); - ProtocolUtils.writeByteArray(buf, payload); - } +public record ClientboundStoreCookiePacket(Key key, byte[] payload) implements MinecraftPacket { @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public ClientboundStoreCookiePacket decode(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + return new ClientboundStoreCookiePacket(ProtocolUtils.readKey(buf), + ProtocolUtils.readByteArray(buf, 5120)); + } + + @Override + public void encode(ClientboundStoreCookiePacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + ProtocolUtils.writeKey(buf, packet.key); + ProtocolUtils.writeByteArray(buf, packet.payload); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogClearPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogClearPacket.java index 4188abfdb9..0f06acc24a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogClearPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogClearPacket.java @@ -20,10 +20,11 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; -import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; +import com.velocitypowered.proxy.protocol.PacketCodec; +import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class DialogClearPacket implements MinecraftPacket { +public final class DialogClearPacket implements MinecraftPacket { public static final DialogClearPacket INSTANCE = new DialogClearPacket(); @@ -31,15 +32,22 @@ private DialogClearPacket() { } @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + @Override + public DialogClearPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return DialogClearPacket.INSTANCE; + } + + @Override + public void encode(DialogClearPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogShowPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogShowPacket.java index 67d4b8f82a..160c092d0a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogShowPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogShowPacket.java @@ -20,45 +20,71 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.StateRegistry; import io.netty.buffer.ByteBuf; import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.BinaryTagIO; -public class DialogShowPacket implements MinecraftPacket { +public final class DialogShowPacket implements MinecraftPacket { private final StateRegistry state; - private int id; - private BinaryTag nbt; + private final int id; + private final BinaryTag nbt; - public DialogShowPacket(final StateRegistry state) { + public DialogShowPacket(StateRegistry state, int id, BinaryTag nbt) { this.state = state; + this.id = id; + this.nbt = nbt; } - @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - this.id = this.state == StateRegistry.CONFIG ? 0 : ProtocolUtils.readVarInt(buf); - if (this.id == 0) { - this.nbt = ProtocolUtils.readBinaryTag(buf, protocolVersion, BinaryTagIO.reader()); - } + public StateRegistry state() { + return state; } - @Override - public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - if (this.state == StateRegistry.CONFIG) { - ProtocolUtils.writeBinaryTag(buf, protocolVersion, this.nbt); - } else { - ProtocolUtils.writeVarInt(buf, this.id); - if (this.id == 0) { - ProtocolUtils.writeBinaryTag(buf, protocolVersion, this.nbt); - } - } + public int id() { + return id; + } + + public BinaryTag nbt() { + return nbt; } @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + private final StateRegistry state; + + public Codec(StateRegistry state) { + this.state = state; + } + + @Override + public DialogShowPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + int id = state == StateRegistry.CONFIG ? 0 : ProtocolUtils.readVarInt(buf); + BinaryTag nbt = null; + if (id == 0) { + nbt = ProtocolUtils.readBinaryTag(buf, protocolVersion, BinaryTagIO.reader()); + } + return new DialogShowPacket(state, id, nbt); + } + + @Override + public void encode(DialogShowPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + if (packet.state == StateRegistry.CONFIG) { + ProtocolUtils.writeBinaryTag(buf, protocolVersion, packet.nbt); + } else { + ProtocolUtils.writeVarInt(buf, packet.id); + if (packet.id == 0) { + ProtocolUtils.writeBinaryTag(buf, protocolVersion, packet.nbt); + } + } + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DisconnectPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DisconnectPacket.java index dd16cb61ed..0526a9ace8 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DisconnectPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DisconnectPacket.java @@ -21,54 +21,18 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import io.netty.buffer.ByteBuf; import net.kyori.adventure.text.Component; -import org.checkerframework.checker.nullness.qual.Nullable; -public class DisconnectPacket implements MinecraftPacket { +public record DisconnectPacket(StateRegistry state, ComponentHolder reason) + implements MinecraftPacket { - private @Nullable ComponentHolder reason; - private final StateRegistry state; - - public DisconnectPacket(StateRegistry state) { - this.state = state; - } - - private DisconnectPacket(StateRegistry state, ComponentHolder reason) { - this.state = state; - this.reason = Preconditions.checkNotNull(reason, "reason"); - } - - public ComponentHolder getReason() { - if (reason == null) { - throw new IllegalStateException("No reason specified"); - } - return reason; - } - - public void setReason(@Nullable ComponentHolder reason) { - this.reason = reason; - } - - @Override - public String toString() { - return "Disconnect{" - + "reason='" + reason + '\'' - + '}'; - } - - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - reason = ComponentHolder.read(buf, state == StateRegistry.LOGIN - ? ProtocolVersion.MINECRAFT_1_20_2 : version); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - getReason().write(buf); + public DisconnectPacket { + Preconditions.checkNotNull(reason, "reason"); } @Override @@ -76,9 +40,32 @@ public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } - public static DisconnectPacket create(Component component, ProtocolVersion version, StateRegistry state) { + public static DisconnectPacket create(Component component, ProtocolVersion version, + StateRegistry state) { Preconditions.checkNotNull(component, "component"); return new DisconnectPacket(state, new ComponentHolder(state == StateRegistry.LOGIN ? ProtocolVersion.MINECRAFT_1_20_2 : version, component)); } -} \ No newline at end of file + + public static class Codec implements PacketCodec { + private final StateRegistry state; + + public Codec(StateRegistry state) { + this.state = state; + } + + @Override + public DisconnectPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + ComponentHolder reason = ComponentHolder.read(buf, state == StateRegistry.LOGIN + ? ProtocolVersion.MINECRAFT_1_20_2 : protocolVersion); + return new DisconnectPacket(state, reason); + } + + @Override + public void encode(DisconnectPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + packet.reason().write(buf); + } + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequestPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequestPacket.java index 422d5e117c..91d649b17e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequestPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionRequestPacket.java @@ -17,80 +17,90 @@ package com.velocitypowered.proxy.protocol.packet; -import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -import java.util.Arrays; -public class EncryptionRequestPacket implements MinecraftPacket { +public final class EncryptionRequestPacket implements MinecraftPacket { - private String serverId = ""; - private byte[] publicKey = EMPTY_BYTE_ARRAY; - private byte[] verifyToken = EMPTY_BYTE_ARRAY; - private boolean shouldAuthenticate = true; + private final String serverId; + private final byte[] publicKey; + private final byte[] verifyToken; + private final boolean shouldAuthenticate; - public byte[] getPublicKey() { - return publicKey.clone(); + public EncryptionRequestPacket(String serverId, byte[] publicKey, byte[] verifyToken, + boolean shouldAuthenticate) { + this.serverId = serverId; + this.publicKey = publicKey.clone(); + this.verifyToken = verifyToken.clone(); + this.shouldAuthenticate = shouldAuthenticate; } - public void setPublicKey(byte[] publicKey) { - this.publicKey = publicKey.clone(); + public String serverId() { + return serverId; } - public byte[] getVerifyToken() { + public byte[] publicKey() { + return publicKey.clone(); + } + + public byte[] verifyToken() { return verifyToken.clone(); } - public void setVerifyToken(byte[] verifyToken) { - this.verifyToken = verifyToken.clone(); + public boolean shouldAuthenticate() { + return shouldAuthenticate; } @Override - public String toString() { - return "EncryptionRequest{" - + "publicKey=" + Arrays.toString(publicKey) - + ", verifyToken=" + Arrays.toString(verifyToken) - + '}'; + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - this.serverId = ProtocolUtils.readString(buf, 20); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - publicKey = ProtocolUtils.readByteArray(buf, 256); - verifyToken = ProtocolUtils.readByteArray(buf, 16); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { - shouldAuthenticate = buf.readBoolean(); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public EncryptionRequestPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + String serverId = ProtocolUtils.readString(buf, 20); + byte[] publicKey; + byte[] verifyToken; + boolean shouldAuthenticate = true; + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + publicKey = ProtocolUtils.readByteArray(buf, 256); + verifyToken = ProtocolUtils.readByteArray(buf, 16); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { + shouldAuthenticate = buf.readBoolean(); + } + } else { + publicKey = ProtocolUtils.readByteArray17(buf); + verifyToken = ProtocolUtils.readByteArray17(buf); } - } else { - publicKey = ProtocolUtils.readByteArray17(buf); - verifyToken = ProtocolUtils.readByteArray17(buf); + + return new EncryptionRequestPacket(serverId, publicKey, verifyToken, shouldAuthenticate); } - } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - ProtocolUtils.writeString(buf, this.serverId); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - ProtocolUtils.writeByteArray(buf, publicKey); - ProtocolUtils.writeByteArray(buf, verifyToken); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { - buf.writeBoolean(shouldAuthenticate); + @Override + public void encode(EncryptionRequestPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + ProtocolUtils.writeString(buf, packet.serverId); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + ProtocolUtils.writeByteArray(buf, packet.publicKey); + ProtocolUtils.writeByteArray(buf, packet.verifyToken); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { + buf.writeBoolean(packet.shouldAuthenticate); + } + } else { + ProtocolUtils.writeByteArray17(packet.publicKey, buf, false); + ProtocolUtils.writeByteArray17(packet.verifyToken, buf, false); } - } else { - ProtocolUtils.writeByteArray17(publicKey, buf, false); - ProtocolUtils.writeByteArray17(verifyToken, buf, false); } } - - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponsePacket.java index fedb67a5e3..8566982bf7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponsePacket.java @@ -17,43 +17,60 @@ package com.velocitypowered.proxy.protocol.packet; -import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.util.except.QuietDecoderException; import io.netty.buffer.ByteBuf; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Arrays; -public class EncryptionResponsePacket implements MinecraftPacket { +public final class EncryptionResponsePacket implements MinecraftPacket { private static final QuietDecoderException NO_SALT = new QuietDecoderException( "Encryption response didn't contain salt"); - private byte[] sharedSecret = EMPTY_BYTE_ARRAY; - private byte[] verifyToken = EMPTY_BYTE_ARRAY; - private @Nullable Long salt; + private final byte[] sharedSecret; + private final byte[] verifyToken; + private final @Nullable Long salt; - public byte[] getSharedSecret() { + public EncryptionResponsePacket(byte[] sharedSecret, byte[] verifyToken, @Nullable Long salt) { + this.sharedSecret = sharedSecret.clone(); + this.verifyToken = verifyToken.clone(); + this.salt = salt; + } + + public byte[] sharedSecret() { return sharedSecret.clone(); } - public byte[] getVerifyToken() { + public byte[] verifyToken() { return verifyToken.clone(); } - public long getSalt() { + public long salt() { if (salt == null) { throw NO_SALT; } return salt; } + public byte[] getSharedSecret() { + return sharedSecret(); + } + + public byte[] getVerifyToken() { + return verifyToken(); + } + + public long getSalt() { + return salt(); + } + @Override public String toString() { return "EncryptionResponse{" @@ -63,72 +80,86 @@ public String toString() { } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - this.sharedSecret = ProtocolUtils.readByteArray(buf, 128); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19) - && version.lessThan(ProtocolVersion.MINECRAFT_1_19_3) - && !buf.readBoolean()) { - salt = buf.readLong(); + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public EncryptionResponsePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + byte[] sharedSecret; + byte[] verifyToken; + Long salt = null; + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + sharedSecret = ProtocolUtils.readByteArray(buf, 128); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19) + && version.lessThan(ProtocolVersion.MINECRAFT_1_19_3) + && !buf.readBoolean()) { + salt = buf.readLong(); + } + + verifyToken = ProtocolUtils.readByteArray(buf, + version.noLessThan(ProtocolVersion.MINECRAFT_1_19) ? 256 : 128); + } else { + sharedSecret = ProtocolUtils.readByteArray17(buf); + verifyToken = ProtocolUtils.readByteArray17(buf); } - this.verifyToken = ProtocolUtils.readByteArray(buf, - version.noLessThan(ProtocolVersion.MINECRAFT_1_19) ? 256 : 128); - } else { - this.sharedSecret = ProtocolUtils.readByteArray17(buf); - this.verifyToken = ProtocolUtils.readByteArray17(buf); + return new EncryptionResponsePacket(sharedSecret, verifyToken, salt); } - } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - ProtocolUtils.writeByteArray(buf, sharedSecret); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19) - && version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { - if (salt != null) { - buf.writeBoolean(false); - buf.writeLong(salt); - } else { - buf.writeBoolean(true); + @Override + public void encode(EncryptionResponsePacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + ProtocolUtils.writeByteArray(buf, packet.sharedSecret); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19) + && version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { + if (packet.salt != null) { + buf.writeBoolean(false); + buf.writeLong(packet.salt); + } else { + buf.writeBoolean(true); + } } + ProtocolUtils.writeByteArray(buf, packet.verifyToken); + } else { + ProtocolUtils.writeByteArray17(packet.sharedSecret, buf, false); + ProtocolUtils.writeByteArray17(packet.verifyToken, buf, false); } - ProtocolUtils.writeByteArray(buf, verifyToken); - } else { - ProtocolUtils.writeByteArray17(sharedSecret, buf, false); - ProtocolUtils.writeByteArray17(verifyToken, buf, false); } - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } - - @Override - public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { - // It turns out these come out to the same length, whether we're talking >=1.8 or not. - // The length prefix always winds up being 2 bytes. - int base = 256 + 2 + 2; - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_3)) { - return base + 128; - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - // Verify token is twice as long on 1.19+ - // Additional 1 byte for left <> right and 8 bytes for salt - base += 128 + 8 + 1; + @Override + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + // It turns out these come out to the same length, whether we're talking >=1.8 or not. + // The length prefix always winds up being 2 bytes. + int base = 256 + 2 + 2; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_3)) { + return base + 128; + } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + // Verify token is twice as long on 1.19+ + // Additional 1 byte for left <> right and 8 bytes for salt + base += 128 + 8 + 1; + } + return base; } - return base; - } - @Override - public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { - int base = decodeExpectedMaxLength(buf, direction, version); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - // These are "optional" - base -= 128 + 8; + @Override + public int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + int base = decodeExpectedMaxLength(buf, direction, version); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + // These are "optional" + base -= 128 + 8; + } + return base; } - return base; } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HandshakePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HandshakePacket.java index 88cb3688bd..f4d4a407bb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HandshakePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HandshakePacket.java @@ -23,84 +23,23 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; -public class HandshakePacket implements MinecraftPacket { +public record HandshakePacket(ProtocolVersion protocolVersion, String serverAddress, int port, + HandshakeIntent intent) + implements MinecraftPacket { // This size was chosen to ensure Forge clients can still connect even with very long hostnames. // While DNS technically allows any character to be used, in practice ASCII is used. private static final int MAXIMUM_HOSTNAME_LENGTH = 255 + HANDSHAKE_HOSTNAME_TOKEN.length() + 1; - private ProtocolVersion protocolVersion; - private String serverAddress = ""; - private int port; - private HandshakeIntent intent; - private int nextStatus; - public ProtocolVersion getProtocolVersion() { - return protocolVersion; - } - - public void setProtocolVersion(ProtocolVersion protocolVersion) { - this.protocolVersion = protocolVersion; - } - - public String getServerAddress() { - return serverAddress; - } - - public void setServerAddress(String serverAddress) { - this.serverAddress = serverAddress; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public int getNextStatus() { - return this.nextStatus; - } - - public void setIntent(HandshakeIntent intent) { - this.intent = intent; - this.nextStatus = intent.id(); - } + public static final HandshakePacket DEFAULT = new HandshakePacket(ProtocolVersion.MINIMUM_VERSION, "", 0, + HandshakeIntent.LOGIN); - public HandshakeIntent getIntent() { - return this.intent; - } - - @Override - public String toString() { - return "Handshake{" - + "protocolVersion=" + protocolVersion - + ", serverAddress='" + serverAddress + '\'' - + ", port=" + port - + ", nextStatus=" + nextStatus - + '}'; - } - - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion ignored) { - int realProtocolVersion = ProtocolUtils.readVarInt(buf); - this.protocolVersion = ProtocolVersion.getProtocolVersion(realProtocolVersion); - this.serverAddress = ProtocolUtils.readString(buf, MAXIMUM_HOSTNAME_LENGTH); - this.port = buf.readUnsignedShort(); - this.nextStatus = ProtocolUtils.readVarInt(buf); - this.intent = HandshakeIntent.getById(nextStatus); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion ignored) { - ProtocolUtils.writeVarInt(buf, this.protocolVersion.getProtocol()); - ProtocolUtils.writeString(buf, this.serverAddress); - buf.writeShort(this.port); - ProtocolUtils.writeVarInt(buf, this.nextStatus); + public int nextStatus() { + return intent.id(); } @Override @@ -108,22 +47,47 @@ public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } - @Override - public int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion version) { - return 7; - } - - @Override - public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion version) { - return 9 + (MAXIMUM_HOSTNAME_LENGTH * 3); - } - - @Override - public int encodeSizeHint(Direction direction, ProtocolVersion version) { - // We could compute an exact size, but 4KiB ought to be enough to encode all reasonable - // sizes of this packet. - return 4 * 1024; + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public HandshakePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion ignored) { + int realProtocolVersion = ProtocolUtils.readVarInt(buf); + ProtocolVersion protocolVersion = ProtocolVersion.getProtocolVersion(realProtocolVersion); + String serverAddress = ProtocolUtils.readString(buf, MAXIMUM_HOSTNAME_LENGTH); + int port = buf.readUnsignedShort(); + int nextStatus = ProtocolUtils.readVarInt(buf); + HandshakeIntent intent = HandshakeIntent.getById(nextStatus); + return new HandshakePacket(protocolVersion, serverAddress, port, intent); + } + + @Override + public void encode(HandshakePacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion ignored) { + ProtocolUtils.writeVarInt(buf, packet.protocolVersion().getProtocol()); + ProtocolUtils.writeString(buf, packet.serverAddress()); + buf.writeShort(packet.port()); + ProtocolUtils.writeVarInt(buf, packet.nextStatus()); + } + + @Override + public int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + return 7; + } + + @Override + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + return 9 + (MAXIMUM_HOSTNAME_LENGTH * 3); + } + + @Override + public int encodeSizeHint(HandshakePacket packet, ProtocolUtils.Direction direction, ProtocolVersion version) { + // We could compute an exact size, but 4KiB ought to be enough to encode all reasonable + // sizes of this packet. + return 4 * 1024; + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HeaderAndFooterPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HeaderAndFooterPacket.java index 339d026f8f..c184ec996b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HeaderAndFooterPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HeaderAndFooterPacket.java @@ -21,42 +21,18 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import io.netty.buffer.ByteBuf; import net.kyori.adventure.text.Component; -public class HeaderAndFooterPacket implements MinecraftPacket { +public record HeaderAndFooterPacket(ComponentHolder header, + ComponentHolder footer) implements MinecraftPacket { - private final ComponentHolder header; - private final ComponentHolder footer; - - public HeaderAndFooterPacket() { - throw new UnsupportedOperationException("Decode is not implemented"); - } - - public HeaderAndFooterPacket(ComponentHolder header, ComponentHolder footer) { - this.header = Preconditions.checkNotNull(header, "header"); - this.footer = Preconditions.checkNotNull(footer, "footer"); - } - - public ComponentHolder getHeader() { - return header; - } - - public ComponentHolder getFooter() { - return footer; - } - - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - throw new UnsupportedOperationException("Decode is not implemented"); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - header.write(buf); - footer.write(buf); + public HeaderAndFooterPacket { + Preconditions.checkNotNull(header, "header"); + Preconditions.checkNotNull(footer, "footer"); } @Override @@ -74,4 +50,21 @@ public static HeaderAndFooterPacket reset(ProtocolVersion version) { ComponentHolder empty = new ComponentHolder(version, Component.empty()); return new HeaderAndFooterPacket(empty, empty); } -} \ No newline at end of file + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public HeaderAndFooterPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + throw new UnsupportedOperationException("Decode is not implemented"); + } + + @Override + public void encode(HeaderAndFooterPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + packet.header.write(buf); + packet.footer.write(buf); + } + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGamePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGamePacket.java index 787d858ebf..80f170ce7d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGamePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGamePacket.java @@ -22,62 +22,83 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.registry.DimensionInfo; import com.velocitypowered.proxy.protocol.*; +import com.velocitypowered.proxy.protocol.PacketCodec; import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.Pair; import net.kyori.adventure.nbt.BinaryTagIO; import net.kyori.adventure.nbt.CompoundBinaryTag; import org.checkerframework.checker.nullness.qual.Nullable; -public class JoinGamePacket implements MinecraftPacket { +public final class JoinGamePacket implements MinecraftPacket { private static final BinaryTagIO.Reader JOINGAME_READER = BinaryTagIO.reader(4 * 1024 * 1024); - private int entityId; - private short gamemode; - private int dimension; - private long partialHashedSeed; // 1.15+ - private short difficulty; - private boolean isHardcore; - private int maxPlayers; - private @Nullable String levelType; - private int viewDistance; // 1.14+ - private boolean reducedDebugInfo; - private boolean showRespawnScreen; - private boolean doLimitedCrafting; // 1.20.2+ - private ImmutableSet levelNames; // 1.16+ - private CompoundBinaryTag registry; // 1.16+ - private DimensionInfo dimensionInfo; // 1.16+ - private CompoundBinaryTag currentDimensionData; // 1.16.2+ - private short previousGamemode; // 1.16+ - private int simulationDistance; // 1.18+ - private @Nullable Pair lastDeathPosition; // 1.19+ - private int portalCooldown; // 1.20+ - private int seaLevel; // 1.21.2+ - private boolean enforcesSecureChat; // 1.20.5+ - public int getEntityId() { - return entityId; + private final int entityId; + private final short gamemode; + private final int dimension; + private final long partialHashedSeed; + private final short difficulty; + private final boolean isHardcore; + private final int maxPlayers; + private final @Nullable String levelType; + private final int viewDistance; + private final boolean reducedDebugInfo; + private final boolean showRespawnScreen; + private final boolean doLimitedCrafting; + private final ImmutableSet levelNames; + private final CompoundBinaryTag registry; + private final DimensionInfo dimensionInfo; + private final CompoundBinaryTag currentDimensionData; + private final short previousGamemode; + private final int simulationDistance; + private final @Nullable Pair lastDeathPosition; + private final int portalCooldown; + private final int seaLevel; + private final boolean enforcesSecureChat; + + public JoinGamePacket(int entityId, short gamemode, int dimension, long partialHashedSeed, + short difficulty, boolean isHardcore, int maxPlayers, @Nullable String levelType, + int viewDistance, boolean reducedDebugInfo, boolean showRespawnScreen, + boolean doLimitedCrafting, ImmutableSet levelNames, CompoundBinaryTag registry, + DimensionInfo dimensionInfo, CompoundBinaryTag currentDimensionData, short previousGamemode, + int simulationDistance, @Nullable Pair lastDeathPosition, int portalCooldown, + int seaLevel, boolean enforcesSecureChat) { + this.entityId = entityId; + this.gamemode = gamemode; + this.dimension = dimension; + this.partialHashedSeed = partialHashedSeed; + this.difficulty = difficulty; + this.isHardcore = isHardcore; + this.maxPlayers = maxPlayers; + this.levelType = levelType; + this.viewDistance = viewDistance; + this.reducedDebugInfo = reducedDebugInfo; + this.showRespawnScreen = showRespawnScreen; + this.doLimitedCrafting = doLimitedCrafting; + this.levelNames = levelNames; + this.registry = registry; + this.dimensionInfo = dimensionInfo; + this.currentDimensionData = currentDimensionData; + this.previousGamemode = previousGamemode; + this.simulationDistance = simulationDistance; + this.lastDeathPosition = lastDeathPosition; + this.portalCooldown = portalCooldown; + this.seaLevel = seaLevel; + this.enforcesSecureChat = enforcesSecureChat; } - public void setEntityId(int entityId) { - this.entityId = entityId; + public int getEntityId() { + return entityId; } public short getGamemode() { return gamemode; } - public void setGamemode(short gamemode) { - this.gamemode = gamemode; - } - public int getDimension() { return dimension; } - public void setDimension(int dimension) { - this.dimension = dimension; - } - public long getPartialHashedSeed() { return partialHashedSeed; } @@ -86,74 +107,38 @@ public short getDifficulty() { return difficulty; } - public void setDifficulty(short difficulty) { - this.difficulty = difficulty; - } - public int getMaxPlayers() { return maxPlayers; } - public void setMaxPlayers(int maxPlayers) { - this.maxPlayers = maxPlayers; - } - public @Nullable String getLevelType() { return levelType; } - public void setLevelType(@Nullable String levelType) { - this.levelType = levelType; - } - public int getViewDistance() { return viewDistance; } - public void setViewDistance(int viewDistance) { - this.viewDistance = viewDistance; - } - public boolean isReducedDebugInfo() { return reducedDebugInfo; } - public void setReducedDebugInfo(boolean reducedDebugInfo) { - this.reducedDebugInfo = reducedDebugInfo; - } - public DimensionInfo getDimensionInfo() { return dimensionInfo; } - public void setDimensionInfo(DimensionInfo dimensionInfo) { - this.dimensionInfo = dimensionInfo; - } - public short getPreviousGamemode() { return previousGamemode; } - public void setPreviousGamemode(short previousGamemode) { - this.previousGamemode = previousGamemode; - } - public boolean getIsHardcore() { return isHardcore; } - public void setIsHardcore(boolean isHardcore) { - this.isHardcore = isHardcore; - } - public boolean getDoLimitedCrafting() { return doLimitedCrafting; } - public void setDoLimitedCrafting(boolean doLimitedCrafting) { - this.doLimitedCrafting = doLimitedCrafting; - } - public CompoundBinaryTag getCurrentDimensionData() { return currentDimensionData; } @@ -162,46 +147,34 @@ public int getSimulationDistance() { return simulationDistance; } - public void setSimulationDistance(int simulationDistance) { - this.simulationDistance = simulationDistance; - } - public Pair getLastDeathPosition() { return lastDeathPosition; } - public void setLastDeathPosition(Pair lastDeathPosition) { - this.lastDeathPosition = lastDeathPosition; - } - public int getPortalCooldown() { return portalCooldown; } - public void setPortalCooldown(int portalCooldown) { - this.portalCooldown = portalCooldown; - } - public int getSeaLevel() { return seaLevel; } - public void setSeaLevel(int seaLevel) { - this.seaLevel = seaLevel; - } - public boolean getEnforcesSecureChat() { return this.enforcesSecureChat; } - public void setEnforcesSecureChat(final boolean enforcesSecureChat) { - this.enforcesSecureChat = enforcesSecureChat; - } - public CompoundBinaryTag getRegistry() { return registry; } + public boolean getShowRespawnScreen() { + return showRespawnScreen; + } + + public ImmutableSet getLevelNames() { + return levelNames; + } + @Override public String toString() { return "JoinGame{" + "entityId=" + entityId + ", gamemode=" + gamemode + ", dimension=" + @@ -218,305 +191,350 @@ public String toString() { } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { - // haha funny, they made 1.20.2 more complicated - this.decode1202Up(buf, version); - } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { - // Minecraft 1.16 and above have significantly more complicated logic for reading this packet, - // so separate it out. - this.decode116Up(buf, version); - } else { - this.decodeLegacy(buf, version); - } + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - private void decodeLegacy(ByteBuf buf, ProtocolVersion version) { - this.entityId = buf.readInt(); - this.gamemode = buf.readByte(); - this.isHardcore = (this.gamemode & 0x08) != 0; - this.gamemode &= ~0x08; + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9_1)) { - this.dimension = buf.readInt(); - } else { - this.dimension = buf.readByte(); - } - if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) { - this.difficulty = buf.readUnsignedByte(); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { - this.partialHashedSeed = buf.readLong(); - } - this.maxPlayers = buf.readUnsignedByte(); - this.levelType = ProtocolUtils.readString(buf, 16); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_14)) { - this.viewDistance = ProtocolUtils.readVarInt(buf); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - this.reducedDebugInfo = buf.readBoolean(); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { - this.showRespawnScreen = buf.readBoolean(); + @Override + public JoinGamePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { + // haha funny, they made 1.20.2 more complicated + return decode1202Up(buf, version); + } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { + // Minecraft 1.16 and above have significantly more complicated logic for reading this packet, + // so separate it out. + return decode116Up(buf, version); + } else { + return decodeLegacy(buf, version); + } } - } - private void decode116Up(ByteBuf buf, ProtocolVersion version) { - this.entityId = buf.readInt(); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) { - this.isHardcore = buf.readBoolean(); - this.gamemode = buf.readByte(); - } else { - this.gamemode = buf.readByte(); - this.isHardcore = (this.gamemode & 0x08) != 0; - this.gamemode &= ~0x08; - } - this.previousGamemode = buf.readByte(); - - this.levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf)); - this.registry = ProtocolUtils.readCompoundTag(buf, version, JOINGAME_READER); - String dimensionIdentifier; - String levelName = null; - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2) - && version.lessThan(ProtocolVersion.MINECRAFT_1_19)) { - this.currentDimensionData = ProtocolUtils.readCompoundTag(buf, version, JOINGAME_READER); - dimensionIdentifier = ProtocolUtils.readString(buf); - } else { - dimensionIdentifier = ProtocolUtils.readString(buf); - levelName = ProtocolUtils.readString(buf); + @Override + public void encode(JoinGamePacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { + // haha funny, they made 1.20.2 more complicated + encode1202Up(packet, buf, version); + } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { + // Minecraft 1.16 and above have significantly more complicated logic for reading this packet, + // so separate it out. + encode116Up(packet, buf, version); + } else { + encodeLegacy(packet, buf, version); + } } - this.partialHashedSeed = buf.readLong(); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) { - this.maxPlayers = ProtocolUtils.readVarInt(buf); - } else { - this.maxPlayers = buf.readUnsignedByte(); - } + private static JoinGamePacket decodeLegacy(ByteBuf buf, ProtocolVersion version) { + int entityId = buf.readInt(); + short gamemode = buf.readByte(); + boolean isHardcore = (gamemode & 0x08) != 0; + gamemode &= ~0x08; - this.viewDistance = ProtocolUtils.readVarInt(buf); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) { - this.simulationDistance = ProtocolUtils.readVarInt(buf); - } + int dimension; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9_1)) { + dimension = buf.readInt(); + } else { + dimension = buf.readByte(); + } + + short difficulty = 0; + if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) { + difficulty = buf.readUnsignedByte(); + } - this.reducedDebugInfo = buf.readBoolean(); - this.showRespawnScreen = buf.readBoolean(); + long partialHashedSeed = 0; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { + partialHashedSeed = buf.readLong(); + } - boolean isDebug = buf.readBoolean(); - boolean isFlat = buf.readBoolean(); - this.dimensionInfo = new DimensionInfo(dimensionIdentifier, levelName, isFlat, isDebug, version); + int maxPlayers = buf.readUnsignedByte(); + String levelType = ProtocolUtils.readString(buf, 16); - // optional death location - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19) && buf.readBoolean()) { - this.lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong()); - } + int viewDistance = 0; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_14)) { + viewDistance = ProtocolUtils.readVarInt(buf); + } + + boolean reducedDebugInfo = false; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + reducedDebugInfo = buf.readBoolean(); + } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) { - this.portalCooldown = ProtocolUtils.readVarInt(buf); + boolean showRespawnScreen = true; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { + showRespawnScreen = buf.readBoolean(); + } + + return new JoinGamePacket(entityId, gamemode, dimension, partialHashedSeed, difficulty, + isHardcore, maxPlayers, levelType, viewDistance, reducedDebugInfo, showRespawnScreen, + false, ImmutableSet.of(), null, null, null, (short) 0, 0, null, 0, 0, false); } - } - @SuppressWarnings("checkstyle:VariableDeclarationUsageDistance") - private void decode1202Up(ByteBuf buf, ProtocolVersion version) { - this.entityId = buf.readInt(); - this.isHardcore = buf.readBoolean(); + private static JoinGamePacket decode116Up(ByteBuf buf, ProtocolVersion version) { + int entityId = buf.readInt(); + boolean isHardcore; + short gamemode; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) { + isHardcore = buf.readBoolean(); + gamemode = buf.readByte(); + } else { + gamemode = buf.readByte(); + isHardcore = (gamemode & 0x08) != 0; + gamemode &= ~0x08; + } + short previousGamemode = buf.readByte(); - this.levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf)); + ImmutableSet levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf)); + CompoundBinaryTag registry = ProtocolUtils.readCompoundTag(buf, version, JOINGAME_READER); - this.maxPlayers = ProtocolUtils.readVarInt(buf); + String dimensionIdentifier; + String levelName = null; + CompoundBinaryTag currentDimensionData = null; - this.viewDistance = ProtocolUtils.readVarInt(buf); - this.simulationDistance = ProtocolUtils.readVarInt(buf); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2) + && version.lessThan(ProtocolVersion.MINECRAFT_1_19)) { + currentDimensionData = ProtocolUtils.readCompoundTag(buf, version, JOINGAME_READER); + dimensionIdentifier = ProtocolUtils.readString(buf); + } else { + dimensionIdentifier = ProtocolUtils.readString(buf); + levelName = ProtocolUtils.readString(buf); + } - this.reducedDebugInfo = buf.readBoolean(); - this.showRespawnScreen = buf.readBoolean(); - this.doLimitedCrafting = buf.readBoolean(); + long partialHashedSeed = buf.readLong(); - String dimensionKey = ""; - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { - dimension = ProtocolUtils.readVarInt(buf); - } else { - dimensionKey = ProtocolUtils.readString(buf); - } - String levelName = ProtocolUtils.readString(buf); - this.partialHashedSeed = buf.readLong(); + int maxPlayers; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) { + maxPlayers = ProtocolUtils.readVarInt(buf); + } else { + maxPlayers = buf.readUnsignedByte(); + } - this.gamemode = buf.readByte(); - this.previousGamemode = buf.readByte(); + int viewDistance = ProtocolUtils.readVarInt(buf); - boolean isDebug = buf.readBoolean(); - boolean isFlat = buf.readBoolean(); - this.dimensionInfo = new DimensionInfo(dimensionKey, levelName, isFlat, isDebug, version); + int simulationDistance = 0; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) { + simulationDistance = ProtocolUtils.readVarInt(buf); + } - // optional death location - if (buf.readBoolean()) { - this.lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong()); - } + boolean reducedDebugInfo = buf.readBoolean(); + boolean showRespawnScreen = buf.readBoolean(); - this.portalCooldown = ProtocolUtils.readVarInt(buf); + boolean isDebug = buf.readBoolean(); + boolean isFlat = buf.readBoolean(); + DimensionInfo dimensionInfo = new DimensionInfo(dimensionIdentifier, levelName, isFlat, isDebug, version); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { - this.seaLevel = ProtocolUtils.readVarInt(buf); - } + Pair lastDeathPosition = null; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19) && buf.readBoolean()) { + lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong()); + } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { - this.enforcesSecureChat = buf.readBoolean(); - } - } + int portalCooldown = 0; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) { + portalCooldown = ProtocolUtils.readVarInt(buf); + } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { - // haha funny, they made 1.20.2 more complicated - this.encode1202Up(buf, version); - } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { - // Minecraft 1.16 and above have significantly more complicated logic for reading this packet, - // so separate it out. - this.encode116Up(buf, version); - } else { - this.encodeLegacy(buf, version); + return new JoinGamePacket(entityId, gamemode, 0, partialHashedSeed, (short) 0, isHardcore, + maxPlayers, null, viewDistance, reducedDebugInfo, showRespawnScreen, false, levelNames, + registry, dimensionInfo, currentDimensionData, previousGamemode, simulationDistance, + lastDeathPosition, portalCooldown, 0, false); } - } - private void encodeLegacy(ByteBuf buf, ProtocolVersion version) { - buf.writeInt(entityId); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) { - buf.writeBoolean(isHardcore); - buf.writeByte(gamemode); - } else { - buf.writeByte(isHardcore ? gamemode | 0x8 : gamemode); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9_1)) { - buf.writeInt(dimension); - } else { - buf.writeByte(dimension); - } - if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) { - buf.writeByte(difficulty); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { - buf.writeLong(partialHashedSeed); - } - buf.writeByte(maxPlayers); - if (levelType == null) { - throw new IllegalStateException("No level type specified."); - } - ProtocolUtils.writeString(buf, levelType); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_14)) { - ProtocolUtils.writeVarInt(buf, viewDistance); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - buf.writeBoolean(reducedDebugInfo); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { - buf.writeBoolean(showRespawnScreen); - } - } + @SuppressWarnings("checkstyle:VariableDeclarationUsageDistance") + private static JoinGamePacket decode1202Up(ByteBuf buf, ProtocolVersion version) { + int entityId = buf.readInt(); + boolean isHardcore = buf.readBoolean(); - private void encode116Up(ByteBuf buf, ProtocolVersion version) { - buf.writeInt(entityId); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) { - buf.writeBoolean(isHardcore); - buf.writeByte(gamemode); - } else { - buf.writeByte(isHardcore ? gamemode | 0x8 : gamemode); - } - buf.writeByte(previousGamemode); - - ProtocolUtils.writeStringArray(buf, levelNames.toArray(String[]::new)); - ProtocolUtils.writeBinaryTag(buf, version, this.registry); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2) && version.lessThan(ProtocolVersion.MINECRAFT_1_19)) { - ProtocolUtils.writeBinaryTag(buf, version, currentDimensionData); - ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier()); - } else { - ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier()); - ProtocolUtils.writeString(buf, dimensionInfo.getLevelName()); - } + ImmutableSet levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf)); + + int maxPlayers = ProtocolUtils.readVarInt(buf); + + int viewDistance = ProtocolUtils.readVarInt(buf); + int simulationDistance = ProtocolUtils.readVarInt(buf); + + boolean reducedDebugInfo = buf.readBoolean(); + boolean showRespawnScreen = buf.readBoolean(); + boolean doLimitedCrafting = buf.readBoolean(); + + int dimension = 0; + String dimensionKey = ""; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { + dimension = ProtocolUtils.readVarInt(buf); + } else { + dimensionKey = ProtocolUtils.readString(buf); + } + String levelName = ProtocolUtils.readString(buf); + long partialHashedSeed = buf.readLong(); + + short gamemode = buf.readByte(); + short previousGamemode = buf.readByte(); + + boolean isDebug = buf.readBoolean(); + boolean isFlat = buf.readBoolean(); + DimensionInfo dimensionInfo = new DimensionInfo(dimensionKey, levelName, isFlat, isDebug, version); + + Pair lastDeathPosition = null; + if (buf.readBoolean()) { + lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong()); + } + + int portalCooldown = ProtocolUtils.readVarInt(buf); - buf.writeLong(partialHashedSeed); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) { - ProtocolUtils.writeVarInt(buf, maxPlayers); - } else { - buf.writeByte(maxPlayers); + int seaLevel = 0; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + seaLevel = ProtocolUtils.readVarInt(buf); + } + + boolean enforcesSecureChat = false; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { + enforcesSecureChat = buf.readBoolean(); + } + + return new JoinGamePacket(entityId, gamemode, dimension, partialHashedSeed, (short) 0, + isHardcore, maxPlayers, null, viewDistance, reducedDebugInfo, showRespawnScreen, + doLimitedCrafting, levelNames, null, dimensionInfo, null, previousGamemode, + simulationDistance, lastDeathPosition, portalCooldown, seaLevel, enforcesSecureChat); } - ProtocolUtils.writeVarInt(buf, viewDistance); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) { - ProtocolUtils.writeVarInt(buf, simulationDistance); + private static void encodeLegacy(JoinGamePacket packet, ByteBuf buf, ProtocolVersion version) { + buf.writeInt(packet.entityId); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) { + buf.writeBoolean(packet.isHardcore); + buf.writeByte(packet.gamemode); + } else { + buf.writeByte(packet.isHardcore ? packet.gamemode | 0x8 : packet.gamemode); + } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9_1)) { + buf.writeInt(packet.dimension); + } else { + buf.writeByte(packet.dimension); + } + if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) { + buf.writeByte(packet.difficulty); + } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { + buf.writeLong(packet.partialHashedSeed); + } + buf.writeByte(packet.maxPlayers); + if (packet.levelType == null) { + throw new IllegalStateException("No level type specified."); + } + ProtocolUtils.writeString(buf, packet.levelType); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_14)) { + ProtocolUtils.writeVarInt(buf, packet.viewDistance); + } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + buf.writeBoolean(packet.reducedDebugInfo); + } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { + buf.writeBoolean(packet.showRespawnScreen); + } } - buf.writeBoolean(reducedDebugInfo); - buf.writeBoolean(showRespawnScreen); + private static void encode116Up(JoinGamePacket packet, ByteBuf buf, ProtocolVersion version) { + buf.writeInt(packet.entityId); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) { + buf.writeBoolean(packet.isHardcore); + buf.writeByte(packet.gamemode); + } else { + buf.writeByte(packet.isHardcore ? packet.gamemode | 0x8 : packet.gamemode); + } + buf.writeByte(packet.previousGamemode); - buf.writeBoolean(dimensionInfo.isDebugType()); - buf.writeBoolean(dimensionInfo.isFlat()); + ProtocolUtils.writeStringArray(buf, packet.levelNames.toArray(String[]::new)); + ProtocolUtils.writeBinaryTag(buf, version, packet.registry); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2) && version.lessThan(ProtocolVersion.MINECRAFT_1_19)) { + ProtocolUtils.writeBinaryTag(buf, version, packet.currentDimensionData); + ProtocolUtils.writeString(buf, packet.dimensionInfo.getRegistryIdentifier()); + } else { + ProtocolUtils.writeString(buf, packet.dimensionInfo.getRegistryIdentifier()); + ProtocolUtils.writeString(buf, packet.dimensionInfo.getLevelName()); + } - // optional death location - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - if (lastDeathPosition != null) { - buf.writeBoolean(true); - ProtocolUtils.writeString(buf, lastDeathPosition.key()); - buf.writeLong(lastDeathPosition.value()); + buf.writeLong(packet.partialHashedSeed); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) { + ProtocolUtils.writeVarInt(buf, packet.maxPlayers); } else { - buf.writeBoolean(false); + buf.writeByte(packet.maxPlayers); + } + + ProtocolUtils.writeVarInt(buf, packet.viewDistance); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) { + ProtocolUtils.writeVarInt(buf, packet.simulationDistance); } - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) { - ProtocolUtils.writeVarInt(buf, portalCooldown); + buf.writeBoolean(packet.reducedDebugInfo); + buf.writeBoolean(packet.showRespawnScreen); + + buf.writeBoolean(packet.dimensionInfo.isDebugType()); + buf.writeBoolean(packet.dimensionInfo.isFlat()); + + // optional death location + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + if (packet.lastDeathPosition != null) { + buf.writeBoolean(true); + ProtocolUtils.writeString(buf, packet.lastDeathPosition.key()); + buf.writeLong(packet.lastDeathPosition.value()); + } else { + buf.writeBoolean(false); + } + } + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) { + ProtocolUtils.writeVarInt(buf, packet.portalCooldown); + } } - } - private void encode1202Up(ByteBuf buf, ProtocolVersion version) { - buf.writeInt(entityId); - buf.writeBoolean(isHardcore); + private static void encode1202Up(JoinGamePacket packet, ByteBuf buf, ProtocolVersion version) { + buf.writeInt(packet.entityId); + buf.writeBoolean(packet.isHardcore); - ProtocolUtils.writeStringArray(buf, levelNames.toArray(String[]::new)); + ProtocolUtils.writeStringArray(buf, packet.levelNames.toArray(String[]::new)); - ProtocolUtils.writeVarInt(buf, maxPlayers); + ProtocolUtils.writeVarInt(buf, packet.maxPlayers); - ProtocolUtils.writeVarInt(buf, viewDistance); - ProtocolUtils.writeVarInt(buf, simulationDistance); + ProtocolUtils.writeVarInt(buf, packet.viewDistance); + ProtocolUtils.writeVarInt(buf, packet.simulationDistance); - buf.writeBoolean(reducedDebugInfo); - buf.writeBoolean(showRespawnScreen); - buf.writeBoolean(doLimitedCrafting); + buf.writeBoolean(packet.reducedDebugInfo); + buf.writeBoolean(packet.showRespawnScreen); + buf.writeBoolean(packet.doLimitedCrafting); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { - ProtocolUtils.writeVarInt(buf, dimension); - } else { - ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier()); - } - ProtocolUtils.writeString(buf, dimensionInfo.getLevelName()); - buf.writeLong(partialHashedSeed); - - buf.writeByte(gamemode); - buf.writeByte(previousGamemode); - - buf.writeBoolean(dimensionInfo.isDebugType()); - buf.writeBoolean(dimensionInfo.isFlat()); - - // optional death location - if (lastDeathPosition != null) { - buf.writeBoolean(true); - ProtocolUtils.writeString(buf, lastDeathPosition.key()); - buf.writeLong(lastDeathPosition.value()); - } else { - buf.writeBoolean(false); - } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { + ProtocolUtils.writeVarInt(buf, packet.dimension); + } else { + ProtocolUtils.writeString(buf, packet.dimensionInfo.getRegistryIdentifier()); + } + ProtocolUtils.writeString(buf, packet.dimensionInfo.getLevelName()); + buf.writeLong(packet.partialHashedSeed); - ProtocolUtils.writeVarInt(buf, portalCooldown); + buf.writeByte(packet.gamemode); + buf.writeByte(packet.previousGamemode); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { - ProtocolUtils.writeVarInt(buf, seaLevel); - } + buf.writeBoolean(packet.dimensionInfo.isDebugType()); + buf.writeBoolean(packet.dimensionInfo.isFlat()); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { - buf.writeBoolean(this.enforcesSecureChat); - } - } + // optional death location + if (packet.lastDeathPosition != null) { + buf.writeBoolean(true); + ProtocolUtils.writeString(buf, packet.lastDeathPosition.key()); + buf.writeLong(packet.lastDeathPosition.value()); + } else { + buf.writeBoolean(false); + } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + ProtocolUtils.writeVarInt(buf, packet.portalCooldown); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + ProtocolUtils.writeVarInt(buf, packet.seaLevel); + } + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { + buf.writeBoolean(packet.enforcesSecureChat); + } + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlivePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlivePacket.java index a44e50eeaa..1689ce1c96 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlivePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlivePacket.java @@ -20,52 +20,44 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class KeepAlivePacket implements MinecraftPacket { - - private long randomId; - - public long getRandomId() { - return randomId; - } - - public void setRandomId(long randomId) { - this.randomId = randomId; - } +public record KeepAlivePacket(long randomId) implements MinecraftPacket { @Override - public String toString() { - return "KeepAlive{" - + "randomId=" + randomId - + '}'; + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_12_2)) { - randomId = buf.readLong(); - } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - randomId = ProtocolUtils.readVarInt(buf); - } else { - randomId = buf.readInt(); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public KeepAlivePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + long randomId; + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_12_2)) { + randomId = buf.readLong(); + } else if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + randomId = ProtocolUtils.readVarInt(buf); + } else { + randomId = buf.readInt(); + } + return new KeepAlivePacket(randomId); } - } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_12_2)) { - buf.writeLong(randomId); - } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - ProtocolUtils.writeVarInt(buf, (int) randomId); - } else { - buf.writeInt((int) randomId); + @Override + public void encode(KeepAlivePacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_12_2)) { + buf.writeLong(packet.randomId); + } else if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + ProtocolUtils.writeVarInt(buf, (int) packet.randomId); + } else { + buf.writeInt((int) packet.randomId); + } } } - - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyHandshakePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyHandshakePacket.java index 38483ed864..bfc793c6b9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyHandshakePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyHandshakePacket.java @@ -20,23 +20,35 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class LegacyHandshakePacket implements MinecraftPacket { +public final class LegacyHandshakePacket implements MinecraftPacket { - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - throw new UnsupportedOperationException(); - } + public static final LegacyHandshakePacket INSTANCE = new LegacyHandshakePacket(); - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - throw new UnsupportedOperationException(); + private LegacyHandshakePacket() { } @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public LegacyHandshakePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + throw new UnsupportedOperationException(); + } + + @Override + public void encode(LegacyHandshakePacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + throw new UnsupportedOperationException(); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPingPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPingPacket.java index 656d3222b2..ee1e10ca83 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPingPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPingPacket.java @@ -20,13 +20,14 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.legacyping.LegacyMinecraftPingVersion; import io.netty.buffer.ByteBuf; import java.net.InetSocketAddress; import org.checkerframework.checker.nullness.qual.Nullable; -public class LegacyPingPacket implements MinecraftPacket { +public final class LegacyPingPacket implements MinecraftPacket { private final LegacyMinecraftPingVersion version; private final @Nullable InetSocketAddress vhost; @@ -41,26 +42,40 @@ public LegacyPingPacket(LegacyMinecraftPingVersion version, InetSocketAddress vh this.vhost = vhost; } - public LegacyMinecraftPingVersion getVersion() { + public LegacyMinecraftPingVersion version() { return version; } - public @Nullable InetSocketAddress getVhost() { + public @Nullable InetSocketAddress vhost() { return vhost; } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - throw new UnsupportedOperationException(); + public LegacyMinecraftPingVersion getVersion() { + return version(); } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - throw new UnsupportedOperationException(); + public @Nullable InetSocketAddress getVhost() { + return vhost(); } @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public LegacyPingPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + throw new UnsupportedOperationException(); + } + + @Override + public void encode(LegacyPingPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + throw new UnsupportedOperationException(); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPlayerListItemPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPlayerListItemPacket.java index 268c707d6f..3bfe5db635 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPlayerListItemPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyPlayerListItemPacket.java @@ -24,6 +24,7 @@ import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import java.util.ArrayList; @@ -33,22 +34,20 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.checkerframework.checker.nullness.qual.Nullable; -public class LegacyPlayerListItemPacket implements MinecraftPacket { +public final class LegacyPlayerListItemPacket implements MinecraftPacket { public static final int ADD_PLAYER = 0; public static final int UPDATE_GAMEMODE = 1; public static final int UPDATE_LATENCY = 2; public static final int UPDATE_DISPLAY_NAME = 3; public static final int REMOVE_PLAYER = 4; - private int action; - private final List items = new ArrayList<>(); + + private final int action; + private final List items; public LegacyPlayerListItemPacket(int action, List items) { this.action = action; - this.items.addAll(items); - } - - public LegacyPlayerListItemPacket() { + this.items = ImmutableList.copyOf(items); } public int getAction() { @@ -60,130 +59,138 @@ public List getItems() { } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - action = ProtocolUtils.readVarInt(buf); - int length = ProtocolUtils.readVarInt(buf); - - for (int i = 0; i < length; i++) { - Item item = new Item(ProtocolUtils.readUuid(buf)); - items.add(item); - switch (action) { - case ADD_PLAYER: - item.setName(ProtocolUtils.readString(buf)); - item.setProperties(ProtocolUtils.readProperties(buf)); - item.setGameMode(ProtocolUtils.readVarInt(buf)); - item.setLatency(ProtocolUtils.readVarInt(buf)); - item.setDisplayName(readOptionalComponent(buf, version)); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - if (buf.readBoolean()) { - item.setPlayerKey(ProtocolUtils.readPlayerKey(version, buf)); + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public LegacyPlayerListItemPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + int action = ProtocolUtils.readVarInt(buf); + int length = ProtocolUtils.readVarInt(buf); + List items = new ArrayList<>(length); + + for (int i = 0; i < length; i++) { + Item item = new Item(ProtocolUtils.readUuid(buf)); + items.add(item); + switch (action) { + case ADD_PLAYER: + item.setName(ProtocolUtils.readString(buf)); + item.setProperties(ProtocolUtils.readProperties(buf)); + item.setGameMode(ProtocolUtils.readVarInt(buf)); + item.setLatency(ProtocolUtils.readVarInt(buf)); + item.setDisplayName(readOptionalComponent(buf, version)); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + if (buf.readBoolean()) { + item.setPlayerKey(ProtocolUtils.readPlayerKey(version, buf)); + } } - } - break; - case UPDATE_GAMEMODE: - item.setGameMode(ProtocolUtils.readVarInt(buf)); - break; - case UPDATE_LATENCY: - item.setLatency(ProtocolUtils.readVarInt(buf)); - break; - case UPDATE_DISPLAY_NAME: - item.setDisplayName(readOptionalComponent(buf, version)); - break; - case REMOVE_PLAYER: - //Do nothing, all that is needed is the uuid - break; - default: - throw new UnsupportedOperationException("Unknown action " + action); + break; + case UPDATE_GAMEMODE: + item.setGameMode(ProtocolUtils.readVarInt(buf)); + break; + case UPDATE_LATENCY: + item.setLatency(ProtocolUtils.readVarInt(buf)); + break; + case UPDATE_DISPLAY_NAME: + item.setDisplayName(readOptionalComponent(buf, version)); + break; + case REMOVE_PLAYER: + //Do nothing, all that is needed is the uuid + break; + default: + throw new UnsupportedOperationException("Unknown action " + action); + } } + return new LegacyPlayerListItemPacket(action, items); + } else { + Item item = new Item(); + item.setName(ProtocolUtils.readString(buf)); + int action = buf.readBoolean() ? ADD_PLAYER : REMOVE_PLAYER; + item.setLatency(buf.readShort()); + return new LegacyPlayerListItemPacket(action, ImmutableList.of(item)); } - } else { - Item item = new Item(); - item.setName(ProtocolUtils.readString(buf)); - action = buf.readBoolean() ? ADD_PLAYER : REMOVE_PLAYER; - item.setLatency(buf.readShort()); - items.add(item); - } - } - - private static @Nullable Component readOptionalComponent(ByteBuf buf, ProtocolVersion version) { - if (buf.readBoolean()) { - return ProtocolUtils.getJsonChatSerializer(version) - .deserialize(ProtocolUtils.readString(buf)); } - return null; - } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - ProtocolUtils.writeVarInt(buf, action); - ProtocolUtils.writeVarInt(buf, items.size()); - for (Item item : items) { - UUID uuid = item.getUuid(); - assert uuid != null : "UUID-less entry serialization attempt - 1.7 component!"; - - ProtocolUtils.writeUuid(buf, uuid); - switch (action) { - case ADD_PLAYER: - ProtocolUtils.writeString(buf, item.getName()); - ProtocolUtils.writeProperties(buf, item.getProperties()); - ProtocolUtils.writeVarInt(buf, item.getGameMode()); - ProtocolUtils.writeVarInt(buf, item.getLatency()); - writeDisplayName(buf, item.getDisplayName(), version); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - if (item.getPlayerKey() != null) { - buf.writeBoolean(true); - ProtocolUtils.writePlayerKey(buf, item.getPlayerKey()); - } else { - buf.writeBoolean(false); + @Override + public void encode(LegacyPlayerListItemPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + ProtocolUtils.writeVarInt(buf, packet.action); + ProtocolUtils.writeVarInt(buf, packet.items.size()); + for (Item item : packet.items) { + UUID uuid = item.getUuid(); + assert uuid != null : "UUID-less entry serialization attempt - 1.7 component!"; + + ProtocolUtils.writeUuid(buf, uuid); + switch (packet.action) { + case ADD_PLAYER: + ProtocolUtils.writeString(buf, item.getName()); + ProtocolUtils.writeProperties(buf, item.getProperties()); + ProtocolUtils.writeVarInt(buf, item.getGameMode()); + ProtocolUtils.writeVarInt(buf, item.getLatency()); + writeDisplayName(buf, item.getDisplayName(), version); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + if (item.getPlayerKey() != null) { + buf.writeBoolean(true); + ProtocolUtils.writePlayerKey(buf, item.getPlayerKey()); + } else { + buf.writeBoolean(false); + } } - } - break; - case UPDATE_GAMEMODE: - ProtocolUtils.writeVarInt(buf, item.getGameMode()); - break; - case UPDATE_LATENCY: - ProtocolUtils.writeVarInt(buf, item.getLatency()); - break; - case UPDATE_DISPLAY_NAME: - writeDisplayName(buf, item.getDisplayName(), version); - break; - case REMOVE_PLAYER: - // Do nothing, all that is needed is the uuid - break; - default: - throw new UnsupportedOperationException("Unknown action " + action); + break; + case UPDATE_GAMEMODE: + ProtocolUtils.writeVarInt(buf, item.getGameMode()); + break; + case UPDATE_LATENCY: + ProtocolUtils.writeVarInt(buf, item.getLatency()); + break; + case UPDATE_DISPLAY_NAME: + writeDisplayName(buf, item.getDisplayName(), version); + break; + case REMOVE_PLAYER: + // Do nothing, all that is needed is the uuid + break; + default: + throw new UnsupportedOperationException("Unknown action " + packet.action); + } } - } - } else { - Item item = items.get(0); - Component displayNameComponent = item.getDisplayName(); - if (displayNameComponent != null) { - String displayName = LegacyComponentSerializer.legacySection() - .serialize(displayNameComponent); - ProtocolUtils.writeString(buf, - displayName.length() > 16 ? displayName.substring(0, 16) : displayName); } else { - ProtocolUtils.writeString(buf, item.getName()); + Item item = packet.items.get(0); + Component displayNameComponent = item.getDisplayName(); + if (displayNameComponent != null) { + String displayName = LegacyComponentSerializer.legacySection() + .serialize(displayNameComponent); + ProtocolUtils.writeString(buf, + displayName.length() > 16 ? displayName.substring(0, 16) : displayName); + } else { + ProtocolUtils.writeString(buf, item.getName()); + } + buf.writeBoolean(packet.action != REMOVE_PLAYER); + buf.writeShort(item.getLatency()); } - buf.writeBoolean(action != REMOVE_PLAYER); - buf.writeShort(item.getLatency()); } - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } + private static @Nullable Component readOptionalComponent(ByteBuf buf, ProtocolVersion version) { + if (buf.readBoolean()) { + return ProtocolUtils.getJsonChatSerializer(version) + .deserialize(ProtocolUtils.readString(buf)); + } + return null; + } - private void writeDisplayName(ByteBuf buf, @Nullable Component displayName, - ProtocolVersion version) { - buf.writeBoolean(displayName != null); - if (displayName != null) { - ProtocolUtils.writeString(buf, ProtocolUtils.getJsonChatSerializer(version) - .serialize(displayName)); + private static void writeDisplayName(ByteBuf buf, @Nullable Component displayName, + ProtocolVersion version) { + buf.writeBoolean(displayName != null); + if (displayName != null) { + ProtocolUtils.writeString(buf, ProtocolUtils.getJsonChatSerializer(version) + .serialize(displayName)); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginAcknowledgedPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginAcknowledgedPacket.java index 16cf519b0b..a40f3c332d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginAcknowledgedPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginAcknowledgedPacket.java @@ -20,29 +20,40 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class LoginAcknowledgedPacket implements MinecraftPacket { +public final class LoginAcknowledgedPacket implements MinecraftPacket { - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - } + public static final LoginAcknowledgedPacket INSTANCE = new LoginAcknowledgedPacket(); - @Override - public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion version) { - return 0; + private LoginAcknowledgedPacket() { } @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public LoginAcknowledgedPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return LoginAcknowledgedPacket.INSTANCE; + } + + @Override + public void encode(LoginAcknowledgedPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + } + + @Override + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return 0; + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java index 2fa82e922c..fafd6f0e57 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java @@ -20,23 +20,19 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; -import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; +import io.netty.buffer.DefaultByteBufHolder; import io.netty.buffer.Unpooled; -import org.checkerframework.checker.nullness.qual.Nullable; -public class LoginPluginMessagePacket extends DeferredByteBufHolder implements MinecraftPacket { +public final class LoginPluginMessagePacket extends DefaultByteBufHolder implements MinecraftPacket { - private int id; - private @Nullable String channel; + private final int id; + private final String channel; - public LoginPluginMessagePacket() { - super(null); - } - - public LoginPluginMessagePacket(int id, @Nullable String channel, ByteBuf data) { + public LoginPluginMessagePacket(int id, String channel, ByteBuf data) { super(data); this.id = id; this.channel = channel; @@ -47,9 +43,6 @@ public int getId() { } public String getChannel() { - if (channel == null) { - throw new IllegalStateException("Channel is not specified!"); - } return channel; } @@ -63,33 +56,41 @@ public String toString() { } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - this.id = ProtocolUtils.readVarInt(buf); - this.channel = ProtocolUtils.readString(buf); - if (buf.isReadable()) { - this.replace(buf.readRetainedSlice(buf.readableBytes())); - } else { - this.replace(Unpooled.EMPTY_BUFFER); - } + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - ProtocolUtils.writeVarInt(buf, id); - if (channel == null) { - throw new IllegalStateException("Channel is not specified!"); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public LoginPluginMessagePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + int id = ProtocolUtils.readVarInt(buf); + String channel = ProtocolUtils.readString(buf); + ByteBuf data; + if (buf.isReadable()) { + data = buf.readRetainedSlice(buf.readableBytes()); + } else { + data = Unpooled.EMPTY_BUFFER; + } + return new LoginPluginMessagePacket(id, channel, data); } - ProtocolUtils.writeString(buf, channel); - buf.writeBytes(content()); - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } + @Override + public void encode(LoginPluginMessagePacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion version) { + ProtocolUtils.writeVarInt(buf, packet.id); + ProtocolUtils.writeString(buf, packet.channel); + buf.writeBytes(packet.content()); + } - @Override - public int encodeSizeHint(Direction direction, ProtocolVersion version) { - return content().readableBytes(); + @Override + public int encodeSizeHint(LoginPluginMessagePacket packet, Direction direction, + ProtocolVersion version) { + return ProtocolUtils.varIntBytes(packet.id) + + ProtocolUtils.stringSizeHint(packet.channel) + + packet.content().readableBytes(); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginResponsePacket.java index e7d9443dca..7ee5514beb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginResponsePacket.java @@ -20,21 +20,18 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; -import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; +import io.netty.buffer.DefaultByteBufHolder; import io.netty.buffer.Unpooled; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -public class LoginPluginResponsePacket extends DeferredByteBufHolder implements MinecraftPacket { +public final class LoginPluginResponsePacket extends DefaultByteBufHolder + implements MinecraftPacket { - private int id; - private boolean success; - - public LoginPluginResponsePacket() { - super(Unpooled.EMPTY_BUFFER); - } + private final int id; + private final boolean success; public LoginPluginResponsePacket(int id, boolean success, @MonotonicNonNull ByteBuf buf) { super(buf); @@ -42,22 +39,14 @@ public LoginPluginResponsePacket(int id, boolean success, @MonotonicNonNull Byte this.success = success; } - public int getId() { + public int id() { return id; } - public void setId(int id) { - this.id = id; - } - - public boolean isSuccess() { + public boolean success() { return success; } - public void setSuccess(boolean success) { - this.success = success; - } - @Override public String toString() { return "LoginPluginResponse{" @@ -67,31 +56,38 @@ public String toString() { + '}'; } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - this.id = ProtocolUtils.readVarInt(buf); - this.success = buf.readBoolean(); - if (buf.isReadable()) { - this.replace(buf.readRetainedSlice(buf.readableBytes())); - } else { - this.replace(Unpooled.EMPTY_BUFFER); - } - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - ProtocolUtils.writeVarInt(buf, id); - buf.writeBoolean(success); - buf.writeBytes(content()); - } - @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } - @Override - public int encodeSizeHint(Direction direction, ProtocolVersion version) { - return content().readableBytes(); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public LoginPluginResponsePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + int id = ProtocolUtils.readVarInt(buf); + boolean success = buf.readBoolean(); + ByteBuf data; + if (buf.isReadable()) { + data = buf.readRetainedSlice(buf.readableBytes()); + } else { + data = Unpooled.EMPTY_BUFFER; + } + return new LoginPluginResponsePacket(id, success, data); + } + + @Override + public void encode(LoginPluginResponsePacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion version) { + ProtocolUtils.writeVarInt(buf, packet.id); + buf.writeBoolean(packet.success); + buf.writeBytes(packet.content()); + } + + public int encodeSizeHint(LoginPluginResponsePacket packet, ProtocolUtils.Direction direction, ProtocolVersion version) { + return ProtocolUtils.varIntBytes(packet.id) + 1 + packet.content().readableBytes(); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PingIdentifyPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PingIdentifyPacket.java index 27c1351d5e..6b01a15036 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PingIdentifyPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PingIdentifyPacket.java @@ -20,30 +20,30 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class PingIdentifyPacket implements MinecraftPacket { - - private int id; +public record PingIdentifyPacket(int id) implements MinecraftPacket { @Override - public String toString() { - return "Ping{" + "id=" + id + '}'; + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - id = buf.readInt(); - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - buf.writeInt(id); - } + @Override + public PingIdentifyPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return new PingIdentifyPacket(buf.readInt()); + } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + @Override + public void encode(PingIdentifyPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + buf.writeInt(packet.id); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java index ecf2887fd9..dca6cd0034 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java @@ -22,38 +22,24 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; -import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import io.netty.buffer.DefaultByteBufHolder; -public class PluginMessagePacket extends DeferredByteBufHolder implements MinecraftPacket { +public final class PluginMessagePacket extends DefaultByteBufHolder implements MinecraftPacket { - private @Nullable String channel; + private final String channel; - public PluginMessagePacket() { - super(null); - } - - public PluginMessagePacket(String channel, - @MonotonicNonNull ByteBuf backing) { + public PluginMessagePacket(String channel, ByteBuf backing) { super(backing); this.channel = channel; } public String getChannel() { - if (channel == null) { - throw new IllegalStateException("Channel is not specified."); - } return channel; } - public void setChannel(String channel) { - this.channel = channel; - } - @Override public String toString() { return "PluginMessage{" @@ -62,44 +48,6 @@ public String toString() { + '}'; } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - this.channel = ProtocolUtils.readString(buf); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_13)) { - this.channel = transformLegacyToModernChannel(this.channel); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - this.replace(buf.readRetainedSlice(buf.readableBytes())); - } else { - this.replace(ProtocolUtils.readRetainedByteBufSlice17(buf)); - } - - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (channel == null) { - throw new IllegalStateException("Channel is not specified."); - } - - if (refCnt() == 0) { - throw new IllegalStateException("Plugin message contents for " + this.channel - + " freed too many times."); - } - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_13)) { - ProtocolUtils.writeString(buf, transformLegacyToModernChannel(this.channel)); - } else { - ProtocolUtils.writeString(buf, this.channel); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - buf.writeBytes(content()); - } else { - ProtocolUtils.writeByteBuf17(content(), buf, true); // True for Forge support - } - - } - @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); @@ -145,8 +93,43 @@ public PluginMessagePacket touch(Object hint) { return (PluginMessagePacket) super.touch(hint); } - @Override - public int encodeSizeHint(Direction direction, ProtocolVersion version) { - return content().readableBytes(); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public PluginMessagePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + String channel = ProtocolUtils.readString(buf); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_13)) { + channel = transformLegacyToModernChannel(channel); + } + ByteBuf data; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + data = buf.readRetainedSlice(buf.readableBytes()); + } else { + data = ProtocolUtils.readRetainedByteBufSlice17(buf); + } + return new PluginMessagePacket(channel, data); + } + + @Override + public void encode(PluginMessagePacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion version) { + if (packet.refCnt() == 0) { + throw new IllegalStateException("Plugin message contents for " + packet.channel + + " freed too many times."); + } + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_13)) { + ProtocolUtils.writeString(buf, transformLegacyToModernChannel(packet.channel)); + } else { + ProtocolUtils.writeString(buf, packet.channel); + } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + buf.writeBytes(packet.content()); + } else { + ProtocolUtils.writeByteBuf17(packet.content(), buf, true); // True for Forge support + } + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemovePlayerInfoPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemovePlayerInfoPacket.java index 90ab387178..a2a97f9287 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemovePlayerInfoPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemovePlayerInfoPacket.java @@ -21,54 +21,44 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -import java.util.ArrayList; import java.util.Collection; import java.util.UUID; -public class RemovePlayerInfoPacket implements MinecraftPacket { - - private Collection profilesToRemove; - - public RemovePlayerInfoPacket() { - this.profilesToRemove = new ArrayList<>(); - } - - public RemovePlayerInfoPacket(Collection profilesToRemove) { - this.profilesToRemove = profilesToRemove; - } +public record RemovePlayerInfoPacket(Collection profilesToRemove) implements MinecraftPacket { public Collection getProfilesToRemove() { return profilesToRemove; } - public void setProfilesToRemove(Collection profilesToRemove) { - this.profilesToRemove = profilesToRemove; - } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - int length = ProtocolUtils.readVarInt(buf); - Collection profilesToRemove = Lists.newArrayListWithCapacity(length); - for (int idx = 0; idx < length; idx++) { - profilesToRemove.add(ProtocolUtils.readUuid(buf)); - } - this.profilesToRemove = profilesToRemove; + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - ProtocolUtils.writeVarInt(buf, this.profilesToRemove.size()); - for (UUID uuid : this.profilesToRemove) { - ProtocolUtils.writeUuid(buf, uuid); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public RemovePlayerInfoPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + int length = ProtocolUtils.readVarInt(buf); + Collection profilesToRemove = Lists.newArrayListWithCapacity(length); + for (int idx = 0; idx < length; idx++) { + profilesToRemove.add(ProtocolUtils.readUuid(buf)); + } + return new RemovePlayerInfoPacket(profilesToRemove); } - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + @Override + public void encode(RemovePlayerInfoPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, packet.profilesToRemove.size()); + for (UUID uuid : packet.profilesToRemove) { + ProtocolUtils.writeUuid(buf, uuid); + } + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemoveResourcePackPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemoveResourcePackPacket.java index d003a0a951..3bd41e8747 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemoveResourcePackPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RemoveResourcePackPacket.java @@ -20,44 +20,44 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; import java.util.UUID; +import org.checkerframework.checker.nullness.qual.Nullable; -public class RemoveResourcePackPacket implements MinecraftPacket { +public record RemoveResourcePackPacket(@Nullable UUID id) implements MinecraftPacket { - private UUID id; - - public RemoveResourcePackPacket() { - } - - public RemoveResourcePackPacket(UUID id) { - this.id = id; - } - - public UUID getId() { + public @Nullable UUID getId() { return id; } @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - if (buf.readBoolean()) { - this.id = ProtocolUtils.readUuid(buf); - } + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - buf.writeBoolean(id != null); - - if (id != null) { - ProtocolUtils.writeUuid(buf, id); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public RemoveResourcePackPacket decode(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + UUID id = null; + if (buf.readBoolean()) { + id = ProtocolUtils.readUuid(buf); + } + return new RemoveResourcePackPacket(id); } - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + @Override + public void encode(RemoveResourcePackPacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + buf.writeBoolean(packet.id != null); + if (packet.id != null) { + ProtocolUtils.writeUuid(buf, packet.id); + } + } } -} \ No newline at end of file +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackRequestPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackRequestPacket.java index a0f86aed11..27d951e5be 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackRequestPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackRequestPacket.java @@ -23,6 +23,7 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; @@ -30,109 +31,27 @@ import io.netty.buffer.ByteBufUtil; import java.util.UUID; import java.util.regex.Pattern; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; -public class ResourcePackRequestPacket implements MinecraftPacket { - - private @MonotonicNonNull UUID id; // 1.20.3+ - private @MonotonicNonNull String url; - private @MonotonicNonNull String hash; - private boolean isRequired; // 1.17+ - private @Nullable ComponentHolder prompt; // 1.17+ +public record ResourcePackRequestPacket( + @Nullable UUID id, // 1.20.3+ + String url, + String hash, + boolean isRequired, // 1.17+ + @Nullable ComponentHolder prompt // 1.17+ +) implements MinecraftPacket { private static final Pattern PLAUSIBLE_SHA1_HASH = Pattern.compile("^[a-z0-9]{40}$"); // 1.20.2+ - public @Nullable UUID getId() { - return id; - } - - public void setId(UUID id) { - this.id = id; - } - - public @Nullable String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public boolean isRequired() { - return isRequired; - } - - public @Nullable String getHash() { - return hash; - } - - public void setHash(String hash) { - this.hash = hash; - } - - public void setRequired(boolean required) { - isRequired = required; - } - - public @Nullable ComponentHolder getPrompt() { - return prompt; - } - - public void setPrompt(@Nullable ComponentHolder prompt) { - this.prompt = prompt; - } - - @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { - this.id = ProtocolUtils.readUuid(buf); - } - this.url = ProtocolUtils.readString(buf); - this.hash = ProtocolUtils.readString(buf); - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { - this.isRequired = buf.readBoolean(); - if (buf.readBoolean()) { - this.prompt = ComponentHolder.read(buf, protocolVersion); - } else { - this.prompt = null; - } - } - } - - @Override - public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { - if (id == null) { - throw new IllegalStateException("Resource pack id not set yet!"); - } - ProtocolUtils.writeUuid(buf, id); - } - if (url == null || hash == null) { - throw new IllegalStateException("Packet not fully filled in yet!"); - } - ProtocolUtils.writeString(buf, url); - ProtocolUtils.writeString(buf, hash); - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { - buf.writeBoolean(isRequired); - if (prompt != null) { - buf.writeBoolean(true); - prompt.write(buf); - } else { - buf.writeBoolean(false); - } - } - } - public VelocityResourcePackInfo toServerPromptedPack() { final ResourcePackInfo.Builder builder = - new VelocityResourcePackInfo.BuilderImpl(Preconditions.checkNotNull(url)) - .setId(id).setPrompt(prompt == null ? null : prompt.getComponent()) - .setShouldForce(isRequired).setOrigin(ResourcePackInfo.Origin.DOWNSTREAM_SERVER); + new VelocityResourcePackInfo.BuilderImpl(Preconditions.checkNotNull(url())) + .setId(id()).setPrompt(prompt() == null ? null : prompt().getComponent()) + .setShouldForce(isRequired()).setOrigin(ResourcePackInfo.Origin.DOWNSTREAM_SERVER); - if (hash != null && !hash.isEmpty()) { - if (PLAUSIBLE_SHA1_HASH.matcher(hash).matches()) { - builder.setHash(ByteBufUtil.decodeHexDump(hash)); + if (hash() != null && !hash().isEmpty()) { + if (PLAUSIBLE_SHA1_HASH.matcher(hash()).matches()) { + builder.setHash(ByteBufUtil.decodeHexDump(hash())); } } return (VelocityResourcePackInfo) builder.build(); @@ -153,4 +72,53 @@ public String toString() { ", prompt=" + prompt + '}'; } -} \ No newline at end of file + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public ResourcePackRequestPacket decode(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + UUID id = null; + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { + id = ProtocolUtils.readUuid(buf); + } + String url = ProtocolUtils.readString(buf); + String hash = ProtocolUtils.readString(buf); + boolean isRequired = false; + ComponentHolder prompt = null; + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { + isRequired = buf.readBoolean(); + if (buf.readBoolean()) { + prompt = ComponentHolder.read(buf, protocolVersion); + } + } + return new ResourcePackRequestPacket(id, url, hash, isRequired, prompt); + } + + @Override + public void encode(ResourcePackRequestPacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { + if (packet.id() == null) { + throw new IllegalStateException("Resource pack id not set yet!"); + } + ProtocolUtils.writeUuid(buf, packet.id()); + } + if (packet.url() == null || packet.hash() == null) { + throw new IllegalStateException("Packet not fully filled in yet!"); + } + ProtocolUtils.writeString(buf, packet.url()); + ProtocolUtils.writeString(buf, packet.hash()); + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { + buf.writeBoolean(packet.isRequired()); + if (packet.prompt() != null) { + buf.writeBoolean(true); + packet.prompt().write(buf); + } else { + buf.writeBoolean(false); + } + } + } + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java index 020c3530de..5fc3b664dc 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java @@ -21,76 +21,61 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import java.util.UUID; -public class ResourcePackResponsePacket implements MinecraftPacket { +public record ResourcePackResponsePacket(UUID id, String hash, + Status status) implements MinecraftPacket { - private UUID id; - private String hash = ""; - private @MonotonicNonNull Status status; - - public ResourcePackResponsePacket() { + public UUID getId() { + return id; } - public ResourcePackResponsePacket(UUID id, String hash, @MonotonicNonNull Status status) { - this.id = id; - this.hash = hash; - this.status = status; + public String getHash() { + return hash; } public Status getStatus() { - if (status == null) { - throw new IllegalStateException("Packet not yet deserialized"); - } return status; } - public String getHash() { - return hash; + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - public UUID getId() { - return id; - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { - this.id = ProtocolUtils.readUuid(buf); - } - if (protocolVersion.noGreaterThan(ProtocolVersion.MINECRAFT_1_9_4)) { - this.hash = ProtocolUtils.readString(buf); + @Override + public ResourcePackResponsePacket decode(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + UUID id = null; + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { + id = ProtocolUtils.readUuid(buf); + } + String hash = ""; + if (protocolVersion.noGreaterThan(ProtocolVersion.MINECRAFT_1_9_4)) { + hash = ProtocolUtils.readString(buf); + } + Status status = Status.values()[ProtocolUtils.readVarInt(buf)]; + return new ResourcePackResponsePacket(id, hash, status); } - this.status = Status.values()[ProtocolUtils.readVarInt(buf)]; - } - @Override - public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { - ProtocolUtils.writeUuid(buf, id); + @Override + public void encode(ResourcePackResponsePacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { + ProtocolUtils.writeUuid(buf, packet.id); + } + if (protocolVersion.noGreaterThan(ProtocolVersion.MINECRAFT_1_9_4)) { + ProtocolUtils.writeString(buf, packet.hash); + } + ProtocolUtils.writeVarInt(buf, packet.status.ordinal()); } - if (protocolVersion.noGreaterThan(ProtocolVersion.MINECRAFT_1_9_4)) { - ProtocolUtils.writeString(buf, hash); - } - ProtocolUtils.writeVarInt(buf, status.ordinal()); - } - - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } - - @Override - public String toString() { - return "ResourcePackResponsePacket{" + - "id=" + id + - ", hash='" + hash + '\'' + - ", status=" + status + - '}'; } -} \ No newline at end of file +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java index fd9c8ca772..7293a794a4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java @@ -21,6 +21,7 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.registry.DimensionInfo; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.Pair; @@ -28,23 +29,20 @@ import net.kyori.adventure.nbt.CompoundBinaryTag; import org.checkerframework.checker.nullness.qual.Nullable; -public class RespawnPacket implements MinecraftPacket { - - private int dimension; - private long partialHashedSeed; - private short difficulty; - private short gamemode; - private String levelType = ""; - private byte dataToKeep; // 1.16+ - private DimensionInfo dimensionInfo; // 1.16-1.16.1 - private short previousGamemode; // 1.16+ - private CompoundBinaryTag currentDimensionData; // 1.16.2+ - private @Nullable Pair lastDeathPosition; // 1.19+ - private int portalCooldown; // 1.20+ - private int seaLevel; // 1.21.2+ - - public RespawnPacket() { - } +public final class RespawnPacket implements MinecraftPacket { + + private final int dimension; + private final long partialHashedSeed; + private final short difficulty; + private final short gamemode; + private final String levelType; + private final byte dataToKeep; + private final DimensionInfo dimensionInfo; + private final short previousGamemode; + private final CompoundBinaryTag currentDimensionData; + private final @Nullable Pair lastDeathPosition; + private final int portalCooldown; + private final int seaLevel; public RespawnPacket(int dimension, long partialHashedSeed, short difficulty, short gamemode, String levelType, byte dataToKeep, DimensionInfo dimensionInfo, @@ -73,84 +71,60 @@ public static RespawnPacket fromJoinGame(JoinGamePacket joinGame) { joinGame.getPortalCooldown(), joinGame.getSeaLevel()); } - public int getDimension() { - return dimension; + public static RespawnPacket fromJoinGame(JoinGamePacket joinGame, int newDimension) { + return new RespawnPacket(newDimension, joinGame.getPartialHashedSeed(), + joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType(), + (byte) 0, joinGame.getDimensionInfo(), joinGame.getPreviousGamemode(), + joinGame.getCurrentDimensionData(), joinGame.getLastDeathPosition(), + joinGame.getPortalCooldown(), joinGame.getSeaLevel()); } - public void setDimension(int dimension) { - this.dimension = dimension; + public int getDimension() { + return dimension; } public long getPartialHashedSeed() { return partialHashedSeed; } - public void setPartialHashedSeed(long partialHashedSeed) { - this.partialHashedSeed = partialHashedSeed; - } - public short getDifficulty() { return difficulty; } - public void setDifficulty(short difficulty) { - this.difficulty = difficulty; - } - public short getGamemode() { return gamemode; } - public void setGamemode(short gamemode) { - this.gamemode = gamemode; - } - public String getLevelType() { return levelType; } - public void setLevelType(String levelType) { - this.levelType = levelType; - } - public byte getDataToKeep() { return dataToKeep; } - public void setDataToKeep(byte dataToKeep) { - this.dataToKeep = dataToKeep; - } - public short getPreviousGamemode() { return previousGamemode; } - public void setPreviousGamemode(short previousGamemode) { - this.previousGamemode = previousGamemode; - } - public Pair getLastDeathPosition() { return lastDeathPosition; } - public void setLastDeathPosition(Pair lastDeathPosition) { - this.lastDeathPosition = lastDeathPosition; - } - public int getPortalCooldown() { return portalCooldown; } - public void setPortalCooldown(int portalCooldown) { - this.portalCooldown = portalCooldown; - } - public int getSeaLevel() { return seaLevel; } - public void setSeaLevel(int seaLevel) { - this.seaLevel = seaLevel; + public DimensionInfo getDimensionInfo() { + return dimensionInfo; + } + + public CompoundBinaryTag getCurrentDimensionData() { + return currentDimensionData; } @Override @@ -172,123 +146,158 @@ public String toString() { } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - String dimensionKey = ""; - String levelName = null; - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2) - && version.lessThan(ProtocolVersion.MINECRAFT_1_19)) { - this.currentDimensionData = ProtocolUtils.readCompoundTag(buf, version, BinaryTagIO.reader()); - dimensionKey = ProtocolUtils.readString(buf); - } else { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { - dimension = ProtocolUtils.readVarInt(buf); - } else { + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public RespawnPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + String dimensionKey = ""; + String levelName = null; + int dimension = 0; + CompoundBinaryTag currentDimensionData = null; + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2) + && version.lessThan(ProtocolVersion.MINECRAFT_1_19)) { + currentDimensionData = ProtocolUtils.readCompoundTag(buf, version, BinaryTagIO.reader()); dimensionKey = ProtocolUtils.readString(buf); + } else { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { + dimension = ProtocolUtils.readVarInt(buf); + } else { + dimensionKey = ProtocolUtils.readString(buf); + } + levelName = ProtocolUtils.readString(buf); } - levelName = ProtocolUtils.readString(buf); + } else { + dimension = buf.readInt(); } - } else { - this.dimension = buf.readInt(); - } - if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) { - this.difficulty = buf.readUnsignedByte(); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { - this.partialHashedSeed = buf.readLong(); - } - this.gamemode = buf.readByte(); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { - this.previousGamemode = buf.readByte(); - boolean isDebug = buf.readBoolean(); - boolean isFlat = buf.readBoolean(); - this.dimensionInfo = new DimensionInfo(dimensionKey, levelName, isFlat, isDebug, version); - if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { - this.dataToKeep = (byte) (buf.readBoolean() ? 1 : 0); - } else if (version.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) { - this.dataToKeep = buf.readByte(); + + short difficulty = 0; + if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) { + difficulty = buf.readUnsignedByte(); } - } else { - this.levelType = ProtocolUtils.readString(buf, 16); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19) && buf.readBoolean()) { - this.lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong()); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) { - this.portalCooldown = ProtocolUtils.readVarInt(buf); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { - this.seaLevel = ProtocolUtils.readVarInt(buf); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { - this.dataToKeep = buf.readByte(); - } - } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2) - && version.lessThan(ProtocolVersion.MINECRAFT_1_19)) { - ProtocolUtils.writeBinaryTag(buf, version, currentDimensionData); - ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier()); + long partialHashedSeed = 0; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { + partialHashedSeed = buf.readLong(); + } + + short gamemode = buf.readByte(); + + DimensionInfo dimensionInfo = null; + String levelType = ""; + short previousGamemode = 0; + byte dataToKeep = 0; + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { + previousGamemode = buf.readByte(); + boolean isDebug = buf.readBoolean(); + boolean isFlat = buf.readBoolean(); + dimensionInfo = new DimensionInfo(dimensionKey, levelName, isFlat, isDebug, version); + if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { + dataToKeep = (byte) (buf.readBoolean() ? 1 : 0); + } else if (version.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) { + dataToKeep = buf.readByte(); + } } else { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { - ProtocolUtils.writeVarInt(buf, dimension); + levelType = ProtocolUtils.readString(buf, 16); + } + + Pair lastDeathPosition = null; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19) && buf.readBoolean()) { + lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong()); + } + + int portalCooldown = 0; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) { + portalCooldown = ProtocolUtils.readVarInt(buf); + } + + int seaLevel = 0; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + seaLevel = ProtocolUtils.readVarInt(buf); + } + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { + dataToKeep = buf.readByte(); + } + + return new RespawnPacket(dimension, partialHashedSeed, difficulty, gamemode, levelType, + dataToKeep, dimensionInfo, previousGamemode, currentDimensionData, lastDeathPosition, + portalCooldown, seaLevel); + } + + @Override + public void encode(RespawnPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2) + && version.lessThan(ProtocolVersion.MINECRAFT_1_19)) { + ProtocolUtils.writeBinaryTag(buf, version, packet.currentDimensionData); + ProtocolUtils.writeString(buf, packet.dimensionInfo.getRegistryIdentifier()); } else { - ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier()); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { + ProtocolUtils.writeVarInt(buf, packet.dimension); + } else { + ProtocolUtils.writeString(buf, packet.dimensionInfo.getRegistryIdentifier()); + } + ProtocolUtils.writeString(buf, packet.dimensionInfo.getLevelName()); } - ProtocolUtils.writeString(buf, dimensionInfo.getLevelName()); + } else { + buf.writeInt(packet.dimension); } - } else { - buf.writeInt(dimension); - } - if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) { - buf.writeByte(difficulty); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { - buf.writeLong(partialHashedSeed); - } - buf.writeByte(gamemode); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { - buf.writeByte(previousGamemode); - buf.writeBoolean(dimensionInfo.isDebugType()); - buf.writeBoolean(dimensionInfo.isFlat()); - if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { - buf.writeBoolean(dataToKeep != 0); - } else if (version.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) { - buf.writeByte(dataToKeep); + + if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) { + buf.writeByte(packet.difficulty); } - } else { - ProtocolUtils.writeString(buf, levelType); - } - // optional death location - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - if (lastDeathPosition != null) { - buf.writeBoolean(true); - ProtocolUtils.writeString(buf, lastDeathPosition.key()); - buf.writeLong(lastDeathPosition.value()); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) { + buf.writeLong(packet.partialHashedSeed); + } + + buf.writeByte(packet.gamemode); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { + buf.writeByte(packet.previousGamemode); + buf.writeBoolean(packet.dimensionInfo.isDebugType()); + buf.writeBoolean(packet.dimensionInfo.isFlat()); + if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { + buf.writeBoolean(packet.dataToKeep != 0); + } else if (version.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) { + buf.writeByte(packet.dataToKeep); + } } else { - buf.writeBoolean(false); + ProtocolUtils.writeString(buf, packet.levelType); } - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) { - ProtocolUtils.writeVarInt(buf, portalCooldown); - } + // optional death location + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + if (packet.lastDeathPosition != null) { + buf.writeBoolean(true); + ProtocolUtils.writeString(buf, packet.lastDeathPosition.key()); + buf.writeLong(packet.lastDeathPosition.value()); + } else { + buf.writeBoolean(false); + } + } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { - ProtocolUtils.writeVarInt(buf, seaLevel); - } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) { + ProtocolUtils.writeVarInt(buf, packet.portalCooldown); + } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { - buf.writeByte(dataToKeep); - } - } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + ProtocolUtils.writeVarInt(buf, packet.seaLevel); + } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { + buf.writeByte(packet.dataToKeep); + } + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerDataPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerDataPacket.java index 325a3c9dfe..cb058b26e6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerDataPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerDataPacket.java @@ -21,6 +21,7 @@ import com.velocitypowered.api.util.Favicon; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; @@ -29,14 +30,11 @@ import java.nio.charset.StandardCharsets; import java.util.Base64; -public class ServerDataPacket implements MinecraftPacket { +public final class ServerDataPacket implements MinecraftPacket { - private @Nullable ComponentHolder description; - private @Nullable Favicon favicon; - private boolean secureChatEnforced; // Added in 1.19.1 - Removed in 1.20.5 - - public ServerDataPacket() { - } + private final @Nullable ComponentHolder description; + private final @Nullable Favicon favicon; + private final boolean secureChatEnforced; // Added in 1.19.1 - Removed in 1.20.5 public ServerDataPacket(@Nullable ComponentHolder description, @Nullable Favicon favicon, boolean secureChatEnforced) { @@ -45,63 +43,6 @@ public ServerDataPacket(@Nullable ComponentHolder description, @Nullable Favicon this.secureChatEnforced = secureChatEnforced; } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4) || buf.readBoolean()) { - this.description = ComponentHolder.read(buf, protocolVersion); - } - if (buf.readBoolean()) { - String iconBase64; - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4)) { - byte[] iconBytes = ProtocolUtils.readByteArray(buf); - iconBase64 = "data:image/png;base64," + new String(Base64.getEncoder().encode(iconBytes), StandardCharsets.UTF_8); - } else { - iconBase64 = ProtocolUtils.readString(buf); - } - this.favicon = new Favicon(iconBase64); - } - if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { - buf.readBoolean(); - } - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1) - && protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) { - this.secureChatEnforced = buf.readBoolean(); - } - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - boolean hasDescription = this.description != null; - if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19_4)) { - buf.writeBoolean(hasDescription); - } - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4) || hasDescription) { - this.description.write(buf); - } - - boolean hasFavicon = this.favicon != null; - buf.writeBoolean(hasFavicon); - if (hasFavicon) { - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4)) { - String cutIconBase64 = favicon.getBase64Url().substring("data:image/png;base64,".length()); - byte[] iconBytes = Base64.getDecoder().decode(cutIconBase64.getBytes(StandardCharsets.UTF_8)); - ProtocolUtils.writeByteArray(buf, iconBytes); - } else { - ProtocolUtils.writeString(buf, favicon.getBase64Url()); - } - } - - if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { - buf.writeBoolean(false); - } - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1) - && protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) { - buf.writeBoolean(this.secureChatEnforced); - } - } - @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); @@ -119,12 +60,70 @@ public boolean isSecureChatEnforced() { return secureChatEnforced; } - public void setSecureChatEnforced(boolean secureChatEnforced) { - this.secureChatEnforced = secureChatEnforced; - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - @Override - public int encodeSizeHint(Direction direction, ProtocolVersion version) { - return 8 * 1024; + @Override + public ServerDataPacket decode(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + ComponentHolder description = null; + Favicon favicon = null; + boolean secureChatEnforced = false; + + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4) || buf.readBoolean()) { + description = ComponentHolder.read(buf, protocolVersion); + } + if (buf.readBoolean()) { + String iconBase64; + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4)) { + byte[] iconBytes = ProtocolUtils.readByteArray(buf); + iconBase64 = "data:image/png;base64," + new String(Base64.getEncoder().encode(iconBytes), StandardCharsets.UTF_8); + } else { + iconBase64 = ProtocolUtils.readString(buf); + } + favicon = new Favicon(iconBase64); + } + if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { + buf.readBoolean(); + } + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1) + && protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) { + secureChatEnforced = buf.readBoolean(); + } + + return new ServerDataPacket(description, favicon, secureChatEnforced); + } + + @Override + public void encode(ServerDataPacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + boolean hasDescription = packet.description != null; + if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19_4)) { + buf.writeBoolean(hasDescription); + } + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4) || hasDescription) { + packet.description.write(buf); + } + + boolean hasFavicon = packet.favicon != null; + buf.writeBoolean(hasFavicon); + if (hasFavicon) { + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4)) { + String cutIconBase64 = packet.favicon.getBase64Url().substring("data:image/png;base64,".length()); + byte[] iconBytes = Base64.getDecoder().decode(cutIconBase64.getBytes(StandardCharsets.UTF_8)); + ProtocolUtils.writeByteArray(buf, iconBytes); + } else { + ProtocolUtils.writeString(buf, packet.favicon.getBase64Url()); + } + } + + if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { + buf.writeBoolean(false); + } + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1) + && protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) { + buf.writeBoolean(packet.secureChatEnforced); + } + } } -} \ No newline at end of file +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginPacket.java index 65693cd815..881dc49ee5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginPacket.java @@ -17,165 +17,148 @@ package com.velocitypowered.proxy.protocol.packet; -import com.google.common.base.Preconditions; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.crypto.IdentifiedKey; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.util.except.QuietDecoderException; import io.netty.buffer.ByteBuf; import java.util.UUID; import org.checkerframework.checker.nullness.qual.Nullable; -public class ServerLoginPacket implements MinecraftPacket { +public record ServerLoginPacket( + String username, + @Nullable IdentifiedKey playerKey, // Introduced in 1.19.3 + @Nullable UUID holderUuid // Used for key revision 2 +) implements MinecraftPacket { private static final QuietDecoderException EMPTY_USERNAME = new QuietDecoderException( "Empty username!"); - private @Nullable String username; - private @Nullable IdentifiedKey playerKey; // Introduced in 1.19.3 - private @Nullable UUID holderUuid; // Used for key revision 2 - - public ServerLoginPacket() { - } - + /** + * Creates a ServerLoginPacket with a player key. + */ public ServerLoginPacket(String username, @Nullable IdentifiedKey playerKey) { - this.username = Preconditions.checkNotNull(username, "username"); - this.playerKey = playerKey; + this(username, playerKey, null); } + /** + * Creates a ServerLoginPacket with a holder UUID. + */ public ServerLoginPacket(String username, @Nullable UUID holderUuid) { - this.username = Preconditions.checkNotNull(username, "username"); - this.holderUuid = holderUuid; - this.playerKey = null; - } - - public String getUsername() { - if (username == null) { - throw new IllegalStateException("No username found!"); - } - return username; + this(username, null, holderUuid); } - public @Nullable IdentifiedKey getPlayerKey() { - return this.playerKey; - } - - public void setPlayerKey(IdentifiedKey playerKey) { - this.playerKey = playerKey; + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - public @Nullable UUID getHolderUuid() { - return holderUuid; - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - @Override - public String toString() { - return "ServerLogin{" - + "username='" + username + '\'' - + "playerKey='" + playerKey + '\'' - + "holderUUID='" + holderUuid + '\'' - + '}'; - } + @Override + public ServerLoginPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + String username = ProtocolUtils.readString(buf, 16); + if (username.isEmpty()) { + throw EMPTY_USERNAME; + } - @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion version) { - username = ProtocolUtils.readString(buf, 16); - if (username.isEmpty()) { - throw EMPTY_USERNAME; - } + IdentifiedKey playerKey = null; + UUID holderUuid = null; - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_3)) { - playerKey = null; - } else { - if (buf.readBoolean()) { - playerKey = ProtocolUtils.readPlayerKey(version, buf); - } else { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_3)) { playerKey = null; + } else { + if (buf.readBoolean()) { + playerKey = ProtocolUtils.readPlayerKey(version, buf); + } else { + playerKey = null; + } } - } - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { - this.holderUuid = ProtocolUtils.readUuid(buf); - return; - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { - if (buf.readBoolean()) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { holderUuid = ProtocolUtils.readUuid(buf); + return new ServerLoginPacket(username, playerKey, holderUuid); } + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { + if (buf.readBoolean()) { + holderUuid = ProtocolUtils.readUuid(buf); + } + } + } else { + playerKey = null; } - } else { - playerKey = null; - } - } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (username == null) { - throw new IllegalStateException("No username found!"); + return new ServerLoginPacket(username, playerKey, holderUuid); } - ProtocolUtils.writeString(buf, username); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { - if (playerKey != null) { - buf.writeBoolean(true); - ProtocolUtils.writePlayerKey(buf, playerKey); - } else { - buf.writeBoolean(false); - } + @Override + public void encode(ServerLoginPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + if (packet.username() == null) { + throw new IllegalStateException("No username found!"); } + ProtocolUtils.writeString(buf, packet.username()); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { + if (packet.playerKey() != null) { + buf.writeBoolean(true); + ProtocolUtils.writePlayerKey(buf, packet.playerKey()); + } else { + buf.writeBoolean(false); + } + } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { - ProtocolUtils.writeUuid(buf, this.holderUuid); - return; - } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { + ProtocolUtils.writeUuid(buf, packet.holderUuid()); + return; + } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { - if (playerKey != null && playerKey.getSignatureHolder() != null) { - buf.writeBoolean(true); - ProtocolUtils.writeUuid(buf, playerKey.getSignatureHolder()); - } else if (this.holderUuid != null) { - buf.writeBoolean(true); - ProtocolUtils.writeUuid(buf, this.holderUuid); - } else { - buf.writeBoolean(false); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { + if (packet.playerKey() != null && packet.playerKey().getSignatureHolder() != null) { + buf.writeBoolean(true); + ProtocolUtils.writeUuid(buf, packet.playerKey().getSignatureHolder()); + } else if (packet.holderUuid() != null) { + buf.writeBoolean(true); + ProtocolUtils.writeUuid(buf, packet.holderUuid()); + } else { + buf.writeBoolean(false); + } } } } - } - @Override - public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { - // Accommodate the rare (but likely malicious) use of UTF-8 usernames, since it is technically - // legal on the protocol level. - int base = 1 + (16 * 3); - // Adjustments for Key-authentication - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { - // + 1 for the boolean present/ not present - // + 8 for the long expiry - // + 2 len for varint key size - // + 294 for the key - // + 2 len for varint signature size - // + 512 for signature - base += 1 + 8 + 2 + 294 + 2 + 512; - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { - // +1 boolean uuid optional - // + 2 * 8 for the long msb/lsb - base += 1 + 8 + 8; + @Override + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + // Accommodate the rare (but likely malicious) use of UTF-8 usernames, since it is technically + // legal on the protocol level. + int base = 1 + (16 * 3); + // Adjustments for Key-authentication + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) { + // + 1 for the boolean present/ not present + // + 8 for the long expiry + // + 2 len for varint key size + // + 294 for the key + // + 2 len for varint signature size + // + 512 for signature + base += 1 + 8 + 2 + 294 + 2 + 512; + } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { + // +1 boolean uuid optional + // + 2 * 8 for the long msb/lsb + base += 1 + 8 + 8; + } } + return base; } - return base; - } - - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccessPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccessPacket.java index 322cd9b195..db300a5f2d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccessPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccessPacket.java @@ -22,52 +22,40 @@ import com.velocitypowered.api.util.UuidUtils; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.util.VelocityProperties; import io.netty.buffer.ByteBuf; import java.util.List; import java.util.UUID; -import org.checkerframework.checker.nullness.qual.Nullable; -public class ServerLoginSuccessPacket implements MinecraftPacket { +public final class ServerLoginSuccessPacket implements MinecraftPacket { - private @Nullable UUID uuid; - private @Nullable String username; - private @Nullable List properties; + private final UUID uuid; + private final String username; + private final List properties; private static final boolean strictErrorHandling = VelocityProperties .readBoolean("velocity.strictErrorHandling", true); - public UUID getUuid() { - if (uuid == null) { - throw new IllegalStateException("No UUID specified!"); - } - return uuid; + public ServerLoginSuccessPacket(UUID uuid, String username, List properties) { + this.uuid = uuid; + this.username = username; + this.properties = properties; } - public void setUuid(UUID uuid) { - this.uuid = uuid; + public UUID getUuid() { + return uuid; } public String getUsername() { - if (username == null) { - throw new IllegalStateException("No username specified!"); - } return username; } - public void setUsername(String username) { - this.username = username; - } - public List getProperties() { return properties; } - public void setProperties(List properties) { - this.properties = properties; - } - @Override public String toString() { return "ServerLoginSuccess{" @@ -78,66 +66,71 @@ public String toString() { } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - uuid = ProtocolUtils.readUuid(buf); - } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { - uuid = ProtocolUtils.readUuidIntArray(buf); - } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_7_6)) { - uuid = UUID.fromString(ProtocolUtils.readString(buf, 36)); - } else { - uuid = UuidUtils.fromUndashed(ProtocolUtils.readString(buf, 32)); - } - username = ProtocolUtils.readString(buf, 16); - - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - properties = ProtocolUtils.readProperties(buf); - } - if (version == ProtocolVersion.MINECRAFT_1_20_5 || version == ProtocolVersion.MINECRAFT_1_21) { - buf.readBoolean(); - } + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (uuid == null) { - throw new IllegalStateException("No UUID specified!"); - } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - ProtocolUtils.writeUuid(buf, uuid); - } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { - ProtocolUtils.writeUuidIntArray(buf, uuid); - } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_7_6)) { - ProtocolUtils.writeString(buf, uuid.toString()); - } else { - ProtocolUtils.writeString(buf, UuidUtils.toUndashed(uuid)); - } - if (username == null) { - throw new IllegalStateException("No username specified!"); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public ServerLoginSuccessPacket decode(ByteBuf buf, Direction direction, + ProtocolVersion version) { + UUID uuid; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + uuid = ProtocolUtils.readUuid(buf); + } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { + uuid = ProtocolUtils.readUuidIntArray(buf); + } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_7_6)) { + uuid = UUID.fromString(ProtocolUtils.readString(buf, 36)); + } else { + uuid = UuidUtils.fromUndashed(ProtocolUtils.readString(buf, 32)); + } + String username = ProtocolUtils.readString(buf, 16); + + List properties = null; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + properties = ProtocolUtils.readProperties(buf); + } + if (version == ProtocolVersion.MINECRAFT_1_20_5 || version == ProtocolVersion.MINECRAFT_1_21) { + buf.readBoolean(); + } + + return new ServerLoginSuccessPacket(uuid, username, properties); } - ProtocolUtils.writeString(buf, username); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { - if (properties == null) { - ProtocolUtils.writeVarInt(buf, 0); + @Override + public void encode(ServerLoginSuccessPacket packet, ByteBuf buf, Direction direction, + ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + ProtocolUtils.writeUuid(buf, packet.uuid); + } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { + ProtocolUtils.writeUuidIntArray(buf, packet.uuid); + } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_7_6)) { + ProtocolUtils.writeString(buf, packet.uuid.toString()); } else { - ProtocolUtils.writeProperties(buf, properties); + ProtocolUtils.writeString(buf, UuidUtils.toUndashed(packet.uuid)); + } + ProtocolUtils.writeString(buf, packet.username); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { + if (packet.properties == null) { + ProtocolUtils.writeVarInt(buf, 0); + } else { + ProtocolUtils.writeProperties(buf, packet.properties); + } + } + if (version == ProtocolVersion.MINECRAFT_1_20_5 || version == ProtocolVersion.MINECRAFT_1_21) { + buf.writeBoolean(strictErrorHandling); } } - if (version == ProtocolVersion.MINECRAFT_1_20_5 || version == ProtocolVersion.MINECRAFT_1_21) { - buf.writeBoolean(strictErrorHandling); - } - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } + @Override + public int encodeSizeHint(ServerLoginSuccessPacket packet, Direction direction, ProtocolVersion version) { + // We could compute an exact size, but 4KiB ought to be enough to encode all reasonable + // sizes of this packet. + return 4 * 1024; + } - @Override - public int encodeSizeHint(Direction direction, ProtocolVersion version) { - // We could compute an exact size, but 4KiB ought to be enough to encode all reasonable - // sizes of this packet. - return 4 * 1024; } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCookieResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCookieResponsePacket.java index bee12b8024..88fe984bc4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCookieResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCookieResponsePacket.java @@ -20,53 +20,44 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.Nullable; -public class ServerboundCookieResponsePacket implements MinecraftPacket { - - private Key key; - private byte @Nullable [] payload; - - public Key getKey() { - return key; - } - - public byte @Nullable [] getPayload() { - return payload; - } - - public ServerboundCookieResponsePacket() { - } - - public ServerboundCookieResponsePacket(final Key key, final byte @Nullable [] payload) { - this.key = key; - this.payload = payload; - } +public record ServerboundCookieResponsePacket(Key key, + byte @Nullable [] payload) implements MinecraftPacket { @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - this.key = ProtocolUtils.readKey(buf); - if (buf.readBoolean()) { - this.payload = ProtocolUtils.readByteArray(buf, 5120); - } + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeKey(buf, key); - final boolean hasPayload = payload != null && payload.length > 0; - buf.writeBoolean(hasPayload); - if (hasPayload) { - ProtocolUtils.writeByteArray(buf, payload); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public ServerboundCookieResponsePacket decode(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + Key key = ProtocolUtils.readKey(buf); + byte[] payload = null; + if (buf.readBoolean()) { + payload = ProtocolUtils.readByteArray(buf, 5120); + } + return new ServerboundCookieResponsePacket(key, payload); } - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + @Override + public void encode(ServerboundCookieResponsePacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + ProtocolUtils.writeKey(buf, packet.key); + final boolean hasPayload = packet.payload != null && packet.payload.length > 0; + buf.writeBoolean(hasPayload); + if (hasPayload) { + ProtocolUtils.writeByteArray(buf, packet.payload); + } + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java index 6b846c2343..a322367484 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java @@ -20,34 +20,82 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; -import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; +import io.netty.buffer.DefaultByteBufHolder; -public class ServerboundCustomClickActionPacket extends DeferredByteBufHolder implements MinecraftPacket { +public class ServerboundCustomClickActionPacket extends DefaultByteBufHolder + implements MinecraftPacket { - public ServerboundCustomClickActionPacket() { - super(null); + public ServerboundCustomClickActionPacket(ByteBuf backing) { + super(backing); } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - replace(buf.readRetainedSlice(buf.readableBytes())); + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - buf.writeBytes(content()); + public ServerboundCustomClickActionPacket copy() { + return (ServerboundCustomClickActionPacket) super.copy(); } @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + public ServerboundCustomClickActionPacket duplicate() { + return (ServerboundCustomClickActionPacket) super.duplicate(); + } + + @Override + public ServerboundCustomClickActionPacket retainedDuplicate() { + return (ServerboundCustomClickActionPacket) super.retainedDuplicate(); + } + + @Override + public ServerboundCustomClickActionPacket replace(ByteBuf content) { + return (ServerboundCustomClickActionPacket) super.replace(content); + } + + @Override + public ServerboundCustomClickActionPacket retain() { + return (ServerboundCustomClickActionPacket) super.retain(); + } + + @Override + public ServerboundCustomClickActionPacket retain(int increment) { + return (ServerboundCustomClickActionPacket) super.retain(increment); + } + + @Override + public ServerboundCustomClickActionPacket touch() { + return (ServerboundCustomClickActionPacket) super.touch(); } @Override - public int encodeSizeHint(Direction direction, ProtocolVersion version) { - return content().readableBytes(); + public ServerboundCustomClickActionPacket touch(Object hint) { + return (ServerboundCustomClickActionPacket) super.touch(hint); + } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public ServerboundCustomClickActionPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return new ServerboundCustomClickActionPacket(buf.readRetainedSlice(buf.readableBytes())); + } + + @Override + public void encode(ServerboundCustomClickActionPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + buf.writeBytes(packet.content()); + } + + @Override + public int encodeSizeHint(ServerboundCustomClickActionPacket packet, Direction direction, ProtocolVersion protocolVersion) { + return packet.content().readableBytes(); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/SetCompressionPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/SetCompressionPacket.java index 6710bf85d8..3c057251ca 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/SetCompressionPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/SetCompressionPacket.java @@ -20,28 +20,16 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class SetCompressionPacket implements MinecraftPacket { - - private int threshold; - - public SetCompressionPacket() { - } - - public SetCompressionPacket(int threshold) { - this.threshold = threshold; - } +public record SetCompressionPacket(int threshold) implements MinecraftPacket { public int getThreshold() { return threshold; } - public void setThreshold(int threshold) { - this.threshold = threshold; - } - @Override public String toString() { return "SetCompression{" @@ -50,17 +38,23 @@ public String toString() { } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - this.threshold = ProtocolUtils.readVarInt(buf); + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - ProtocolUtils.writeVarInt(buf, threshold); - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + @Override + public SetCompressionPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return new SetCompressionPacket(ProtocolUtils.readVarInt(buf)); + } + + @Override + public void encode(SetCompressionPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, packet.threshold); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusPingPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusPingPacket.java index 302367044a..9aa34961b2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusPingPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusPingPacket.java @@ -20,36 +20,43 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; -public class StatusPingPacket implements MinecraftPacket { - - private long randomId; - - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - randomId = buf.readLong(); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - buf.writeLong(randomId); - } +public record StatusPingPacket(long randomId) implements MinecraftPacket { @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } - @Override - public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { - return 8; - } - - @Override - public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { - return 8; + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public StatusPingPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return new StatusPingPacket(buf.readLong()); + } + + @Override + public void encode(StatusPingPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + buf.writeLong(packet.randomId); + } + + @Override + public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + return 8; + } + + @Override + public int decodeExpectedMinLength(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + return 8; + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequestPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequestPacket.java index 870d99093f..6dfef9376e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequestPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequestPacket.java @@ -20,11 +20,11 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; -import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; -public class StatusRequestPacket implements MinecraftPacket { +public final class StatusRequestPacket implements MinecraftPacket { public static final StatusRequestPacket INSTANCE = new StatusRequestPacket(); @@ -32,16 +32,6 @@ private StatusRequestPacket() { } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - // There is no additional data to decode. - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - // There is no data to decode. - } - @Override public String toString() { return "StatusRequest"; @@ -52,8 +42,24 @@ public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } - @Override - public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { - return 0; + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public StatusRequestPacket decode(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + return StatusRequestPacket.INSTANCE; + } + + @Override + public void encode(StatusRequestPacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + } + + @Override + public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + return 0; + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponsePacket.java index 20fada4bc7..bf66f94d0d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponsePacket.java @@ -20,29 +20,30 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; import org.checkerframework.checker.nullness.qual.Nullable; -public class StatusResponsePacket implements MinecraftPacket { +public final class StatusResponsePacket implements MinecraftPacket { - private @Nullable CharSequence status; - - public StatusResponsePacket() { - } + private final @Nullable CharSequence status; public StatusResponsePacket(CharSequence status) { this.status = status; } - public String getStatus() { + public String status() { if (status == null) { throw new IllegalStateException("Status is not specified"); } return status.toString(); } + public String getStatus() { + return status(); + } + @Override public String toString() { return "StatusResponse{" @@ -51,25 +52,32 @@ public String toString() { } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - status = ProtocolUtils.readString(buf, Short.MAX_VALUE); + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (status == null) { - throw new IllegalStateException("Status is not specified"); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public StatusResponsePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + CharSequence status = ProtocolUtils.readString(buf, Short.MAX_VALUE); + return new StatusResponsePacket(status); } - ProtocolUtils.writeString(buf, status); - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } + @Override + public void encode(StatusResponsePacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + if (packet.status == null) { + throw new IllegalStateException("Status is not specified"); + } + ProtocolUtils.writeString(buf, packet.status); + } - @Override - public int encodeSizeHint(Direction direction, ProtocolVersion version) { - return ProtocolUtils.stringSizeHint(this.status); + @Override + public int encodeSizeHint(StatusResponsePacket packet, ProtocolUtils.Direction direction, ProtocolVersion version) { + return ProtocolUtils.stringSizeHint(packet.status); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequestPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequestPacket.java index dda4695bbc..86de169a9c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequestPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequestPacket.java @@ -21,122 +21,127 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; -import com.google.common.base.MoreObjects; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import org.checkerframework.checker.nullness.qual.Nullable; -public class TabCompleteRequestPacket implements MinecraftPacket { +public final class TabCompleteRequestPacket implements MinecraftPacket { private static final int VANILLA_MAX_TAB_COMPLETE_LEN = 2048; - private @Nullable String command; - private int transactionId; - private boolean assumeCommand; - private boolean hasPosition; - private long position; + private final @Nullable String command; + private final int transactionId; + private final boolean assumeCommand; + private final boolean hasPosition; + private final long position; - public String getCommand() { + public TabCompleteRequestPacket() { + this(null, 0, false, false, 0); + } + + public TabCompleteRequestPacket(@Nullable String command, int transactionId, + boolean assumeCommand, boolean hasPosition, long position) { + this.command = command; + this.transactionId = transactionId; + this.assumeCommand = assumeCommand; + this.hasPosition = hasPosition; + this.position = position; + } + + public String command() { if (command == null) { throw new IllegalStateException("Command is not specified"); } return command; } - public void setCommand(String command) { - this.command = command; + public String getCommand() { + return command(); } - public boolean isAssumeCommand() { + public boolean assumeCommand() { return assumeCommand; } - public void setAssumeCommand(boolean assumeCommand) { - this.assumeCommand = assumeCommand; + public boolean isAssumeCommand() { + return assumeCommand(); } public boolean hasPosition() { return hasPosition; } - public void setHasPosition(boolean hasPosition) { - this.hasPosition = hasPosition; - } - - public long getPosition() { + public long position() { return position; } - public void setPosition(long position) { - this.position = position; - } - - public int getTransactionId() { + public int transactionId() { return transactionId; } - public void setTransactionId(int transactionId) { - this.transactionId = transactionId; - } - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("command", command) - .add("transactionId", transactionId) - .add("assumeCommand", assumeCommand) - .add("hasPosition", hasPosition) - .add("position", position) - .toString(); + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(MINECRAFT_1_13)) { - this.transactionId = ProtocolUtils.readVarInt(buf); - this.command = ProtocolUtils.readString(buf, VANILLA_MAX_TAB_COMPLETE_LEN); - } else { - this.command = ProtocolUtils.readString(buf, VANILLA_MAX_TAB_COMPLETE_LEN); - if (version.noLessThan(MINECRAFT_1_9)) { - this.assumeCommand = buf.readBoolean(); - } - if (version.noLessThan(MINECRAFT_1_8)) { - this.hasPosition = buf.readBoolean(); - if (hasPosition) { - this.position = buf.readLong(); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public TabCompleteRequestPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + String command; + int transactionId = 0; + boolean assumeCommand = false; + boolean hasPosition = false; + long position = 0; + + if (version.noLessThan(MINECRAFT_1_13)) { + transactionId = ProtocolUtils.readVarInt(buf); + command = ProtocolUtils.readString(buf, VANILLA_MAX_TAB_COMPLETE_LEN); + } else { + command = ProtocolUtils.readString(buf, VANILLA_MAX_TAB_COMPLETE_LEN); + if (version.noLessThan(MINECRAFT_1_9)) { + assumeCommand = buf.readBoolean(); + } + if (version.noLessThan(MINECRAFT_1_8)) { + hasPosition = buf.readBoolean(); + if (hasPosition) { + position = buf.readLong(); + } } } - } - } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (command == null) { - throw new IllegalStateException("Command is not specified"); + return new TabCompleteRequestPacket(command, transactionId, assumeCommand, hasPosition, + position); } - if (version.noLessThan(MINECRAFT_1_13)) { - ProtocolUtils.writeVarInt(buf, transactionId); - ProtocolUtils.writeString(buf, command); - } else { - ProtocolUtils.writeString(buf, command); - if (version.noLessThan(MINECRAFT_1_9)) { - buf.writeBoolean(assumeCommand); + @Override + public void encode(TabCompleteRequestPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion version) { + if (packet.command == null) { + throw new IllegalStateException("Command is not specified"); } - if (version.noLessThan(MINECRAFT_1_8)) { - buf.writeBoolean(hasPosition); - if (hasPosition) { - buf.writeLong(position); + + if (version.noLessThan(MINECRAFT_1_13)) { + ProtocolUtils.writeVarInt(buf, packet.transactionId); + ProtocolUtils.writeString(buf, packet.command); + } else { + ProtocolUtils.writeString(buf, packet.command); + if (version.noLessThan(MINECRAFT_1_9)) { + buf.writeBoolean(packet.assumeCommand); + } + if (version.noLessThan(MINECRAFT_1_8)) { + buf.writeBoolean(packet.hasPosition); + if (packet.hasPosition) { + buf.writeLong(packet.position); + } } } } } - - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponsePacket.java index a22fff0bdb..ee311e7996 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponsePacket.java @@ -23,6 +23,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import io.netty.buffer.ByteBuf; @@ -30,39 +31,54 @@ import java.util.List; import org.checkerframework.checker.nullness.qual.Nullable; -public class TabCompleteResponsePacket implements MinecraftPacket { +public final class TabCompleteResponsePacket implements MinecraftPacket { - private int transactionId; - private int start; - private int length; - private final List offers = new ArrayList<>(); + private final int transactionId; + private final int start; + private final int length; + private final List offers; - public int getTransactionId() { - return transactionId; + public TabCompleteResponsePacket(int transactionId, int start, int length, List offers) { + this.transactionId = transactionId; + this.start = start; + this.length = length; + this.offers = List.copyOf(offers); } - public void setTransactionId(int transactionId) { - this.transactionId = transactionId; + public int transactionId() { + return transactionId; } - public int getStart() { + public int start() { return start; } - public void setStart(int start) { - this.start = start; + public int length() { + return length; } - public int getLength() { - return length; + public List offers() { + return offers; } - public void setLength(int length) { - this.length = length; + public int getTransactionId() { + return transactionId(); + } + + public int getStart() { + return start(); + } + + public int getLength() { + return length(); } public List getOffers() { - return offers; + return offers(); + } + + public TabCompleteResponsePacket withOffers(List offers) { + return new TabCompleteResponsePacket(transactionId, start, length, offers); } @Override @@ -76,50 +92,60 @@ public String toString() { } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(MINECRAFT_1_13)) { - this.transactionId = ProtocolUtils.readVarInt(buf); - this.start = ProtocolUtils.readVarInt(buf); - this.length = ProtocolUtils.readVarInt(buf); - int offersAvailable = ProtocolUtils.readVarInt(buf); - for (int i = 0; i < offersAvailable; i++) { - String offer = ProtocolUtils.readString(buf); - ComponentHolder tooltip = buf.readBoolean() ? ComponentHolder.read(buf, version) : null; - offers.add(new Offer(offer, tooltip)); - } - } else { - int offersAvailable = ProtocolUtils.readVarInt(buf); - for (int i = 0; i < offersAvailable; i++) { - offers.add(new Offer(ProtocolUtils.readString(buf), null)); - } - } + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.noLessThan(MINECRAFT_1_13)) { - ProtocolUtils.writeVarInt(buf, this.transactionId); - ProtocolUtils.writeVarInt(buf, this.start); - ProtocolUtils.writeVarInt(buf, this.length); - ProtocolUtils.writeVarInt(buf, offers.size()); - for (Offer offer : offers) { - ProtocolUtils.writeString(buf, offer.text); - buf.writeBoolean(offer.tooltip != null); - if (offer.tooltip != null) { - offer.tooltip.write(buf); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public TabCompleteResponsePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + if (version.noLessThan(MINECRAFT_1_13)) { + int transactionId = ProtocolUtils.readVarInt(buf); + int start = ProtocolUtils.readVarInt(buf); + int length = ProtocolUtils.readVarInt(buf); + int offersAvailable = ProtocolUtils.readVarInt(buf); + List offers = new ArrayList<>(offersAvailable); + for (int i = 0; i < offersAvailable; i++) { + String offer = ProtocolUtils.readString(buf); + ComponentHolder tooltip = buf.readBoolean() ? ComponentHolder.read(buf, version) : null; + offers.add(new Offer(offer, tooltip)); } - } - } else { - ProtocolUtils.writeVarInt(buf, offers.size()); - for (Offer offer : offers) { - ProtocolUtils.writeString(buf, offer.text); + return new TabCompleteResponsePacket(transactionId, start, length, offers); + } else { + int offersAvailable = ProtocolUtils.readVarInt(buf); + List offers = new ArrayList<>(offersAvailable); + for (int i = 0; i < offersAvailable; i++) { + offers.add(new Offer(ProtocolUtils.readString(buf), null)); + } + return new TabCompleteResponsePacket(0, 0, 0, offers); } } - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + @Override + public void encode(TabCompleteResponsePacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion version) { + if (version.noLessThan(MINECRAFT_1_13)) { + ProtocolUtils.writeVarInt(buf, packet.transactionId); + ProtocolUtils.writeVarInt(buf, packet.start); + ProtocolUtils.writeVarInt(buf, packet.length); + ProtocolUtils.writeVarInt(buf, packet.offers.size()); + for (Offer offer : packet.offers) { + ProtocolUtils.writeString(buf, offer.text); + buf.writeBoolean(offer.tooltip != null); + if (offer.tooltip != null) { + offer.tooltip.write(buf); + } + } + } else { + ProtocolUtils.writeVarInt(buf, packet.offers.size()); + for (Offer offer : packet.offers) { + ProtocolUtils.writeString(buf, offer.text); + } + } + } } public static class Offer implements Comparable { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TransferPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TransferPacket.java index b5a74e49f2..d9f83c7c58 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TransferPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TransferPacket.java @@ -20,22 +20,13 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import java.net.InetSocketAddress; import org.jetbrains.annotations.Nullable; -public class TransferPacket implements MinecraftPacket { - private String host; - private int port; - - public TransferPacket() { - } - - public TransferPacket(final String host, final int port) { - this.host = host; - this.port = port; - } +public record TransferPacket(String host, int port) implements MinecraftPacket { @Nullable public InetSocketAddress address() { @@ -45,20 +36,33 @@ public InetSocketAddress address() { return new InetSocketAddress(host, port); } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - this.host = ProtocolUtils.readString(buf); - this.port = ProtocolUtils.readVarInt(buf); + public String getHost() { + return host; } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeString(buf, host); - ProtocolUtils.writeVarInt(buf, port); + public int getPort() { + return port; } @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public TransferPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return new TransferPacket(ProtocolUtils.readString(buf), ProtocolUtils.readVarInt(buf)); + } + + @Override + public void encode(TransferPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + ProtocolUtils.writeString(buf, packet.host); + ProtocolUtils.writeVarInt(buf, packet.port); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java index 9ef40ef092..c44010aed1 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java @@ -21,6 +21,7 @@ import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.RemoteChatSession; @@ -28,29 +29,18 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; -import java.util.Collection; import java.util.EnumSet; import java.util.List; import java.util.UUID; import org.jetbrains.annotations.Nullable; -public class UpsertPlayerInfoPacket implements MinecraftPacket { +public final class UpsertPlayerInfoPacket implements MinecraftPacket { private static final Action[] ALL_ACTIONS = Action.class.getEnumConstants(); private final EnumSet actions; private final List entries; - public UpsertPlayerInfoPacket() { - this.actions = EnumSet.noneOf(Action.class); - this.entries = new ArrayList<>(); - } - - public UpsertPlayerInfoPacket(Action action) { - this.actions = EnumSet.of(action); - this.entries = new ArrayList<>(); - } - public UpsertPlayerInfoPacket(EnumSet actions, List entries) { this.actions = actions; this.entries = entries; @@ -68,71 +58,62 @@ public boolean containsAction(Action action) { return this.actions.contains(action); } - public void addAction(Action action) { - this.actions.add(action); - } - - public void addAllActions(Collection actions) { - this.actions.addAll(actions); - } - - public void addEntry(Entry entry) { - this.entries.add(entry); + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - public void addAllEntries(Collection entries) { - this.entries.addAll(entries); - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - byte[] bytes = new byte[-Math.floorDiv(-ALL_ACTIONS.length, 8)]; - buf.readBytes(bytes); - BitSet actionSet = BitSet.valueOf(bytes); - - for (int idx = 0; idx < ALL_ACTIONS.length; idx++) { - if (actionSet.get(idx)) { - addAction(ALL_ACTIONS[idx]); + @Override + public UpsertPlayerInfoPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + byte[] bytes = new byte[-Math.floorDiv(-ALL_ACTIONS.length, 8)]; + buf.readBytes(bytes); + BitSet actionSet = BitSet.valueOf(bytes); + + EnumSet actions = EnumSet.noneOf(Action.class); + for (int idx = 0; idx < ALL_ACTIONS.length; idx++) { + if (actionSet.get(idx)) { + actions.add(ALL_ACTIONS[idx]); + } } - } - int length = ProtocolUtils.readVarInt(buf); - for (int idx = 0; idx < length; idx++) { - Entry entry = new Entry(ProtocolUtils.readUuid(buf)); - for (Action action : this.actions) { - action.read.read(protocolVersion, buf, entry); + int length = ProtocolUtils.readVarInt(buf); + List entries = new ArrayList<>(length); + for (int idx = 0; idx < length; idx++) { + Entry entry = new Entry(ProtocolUtils.readUuid(buf)); + for (Action action : actions) { + action.read.read(protocolVersion, buf, entry); + } + entries.add(entry); } - addEntry(entry); + return new UpsertPlayerInfoPacket(actions, entries); } - } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - BitSet set = new BitSet(ALL_ACTIONS.length); - for (int idx = 0; idx < ALL_ACTIONS.length; idx++) { - set.set(idx, this.actions.contains(ALL_ACTIONS[idx])); - } + @Override + public void encode(UpsertPlayerInfoPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + BitSet set = new BitSet(ALL_ACTIONS.length); + for (int idx = 0; idx < ALL_ACTIONS.length; idx++) { + set.set(idx, packet.actions.contains(ALL_ACTIONS[idx])); + } - byte[] bytes = set.toByteArray(); - buf.writeBytes(Arrays.copyOf(bytes, -Math.floorDiv(-ALL_ACTIONS.length, 8))); + byte[] bytes = set.toByteArray(); + buf.writeBytes(Arrays.copyOf(bytes, -Math.floorDiv(-ALL_ACTIONS.length, 8))); - ProtocolUtils.writeVarInt(buf, this.entries.size()); - for (Entry entry : this.entries) { - ProtocolUtils.writeUuid(buf, entry.profileId); + ProtocolUtils.writeVarInt(buf, packet.entries.size()); + for (Entry entry : packet.entries) { + ProtocolUtils.writeUuid(buf, entry.profileId); - for (Action action : this.actions) { - action.write.write(protocolVersion, buf, entry); + for (Action action : packet.actions) { + action.write.write(protocolVersion, buf, entry); + } } } } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } - public enum Action { ADD_PLAYER((ignored, buf, info) -> { // read info.profile = new GameProfile( @@ -315,4 +296,4 @@ public String toString() { '}'; } } -} \ No newline at end of file +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ChatAcknowledgementPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ChatAcknowledgementPacket.java index b0718090e9..7c458bbcd5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ChatAcknowledgementPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ChatAcknowledgementPacket.java @@ -20,42 +20,37 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class ChatAcknowledgementPacket implements MinecraftPacket { - int offset; +public record ChatAcknowledgementPacket(int offset) implements MinecraftPacket { - public ChatAcknowledgementPacket(int offset) { - this.offset = offset; - } + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } - public ChatAcknowledgementPacket() { - } + @Override + public String toString() { + return "ChatAcknowledgement{" + + "offset=" + offset + + '}'; + } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - offset = ProtocolUtils.readVarInt(buf); - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeVarInt(buf, offset); + public ChatAcknowledgementPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return new ChatAcknowledgementPacket(ProtocolUtils.readVarInt(buf)); } @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } - - @Override - public String toString() { - return "ChatAcknowledgement{" + - "offset=" + offset + - '}'; - } - - public int offset() { - return offset; + public void encode(ChatAcknowledgementPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, packet.offset); } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/PlayerChatCompletionPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/PlayerChatCompletionPacket.java index d07b798a52..4d6ea16274 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/PlayerChatCompletionPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/PlayerChatCompletionPacket.java @@ -20,15 +20,17 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class PlayerChatCompletionPacket implements MinecraftPacket { +public final class PlayerChatCompletionPacket implements MinecraftPacket { - private String[] completions; - private Action action; + private final String[] completions; + private final Action action; public PlayerChatCompletionPacket() { + this(new String[0], Action.ADD); } public PlayerChatCompletionPacket(String[] completions, Action action) { @@ -36,36 +38,14 @@ public PlayerChatCompletionPacket(String[] completions, Action action) { this.action = action; } - public String[] getCompletions() { + public String[] completions() { return completions; } - public Action getAction() { + public Action action() { return action; } - public void setCompletions(String[] completions) { - this.completions = completions; - } - - public void setAction(Action action) { - this.action = action; - } - - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - action = Action.values()[ProtocolUtils.readVarInt(buf)]; - completions = ProtocolUtils.readStringArray(buf); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - ProtocolUtils.writeVarInt(buf, action.ordinal()); - ProtocolUtils.writeStringArray(buf, completions); - } - @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); @@ -76,4 +56,23 @@ public enum Action { REMOVE, SET } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public PlayerChatCompletionPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + Action action = Action.values()[ProtocolUtils.readVarInt(buf)]; + String[] completions = ProtocolUtils.readStringArray(buf); + return new PlayerChatCompletionPacket(completions, action); + } + + @Override + public void encode(PlayerChatCompletionPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, packet.action.ordinal()); + ProtocolUtils.writeStringArray(buf, packet.completions); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/SystemChatPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/SystemChatPacket.java index b36014d5b4..bf66ffca7a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/SystemChatPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/SystemChatPacket.java @@ -20,12 +20,17 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class SystemChatPacket implements MinecraftPacket { +public final class SystemChatPacket implements MinecraftPacket { + + private final ComponentHolder component; + private final ChatType type; public SystemChatPacket() { + this(null, ChatType.SYSTEM); } public SystemChatPacket(ComponentHolder component, ChatType type) { @@ -33,48 +38,61 @@ public SystemChatPacket(ComponentHolder component, ChatType type) { this.type = type; } - private ComponentHolder component; - private ChatType type; - - public ChatType getType() { + public ChatType type() { return type; } - public ComponentHolder getComponent() { + public ComponentHolder component() { return component; } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - component = ComponentHolder.read(buf, version); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)){ - type = buf.readBoolean() ? ChatType.GAME_INFO : ChatType.SYSTEM; - } else { - type = ChatType.values()[ProtocolUtils.readVarInt(buf)]; - } + public ComponentHolder getComponent() { + return component(); } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - component.write(buf); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { - switch (type) { - case SYSTEM: - buf.writeBoolean(false); - break; - case GAME_INFO: - buf.writeBoolean(true); - break; - default: - throw new IllegalArgumentException("Invalid chat type"); - } - } else { - ProtocolUtils.writeVarInt(buf, type.getId()); - } + public ChatType getType() { + return type(); } @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public SystemChatPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + ComponentHolder component = ComponentHolder.read(buf, version); + ChatType type; + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { + type = buf.readBoolean() ? ChatType.GAME_INFO : ChatType.SYSTEM; + } else { + type = ChatType.values()[ProtocolUtils.readVarInt(buf)]; + } + return new SystemChatPacket(component, type); + } + + @Override + public void encode(SystemChatPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + packet.component.write(buf); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { + switch (packet.type) { + case SYSTEM: + buf.writeBoolean(false); + break; + case GAME_INFO: + buf.writeBoolean(true); + break; + default: + throw new IllegalArgumentException("Invalid chat type"); + } + } else { + ProtocolUtils.writeVarInt(buf, packet.type.getId()); + } + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedChatBuilder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedChatBuilder.java index d654c619c7..c60a18ac4e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedChatBuilder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedChatBuilder.java @@ -17,7 +17,6 @@ package com.velocitypowered.proxy.protocol.packet.chat.keyed; -import com.google.common.collect.ImmutableList; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.packet.chat.ChatType; @@ -42,12 +41,13 @@ public MinecraftPacket toClient() { @Override public MinecraftPacket toServer() { if (message.startsWith("/")) { - return new KeyedPlayerCommandPacket(message.substring(1), ImmutableList.of(), timestamp); + return new KeyedPlayerCommandPacket(false, message.substring(1), timestamp, 0L, + false, new com.velocitypowered.proxy.crypto.SignaturePair[0], null, + java.util.Collections.emptyMap()); } else { // This will produce an error on the server, but needs to be here. - KeyedPlayerChatPacket v1Chat = new KeyedPlayerChatPacket(message); - v1Chat.setExpiry(this.timestamp); - return v1Chat; + return new KeyedPlayerChatPacket(message, false, false, timestamp, null, null, + new com.velocitypowered.proxy.crypto.SignaturePair[0], null); } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedPlayerChatPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedPlayerChatPacket.java index 74fa88f5a5..68d044fd57 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedPlayerChatPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedPlayerChatPacket.java @@ -23,41 +23,43 @@ import com.velocitypowered.proxy.crypto.EncryptionUtils; import com.velocitypowered.proxy.crypto.SignaturePair; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.util.except.QuietDecoderException; import io.netty.buffer.ByteBuf; import java.time.Instant; import org.checkerframework.checker.nullness.qual.Nullable; -public class KeyedPlayerChatPacket implements MinecraftPacket { - - private String message; - private boolean signedPreview; - private boolean unsigned = false; - private @Nullable Instant expiry; - private @Nullable byte[] signature; - private @Nullable byte[] salt; - private SignaturePair[] previousMessages = new SignaturePair[0]; - private @Nullable SignaturePair lastMessage; +public final class KeyedPlayerChatPacket implements MinecraftPacket { public static final int MAXIMUM_PREVIOUS_MESSAGE_COUNT = 5; public static final QuietDecoderException INVALID_PREVIOUS_MESSAGES = new QuietDecoderException("Invalid previous messages"); - public KeyedPlayerChatPacket() { - } - - public KeyedPlayerChatPacket(String message) { + private final String message; + private final boolean signedPreview; + private final boolean unsigned; + private final @Nullable Instant expiry; + private final @Nullable byte[] signature; + private final @Nullable byte[] salt; + private final SignaturePair[] previousMessages; + private final @Nullable SignaturePair lastMessage; + + public KeyedPlayerChatPacket(String message, boolean signedPreview, boolean unsigned, + @Nullable Instant expiry, @Nullable byte[] signature, @Nullable byte[] salt, + SignaturePair[] previousMessages, @Nullable SignaturePair lastMessage) { this.message = message; - this.unsigned = true; - } - - public void setExpiry(@Nullable Instant expiry) { + this.signedPreview = signedPreview; + this.unsigned = unsigned; this.expiry = expiry; + this.signature = signature; + this.salt = salt; + this.previousMessages = previousMessages; + this.lastMessage = lastMessage; } - public Instant getExpiry() { + public @Nullable Instant getExpiry() { return expiry; } @@ -74,81 +76,97 @@ public boolean isSignedPreview() { } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - message = ProtocolUtils.readString(buf, 256); - - long expiresAt = buf.readLong(); - long saltLong = buf.readLong(); - byte[] signatureBytes = ProtocolUtils.readByteArray(buf); - - if (saltLong != 0L && signatureBytes.length > 0) { - salt = Longs.toByteArray(saltLong); - signature = signatureBytes; - expiry = Instant.ofEpochMilli(expiresAt); - } else if ((protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1) - || saltLong == 0L) && signatureBytes.length == 0) { - unsigned = true; - } else { - throw EncryptionUtils.INVALID_SIGNATURE; - } - - signedPreview = buf.readBoolean(); - if (signedPreview && unsigned) { - throw EncryptionUtils.PREVIEW_SIGNATURE_MISSING; - } + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { - int size = ProtocolUtils.readVarInt(buf); - if (size < 0 || size > MAXIMUM_PREVIOUS_MESSAGE_COUNT) { - throw INVALID_PREVIOUS_MESSAGES; + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public KeyedPlayerChatPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + String message = ProtocolUtils.readString(buf, 256); + + long expiresAt = buf.readLong(); + long saltLong = buf.readLong(); + byte[] signatureBytes = ProtocolUtils.readByteArray(buf); + + byte[] salt = null; + byte[] signature = null; + Instant expiry = null; + boolean unsigned; + + if (saltLong != 0L && signatureBytes.length > 0) { + salt = Longs.toByteArray(saltLong); + signature = signatureBytes; + expiry = Instant.ofEpochMilli(expiresAt); + unsigned = false; + } else if ((protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1) + || saltLong == 0L) && signatureBytes.length == 0) { + unsigned = true; + } else { + throw EncryptionUtils.INVALID_SIGNATURE; } - SignaturePair[] lastSignatures = new SignaturePair[size]; - for (int i = 0; i < size; i++) { - lastSignatures[i] = new SignaturePair(ProtocolUtils.readUuid(buf), - ProtocolUtils.readByteArray(buf)); + boolean signedPreview = buf.readBoolean(); + if (signedPreview && unsigned) { + throw EncryptionUtils.PREVIEW_SIGNATURE_MISSING; } - previousMessages = lastSignatures; - if (buf.readBoolean()) { - lastMessage = new SignaturePair(ProtocolUtils.readUuid(buf), - ProtocolUtils.readByteArray(buf)); + SignaturePair[] previousMessages = new SignaturePair[0]; + SignaturePair lastMessage = null; + + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { + int size = ProtocolUtils.readVarInt(buf); + if (size < 0 || size > MAXIMUM_PREVIOUS_MESSAGE_COUNT) { + throw INVALID_PREVIOUS_MESSAGES; + } + + SignaturePair[] lastSignatures = new SignaturePair[size]; + for (int i = 0; i < size; i++) { + lastSignatures[i] = new SignaturePair(ProtocolUtils.readUuid(buf), + ProtocolUtils.readByteArray(buf)); + } + previousMessages = lastSignatures; + + if (buf.readBoolean()) { + lastMessage = new SignaturePair(ProtocolUtils.readUuid(buf), + ProtocolUtils.readByteArray(buf)); + } } + + return new KeyedPlayerChatPacket(message, signedPreview, unsigned, expiry, signature, salt, + previousMessages, lastMessage); } - } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - ProtocolUtils.writeString(buf, message); + @Override + public void encode(KeyedPlayerChatPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeString(buf, packet.message); - buf.writeLong(unsigned ? Instant.now().toEpochMilli() : expiry.toEpochMilli()); - buf.writeLong(unsigned ? 0L : Longs.fromByteArray(salt)); + buf.writeLong(packet.unsigned ? Instant.now().toEpochMilli() : packet.expiry.toEpochMilli()); + buf.writeLong(packet.unsigned ? 0L : Longs.fromByteArray(packet.salt)); - ProtocolUtils.writeByteArray(buf, unsigned ? EncryptionUtils.EMPTY : signature); + ProtocolUtils.writeByteArray(buf, packet.unsigned ? EncryptionUtils.EMPTY : packet.signature); - buf.writeBoolean(signedPreview); + buf.writeBoolean(packet.signedPreview); - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { - ProtocolUtils.writeVarInt(buf, previousMessages.length); - for (SignaturePair previousMessage : previousMessages) { - ProtocolUtils.writeUuid(buf, previousMessage.getSigner()); - ProtocolUtils.writeByteArray(buf, previousMessage.getSignature()); - } + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { + ProtocolUtils.writeVarInt(buf, packet.previousMessages.length); + for (SignaturePair previousMessage : packet.previousMessages) { + ProtocolUtils.writeUuid(buf, previousMessage.getSigner()); + ProtocolUtils.writeByteArray(buf, previousMessage.getSignature()); + } - if (lastMessage != null) { - buf.writeBoolean(true); - ProtocolUtils.writeUuid(buf, lastMessage.getSigner()); - ProtocolUtils.writeByteArray(buf, lastMessage.getSignature()); - } else { - buf.writeBoolean(false); + if (packet.lastMessage != null) { + buf.writeBoolean(true); + ProtocolUtils.writeUuid(buf, packet.lastMessage.getSigner()); + ProtocolUtils.writeByteArray(buf, packet.lastMessage.getSignature()); + } else { + buf.writeBoolean(false); + } } } } - - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedPlayerCommandPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedPlayerCommandPacket.java index 0bb43ec8e9..06eb9b9af6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedPlayerCommandPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedPlayerCommandPacket.java @@ -26,30 +26,43 @@ import com.velocitypowered.proxy.crypto.EncryptionUtils; import com.velocitypowered.proxy.crypto.SignaturePair; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.util.except.QuietDecoderException; import io.netty.buffer.ByteBuf; import java.time.Instant; import java.util.Arrays; -import java.util.List; import java.util.Map; import org.checkerframework.checker.nullness.qual.Nullable; -public class KeyedPlayerCommandPacket implements MinecraftPacket { +public final class KeyedPlayerCommandPacket implements MinecraftPacket { private static final int MAX_NUM_ARGUMENTS = 8; private static final int MAX_LENGTH_ARGUMENTS = 16; private static final QuietDecoderException LIMITS_VIOLATION = new QuietDecoderException("Command arguments incorrect size"); - private boolean unsigned = false; - private String command; - private Instant timestamp; - private long salt; - private boolean signedPreview; // purely for pass through for 1.19 -> 1.19.2 - this will never be implemented - private SignaturePair[] previousMessages = new SignaturePair[0]; - private @Nullable SignaturePair lastMessage; - private Map arguments = ImmutableMap.of(); + private final boolean unsigned; + private final String command; + private final Instant timestamp; + private final long salt; + private final boolean signedPreview; + private final SignaturePair[] previousMessages; + private final @Nullable SignaturePair lastMessage; + private final Map arguments; + + public KeyedPlayerCommandPacket(boolean unsigned, String command, Instant timestamp, long salt, + boolean signedPreview, SignaturePair[] previousMessages, @Nullable SignaturePair lastMessage, + Map arguments) { + this.unsigned = unsigned; + this.command = command; + this.timestamp = timestamp; + this.salt = salt; + this.signedPreview = signedPreview; + this.previousMessages = previousMessages; + this.lastMessage = lastMessage; + this.arguments = arguments; + } public Instant getTimestamp() { return timestamp; @@ -63,131 +76,119 @@ public String getCommand() { return command; } - public KeyedPlayerCommandPacket() { - } - - /** - * Creates an {@link KeyedPlayerCommandPacket} packet based on a command and list of arguments. - * - * @param command the command to run - * @param arguments the arguments of the command - * @param timestamp the timestamp of the command execution - */ - public KeyedPlayerCommandPacket(String command, List arguments, Instant timestamp) { - this.unsigned = true; - ImmutableMap.Builder builder = ImmutableMap.builder(); - arguments.forEach(entry -> builder.put(entry, EncryptionUtils.EMPTY)); - this.arguments = builder.build(); - this.timestamp = timestamp; - this.command = command; - this.signedPreview = false; - this.salt = 0L; + @Override + public String toString() { + return "PlayerCommand{" + + "unsigned=" + unsigned + + ", command='" + command + '\'' + + ", timestamp=" + timestamp + + ", salt=" + salt + + ", signedPreview=" + signedPreview + + ", previousMessages=" + Arrays.toString(previousMessages) + + ", arguments=" + arguments + + '}'; } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - command = ProtocolUtils.readString(buf, 256); - timestamp = Instant.ofEpochMilli(buf.readLong()); + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } - salt = buf.readLong(); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - int mapSize = ProtocolUtils.readVarInt(buf); - if (mapSize > MAX_NUM_ARGUMENTS) { - throw LIMITS_VIOLATION; - } - // Mapped as Argument : signature - ImmutableMap.Builder entries = ImmutableMap.builderWithExpectedSize(mapSize); - for (int i = 0; i < mapSize; i++) { - entries.put(ProtocolUtils.readString(buf, MAX_LENGTH_ARGUMENTS), - ProtocolUtils.readByteArray(buf, unsigned ? 0 : ProtocolUtils.DEFAULT_MAX_STRING_SIZE)); - } - arguments = entries.build(); + @Override + public KeyedPlayerCommandPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + String command = ProtocolUtils.readString(buf, 256); + Instant timestamp = Instant.ofEpochMilli(buf.readLong()); - this.signedPreview = buf.readBoolean(); - if (unsigned && signedPreview) { - throw EncryptionUtils.PREVIEW_SIGNATURE_MISSING; - } + long salt = buf.readLong(); - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { - int size = ProtocolUtils.readVarInt(buf); - if (size < 0 || size > MAXIMUM_PREVIOUS_MESSAGE_COUNT) { - throw INVALID_PREVIOUS_MESSAGES; + int mapSize = ProtocolUtils.readVarInt(buf); + if (mapSize > MAX_NUM_ARGUMENTS) { + throw LIMITS_VIOLATION; } - SignaturePair[] lastSignatures = new SignaturePair[size]; - for (int i = 0; i < size; i++) { - lastSignatures[i] = new SignaturePair(ProtocolUtils.readUuid(buf), - ProtocolUtils.readByteArray(buf)); + boolean unsigned = false; + ImmutableMap.Builder entries = ImmutableMap.builderWithExpectedSize(mapSize); + for (int i = 0; i < mapSize; i++) { + entries.put(ProtocolUtils.readString(buf, MAX_LENGTH_ARGUMENTS), + ProtocolUtils.readByteArray(buf, unsigned ? 0 : ProtocolUtils.DEFAULT_MAX_STRING_SIZE)); } - previousMessages = lastSignatures; + Map arguments = entries.build(); - if (buf.readBoolean()) { - lastMessage = new SignaturePair(ProtocolUtils.readUuid(buf), - ProtocolUtils.readByteArray(buf)); + boolean signedPreview = buf.readBoolean(); + if (unsigned && signedPreview) { + throw EncryptionUtils.PREVIEW_SIGNATURE_MISSING; } - } - - if (salt == 0L && previousMessages.length == 0) { - unsigned = true; - } - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - ProtocolUtils.writeString(buf, command); - buf.writeLong(timestamp.toEpochMilli()); + SignaturePair[] previousMessages = new SignaturePair[0]; + SignaturePair lastMessage = null; + + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { + int size = ProtocolUtils.readVarInt(buf); + if (size < 0 || size > MAXIMUM_PREVIOUS_MESSAGE_COUNT) { + throw INVALID_PREVIOUS_MESSAGES; + } + + SignaturePair[] lastSignatures = new SignaturePair[size]; + for (int i = 0; i < size; i++) { + lastSignatures[i] = new SignaturePair(ProtocolUtils.readUuid(buf), + ProtocolUtils.readByteArray(buf)); + } + previousMessages = lastSignatures; + + if (buf.readBoolean()) { + lastMessage = new SignaturePair(ProtocolUtils.readUuid(buf), + ProtocolUtils.readByteArray(buf)); + } + } - buf.writeLong(unsigned ? 0L : salt); + if (salt == 0L && previousMessages.length == 0) { + unsigned = true; + } - int size = arguments.size(); - if (size > MAX_NUM_ARGUMENTS) { - throw LIMITS_VIOLATION; - } - ProtocolUtils.writeVarInt(buf, size); - for (Map.Entry entry : arguments.entrySet()) { - // What annoys me is that this isn't "sorted" - ProtocolUtils.writeString(buf, entry.getKey()); - ProtocolUtils.writeByteArray(buf, unsigned ? EncryptionUtils.EMPTY : entry.getValue()); + return new KeyedPlayerCommandPacket(unsigned, command, timestamp, salt, signedPreview, + previousMessages, lastMessage, arguments); } - buf.writeBoolean(signedPreview); + @Override + public void encode(KeyedPlayerCommandPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeString(buf, packet.command); + buf.writeLong(packet.timestamp.toEpochMilli()); + + buf.writeLong(packet.unsigned ? 0L : packet.salt); - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { - ProtocolUtils.writeVarInt(buf, previousMessages.length); - for (SignaturePair previousMessage : previousMessages) { - ProtocolUtils.writeUuid(buf, previousMessage.getSigner()); - ProtocolUtils.writeByteArray(buf, previousMessage.getSignature()); + int size = packet.arguments.size(); + if (size > MAX_NUM_ARGUMENTS) { + throw LIMITS_VIOLATION; + } + ProtocolUtils.writeVarInt(buf, size); + for (Map.Entry entry : packet.arguments.entrySet()) { + // What annoys me is that this isn't "sorted" + ProtocolUtils.writeString(buf, entry.getKey()); + ProtocolUtils.writeByteArray(buf, packet.unsigned ? EncryptionUtils.EMPTY : entry.getValue()); } - if (lastMessage != null) { - buf.writeBoolean(true); - ProtocolUtils.writeUuid(buf, lastMessage.getSigner()); - ProtocolUtils.writeByteArray(buf, lastMessage.getSignature()); - } else { - buf.writeBoolean(false); + buf.writeBoolean(packet.signedPreview); + + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) { + ProtocolUtils.writeVarInt(buf, packet.previousMessages.length); + for (SignaturePair previousMessage : packet.previousMessages) { + ProtocolUtils.writeUuid(buf, previousMessage.getSigner()); + ProtocolUtils.writeByteArray(buf, previousMessage.getSignature()); + } + + if (packet.lastMessage != null) { + buf.writeBoolean(true); + ProtocolUtils.writeUuid(buf, packet.lastMessage.getSigner()); + ProtocolUtils.writeByteArray(buf, packet.lastMessage.getSignature()); + } else { + buf.writeBoolean(false); + } } } - - } - - @Override - public String toString() { - return "PlayerCommand{" - + "unsigned=" + unsigned - + ", command='" + command + '\'' - + ", timestamp=" + timestamp - + ", salt=" + salt - + ", signedPreview=" + signedPreview - + ", previousMessages=" + Arrays.toString(previousMessages) - + ", arguments=" + arguments - + '}'; - } - - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatBuilder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatBuilder.java index 77df43bb42..f5c8c7d4c9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatBuilder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatBuilder.java @@ -44,8 +44,6 @@ public MinecraftPacket toClient() { @Override public MinecraftPacket toServer() { - LegacyChatPacket chat = new LegacyChatPacket(); - chat.setMessage(message); - return chat; + return new LegacyChatPacket(message, (byte) 0, null); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java index 80b239d53b..8469b7059b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java @@ -20,12 +20,13 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import java.util.UUID; import org.checkerframework.checker.nullness.qual.Nullable; -public class LegacyChatPacket implements MinecraftPacket { +public record LegacyChatPacket(String message, byte type, @Nullable UUID sender) implements MinecraftPacket { public static final byte CHAT_TYPE = (byte) 0; public static final byte SYSTEM_TYPE = (byte) 1; @@ -34,91 +35,46 @@ public class LegacyChatPacket implements MinecraftPacket { public static final int MAX_SERVERBOUND_MESSAGE_LENGTH = 256; public static final UUID EMPTY_SENDER = new UUID(0, 0); - private @Nullable String message; - private byte type; - private @Nullable UUID sender; - - public LegacyChatPacket() { - } - - /** - * Creates a Chat packet. - */ - public LegacyChatPacket(String message, byte type, UUID sender) { - this.message = message; - this.type = type; - this.sender = sender; - } - - /** - * Retrieves the Chat message. - */ public String getMessage() { - if (message == null) { - throw new IllegalStateException("Message is not specified"); - } return message; } - public void setMessage(String message) { - this.message = message; - } - - public byte getType() { - return type; - } - - public void setType(byte type) { - this.type = type; - } - - public UUID getSenderUuid() { - return sender; - } - - public void setSenderUuid(UUID sender) { - this.sender = sender; - } - @Override - public String toString() { - return "Chat{" - + "message='" + message + '\'' - + ", type=" + type - + ", sender=" + sender - + '}'; + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - message = ProtocolUtils.readString(buf, direction == ProtocolUtils.Direction.CLIENTBOUND - ? 262144 : version.noLessThan(ProtocolVersion.MINECRAFT_1_11) ? 256 : 100); - if (direction == ProtocolUtils.Direction.CLIENTBOUND - && version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - type = buf.readByte(); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { - sender = ProtocolUtils.readUuid(buf); + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public LegacyChatPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + String message = ProtocolUtils.readString(buf, direction == ProtocolUtils.Direction.CLIENTBOUND + ? 262144 : version.noLessThan(ProtocolVersion.MINECRAFT_1_11) ? 256 : 100); + byte type = 0; + UUID sender = null; + if (direction == ProtocolUtils.Direction.CLIENTBOUND + && version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + type = buf.readByte(); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { + sender = ProtocolUtils.readUuid(buf); + } } + return new LegacyChatPacket(message, type, sender); } - } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (message == null) { - throw new IllegalStateException("Message is not specified"); - } - ProtocolUtils.writeString(buf, message); - if (direction == ProtocolUtils.Direction.CLIENTBOUND - && version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { - buf.writeByte(type); - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { - ProtocolUtils.writeUuid(buf, sender == null ? EMPTY_SENDER : sender); + @Override + public void encode(LegacyChatPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + ProtocolUtils.writeString(buf, packet.message); + if (direction == ProtocolUtils.Direction.CLIENTBOUND + && version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) { + buf.writeByte(packet.type); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) { + ProtocolUtils.writeUuid(buf, packet.sender == null ? EMPTY_SENDER : packet.sender); + } } } } - - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionChatBuilder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionChatBuilder.java index eb74123fc7..46396047ce 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionChatBuilder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionChatBuilder.java @@ -44,27 +44,14 @@ public MinecraftPacket toServer() { LastSeenMessages lastSeenMessages = this.lastSeenMessages != null ? this.lastSeenMessages : new LastSeenMessages(); if (message.startsWith("/")) { if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { - UnsignedPlayerCommandPacket command = new UnsignedPlayerCommandPacket(); - command.command = message.substring(1); - return command; + return new UnsignedPlayerCommandPacket(message.substring(1)); } else { - SessionPlayerCommandPacket command = new SessionPlayerCommandPacket(); - command.command = message.substring(1); - command.salt = 0L; - command.timeStamp = timestamp; - command.argumentSignatures = new SessionPlayerCommandPacket.ArgumentSignatures(); - command.lastSeenMessages = lastSeenMessages; - return command; + return new SessionPlayerCommandPacket(message.substring(1), timestamp, 0L, + new SessionPlayerCommandPacket.ArgumentSignatures(), lastSeenMessages); } } else { - SessionPlayerChatPacket chat = new SessionPlayerChatPacket(); - chat.message = message; - chat.signed = false; - chat.signature = new byte[0]; - chat.timestamp = timestamp; - chat.salt = 0L; - chat.lastSeenMessages = lastSeenMessages; - return chat; + return new SessionPlayerChatPacket(message, timestamp, 0L, false, new byte[0], + lastSeenMessages); } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerChatPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerChatPacket.java index 8a00452c6c..8e226f7c0b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerChatPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerChatPacket.java @@ -20,6 +20,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages; import io.netty.buffer.ByteBuf; @@ -27,14 +28,21 @@ public class SessionPlayerChatPacket implements MinecraftPacket { - protected String message; - protected Instant timestamp; - protected long salt; - protected boolean signed; - protected byte[] signature; - protected LastSeenMessages lastSeenMessages; - - public SessionPlayerChatPacket() { + protected final String message; + protected final Instant timestamp; + protected final long salt; + protected final boolean signed; + protected final byte[] signature; + protected final LastSeenMessages lastSeenMessages; + + public SessionPlayerChatPacket(String message, Instant timestamp, long salt, boolean signed, + byte[] signature, LastSeenMessages lastSeenMessages) { + this.message = message; + this.timestamp = timestamp; + this.salt = salt; + this.signed = signed; + this.signature = signature; + this.lastSeenMessages = lastSeenMessages; } public String getMessage() { @@ -61,34 +69,6 @@ public LastSeenMessages getLastSeenMessages() { return lastSeenMessages; } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - this.message = ProtocolUtils.readString(buf, 256); - this.timestamp = Instant.ofEpochMilli(buf.readLong()); - this.salt = buf.readLong(); - this.signed = buf.readBoolean(); - if (this.signed) { - this.signature = readMessageSignature(buf); - } else { - this.signature = new byte[0]; - } - this.lastSeenMessages = new LastSeenMessages(buf, protocolVersion); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - ProtocolUtils.writeString(buf, this.message); - buf.writeLong(this.timestamp.toEpochMilli()); - buf.writeLong(this.salt); - buf.writeBoolean(this.signed); - if (this.signed) { - buf.writeBytes(this.signature); - } - this.lastSeenMessages.encode(buf, protocolVersion); - } - @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); @@ -101,13 +81,40 @@ protected static byte[] readMessageSignature(ByteBuf buf) { } public SessionPlayerChatPacket withLastSeenMessages(LastSeenMessages lastSeenMessages) { - SessionPlayerChatPacket packet = new SessionPlayerChatPacket(); - packet.message = message; - packet.timestamp = timestamp; - packet.salt = salt; - packet.signed = signed; - packet.signature = signature; - packet.lastSeenMessages = lastSeenMessages; - return packet; + return new SessionPlayerChatPacket(message, timestamp, salt, signed, signature, lastSeenMessages); + } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public SessionPlayerChatPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + String message = ProtocolUtils.readString(buf, 256); + Instant timestamp = Instant.ofEpochMilli(buf.readLong()); + long salt = buf.readLong(); + boolean signed = buf.readBoolean(); + byte[] signature; + if (signed) { + signature = readMessageSignature(buf); + } else { + signature = new byte[0]; + } + LastSeenMessages lastSeenMessages = new LastSeenMessages(buf, protocolVersion); + return new SessionPlayerChatPacket(message, timestamp, salt, signed, signature, lastSeenMessages); + } + + @Override + public void encode(SessionPlayerChatPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeString(buf, packet.message); + buf.writeLong(packet.timestamp.toEpochMilli()); + buf.writeLong(packet.salt); + buf.writeBoolean(packet.signed); + if (packet.signed) { + buf.writeBytes(packet.signature); + } + packet.lastSeenMessages.encode(buf, protocolVersion); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerCommandPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerCommandPacket.java index f4ea3a1e2d..94af39b0f0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerCommandPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerCommandPacket.java @@ -22,6 +22,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages; import com.velocitypowered.proxy.util.except.QuietDecoderException; @@ -33,29 +34,19 @@ public class SessionPlayerCommandPacket implements MinecraftPacket { - protected String command; - protected Instant timeStamp; - protected long salt; - protected ArgumentSignatures argumentSignatures; - protected LastSeenMessages lastSeenMessages; - - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - int cap = protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5) ? 256 : ProtocolUtils.DEFAULT_MAX_STRING_SIZE; - this.command = ProtocolUtils.readString(buf, cap); - this.timeStamp = Instant.ofEpochMilli(buf.readLong()); - this.salt = buf.readLong(); - this.argumentSignatures = new ArgumentSignatures(buf); - this.lastSeenMessages = new LastSeenMessages(buf, protocolVersion); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeString(buf, this.command); - buf.writeLong(this.timeStamp.toEpochMilli()); - buf.writeLong(this.salt); - this.argumentSignatures.encode(buf); - this.lastSeenMessages.encode(buf, protocolVersion); + protected final String command; + protected final Instant timeStamp; + protected final long salt; + protected final ArgumentSignatures argumentSignatures; + protected final LastSeenMessages lastSeenMessages; + + public SessionPlayerCommandPacket(String command, Instant timeStamp, long salt, + ArgumentSignatures argumentSignatures, LastSeenMessages lastSeenMessages) { + this.command = command; + this.timeStamp = timeStamp; + this.salt = salt; + this.argumentSignatures = argumentSignatures; + this.lastSeenMessages = lastSeenMessages; } public String getCommand() { @@ -92,17 +83,35 @@ public String toString() { public SessionPlayerCommandPacket withLastSeenMessages(@Nullable LastSeenMessages lastSeenMessages) { if (lastSeenMessages == null) { - UnsignedPlayerCommandPacket packet = new UnsignedPlayerCommandPacket(); - packet.command = command; - return packet; + return new UnsignedPlayerCommandPacket(command); + } + return new SessionPlayerCommandPacket(command, timeStamp, salt, argumentSignatures, lastSeenMessages); + } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public SessionPlayerCommandPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + int cap = protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5) ? 256 : ProtocolUtils.DEFAULT_MAX_STRING_SIZE; + String command = ProtocolUtils.readString(buf, cap); + Instant timeStamp = Instant.ofEpochMilli(buf.readLong()); + long salt = buf.readLong(); + ArgumentSignatures argumentSignatures = new ArgumentSignatures(buf); + LastSeenMessages lastSeenMessages = new LastSeenMessages(buf, protocolVersion); + return new SessionPlayerCommandPacket(command, timeStamp, salt, argumentSignatures, lastSeenMessages); + } + + @Override + public void encode(SessionPlayerCommandPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeString(buf, packet.command); + buf.writeLong(packet.timeStamp.toEpochMilli()); + buf.writeLong(packet.salt); + packet.argumentSignatures.encode(buf); + packet.lastSeenMessages.encode(buf, protocolVersion); } - SessionPlayerCommandPacket packet = new SessionPlayerCommandPacket(); - packet.command = command; - packet.timeStamp = timeStamp; - packet.salt = salt; - packet.argumentSignatures = argumentSignatures; - packet.lastSeenMessages = lastSeenMessages; - return packet; } public static class ArgumentSignatures { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/UnsignedPlayerCommandPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/UnsignedPlayerCommandPacket.java index 915f0cfbc7..7948c7c8ee 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/UnsignedPlayerCommandPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/UnsignedPlayerCommandPacket.java @@ -19,21 +19,18 @@ import com.velocitypowered.api.event.command.CommandExecuteEvent; import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages; import io.netty.buffer.ByteBuf; import org.checkerframework.checker.nullness.qual.Nullable; -public class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket { +import java.time.Instant; - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - this.command = ProtocolUtils.readString(buf, ProtocolUtils.DEFAULT_MAX_STRING_SIZE); - } +public final class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket { - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeString(buf, this.command); + public UnsignedPlayerCommandPacket(String command) { + super(command, Instant.EPOCH, 0L, new ArgumentSignatures(), null); } @Override @@ -41,6 +38,7 @@ public SessionPlayerCommandPacket withLastSeenMessages(@Nullable LastSeenMessage return this; } + @Override public boolean isSigned() { return false; } @@ -56,4 +54,21 @@ public String toString() { "command='" + command + '\'' + '}'; } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public UnsignedPlayerCommandPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + String command = ProtocolUtils.readString(buf, ProtocolUtils.DEFAULT_MAX_STRING_SIZE); + return new UnsignedPlayerCommandPacket(command); + } + + @Override + public void encode(UnsignedPlayerCommandPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeString(buf, packet.command); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ActiveFeaturesPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ActiveFeaturesPacket.java index 79c94b641b..53c2a24179 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ActiveFeaturesPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ActiveFeaturesPacket.java @@ -20,44 +20,40 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import net.kyori.adventure.key.Key; -public class ActiveFeaturesPacket implements MinecraftPacket { - - private Key[] activeFeatures; - - public ActiveFeaturesPacket(Key[] activeFeatures) { - this.activeFeatures = activeFeatures; - } +public record ActiveFeaturesPacket(Key[] activeFeatures) implements MinecraftPacket { public ActiveFeaturesPacket() { - this.activeFeatures = new Key[0]; - } - - public void setActiveFeatures(Key[] activeFeatures) { - this.activeFeatures = activeFeatures; + this(new Key[0]); } public Key[] getActiveFeatures() { return activeFeatures; } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - activeFeatures = ProtocolUtils.readKeyArray(buf); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - ProtocolUtils.writeKeyArray(buf, activeFeatures); - } - @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public ActiveFeaturesPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + Key[] activeFeatures = ProtocolUtils.readKeyArray(buf); + return new ActiveFeaturesPacket(activeFeatures); + } + + @Override + public void encode(ActiveFeaturesPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + ProtocolUtils.writeKeyArray(buf, packet.activeFeatures); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundCustomReportDetailsPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundCustomReportDetailsPacket.java index 6a3618cb73..1410380b70 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundCustomReportDetailsPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundCustomReportDetailsPacket.java @@ -20,48 +20,43 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import java.util.HashMap; import java.util.Map; -public class ClientboundCustomReportDetailsPacket implements MinecraftPacket { +public record ClientboundCustomReportDetailsPacket(Map details) implements MinecraftPacket { - private Map details; - - public ClientboundCustomReportDetailsPacket() { + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - public ClientboundCustomReportDetailsPacket(Map details) { - this.details = details; - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - int detailsCount = ProtocolUtils.readVarInt(buf); + @Override + public ClientboundCustomReportDetailsPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + int detailsCount = ProtocolUtils.readVarInt(buf); - this.details = new HashMap<>(detailsCount); - for (int i = 0; i < detailsCount; i++) { - details.put(ProtocolUtils.readString(buf), ProtocolUtils.readString(buf)); + Map details = new HashMap<>(detailsCount); + for (int i = 0; i < detailsCount; i++) { + details.put(ProtocolUtils.readString(buf), ProtocolUtils.readString(buf)); + } + return new ClientboundCustomReportDetailsPacket(details); } - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeVarInt(buf, details.size()); - - details.forEach((key, detail) -> { - ProtocolUtils.writeString(buf, key); - ProtocolUtils.writeString(buf, detail); - }); - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } + @Override + public void encode(ClientboundCustomReportDetailsPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, packet.details.size()); - public Map getDetails() { - return details; + packet.details.forEach((key, detail) -> { + ProtocolUtils.writeString(buf, key); + ProtocolUtils.writeString(buf, detail); + }); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundServerLinksPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundServerLinksPacket.java index d37866d86a..1a0229acc2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundServerLinksPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/ClientboundServerLinksPacket.java @@ -21,40 +21,17 @@ import com.velocitypowered.api.util.ServerLink; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import io.netty.buffer.ByteBuf; import java.util.ArrayList; import java.util.List; -public class ClientboundServerLinksPacket implements MinecraftPacket { - - private List serverLinks; +public record ClientboundServerLinksPacket(List serverLinks) implements MinecraftPacket { public ClientboundServerLinksPacket() { - } - - public ClientboundServerLinksPacket(List serverLinks) { - this.serverLinks = serverLinks; - } - - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - int linksCount = ProtocolUtils.readVarInt(buf); - - this.serverLinks = new ArrayList<>(linksCount); - for (int i = 0; i < linksCount; i++) { - serverLinks.add(ServerLink.read(buf, version)); - } - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeVarInt(buf, serverLinks.size()); - - for (ServerLink serverLink : serverLinks) { - serverLink.write(buf); - } + this(List.of()); } @Override @@ -62,10 +39,6 @@ public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } - public List getServerLinks() { - return serverLinks; - } - public record ServerLink(int id, ComponentHolder displayName, String url) { private static ServerLink read(ByteBuf buf, ProtocolVersion version) { @@ -87,4 +60,30 @@ private void write(ByteBuf buf) { ProtocolUtils.writeString(buf, url); } } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public ClientboundServerLinksPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion version) { + int linksCount = ProtocolUtils.readVarInt(buf); + + List serverLinks = new ArrayList<>(linksCount); + for (int i = 0; i < linksCount; i++) { + serverLinks.add(ServerLink.read(buf, version)); + } + return new ClientboundServerLinksPacket(serverLinks); + } + + @Override + public void encode(ClientboundServerLinksPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, packet.serverLinks.size()); + + for (ServerLink serverLink : packet.serverLinks) { + serverLink.write(buf); + } + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java index e9811f9fc2..9f1b59ec30 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java @@ -20,10 +20,11 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; -public class CodeOfConductAcceptPacket implements MinecraftPacket { +public final class CodeOfConductAcceptPacket implements MinecraftPacket { public static final CodeOfConductAcceptPacket INSTANCE = new CodeOfConductAcceptPacket(); @@ -31,15 +32,22 @@ private CodeOfConductAcceptPacket() { } @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } - @Override - public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + @Override + public CodeOfConductAcceptPacket decode(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + return CodeOfConductAcceptPacket.INSTANCE; + } + + @Override + public void encode(CodeOfConductAcceptPacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java index 41433307ef..e9e2cbb44f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java @@ -20,33 +20,80 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; -import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; +import io.netty.buffer.DefaultByteBufHolder; -public class CodeOfConductPacket extends DeferredByteBufHolder implements MinecraftPacket { +public class CodeOfConductPacket extends DefaultByteBufHolder implements MinecraftPacket { - public CodeOfConductPacket() { - super(null); + public CodeOfConductPacket(ByteBuf buf) { + super(buf); } @Override - public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - this.replace(buf.readRetainedSlice(buf.readableBytes())); + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); } @Override - public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { - buf.writeBytes(this.content()); + public CodeOfConductPacket copy() { + return (CodeOfConductPacket) super.copy(); } @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); + public CodeOfConductPacket duplicate() { + return (CodeOfConductPacket) super.duplicate(); + } + + @Override + public CodeOfConductPacket retainedDuplicate() { + return (CodeOfConductPacket) super.retainedDuplicate(); + } + + @Override + public CodeOfConductPacket replace(ByteBuf content) { + return (CodeOfConductPacket) super.replace(content); + } + + @Override + public CodeOfConductPacket retain() { + return (CodeOfConductPacket) super.retain(); + } + + @Override + public CodeOfConductPacket retain(int increment) { + return (CodeOfConductPacket) super.retain(increment); + } + + @Override + public CodeOfConductPacket touch() { + return (CodeOfConductPacket) super.touch(); } @Override - public int encodeSizeHint(Direction direction, ProtocolVersion version) { - return content().readableBytes(); + public CodeOfConductPacket touch(Object hint) { + return (CodeOfConductPacket) super.touch(hint); + } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public CodeOfConductPacket decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { + return new CodeOfConductPacket(buf.readRetainedSlice(buf.readableBytes())); + } + + @Override + public void encode(CodeOfConductPacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + buf.writeBytes(packet.content()); + } + + @Override + public int encodeSizeHint(CodeOfConductPacket packet, Direction direction, + ProtocolVersion version) { + return packet.content().readableBytes(); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/FinishedUpdatePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/FinishedUpdatePacket.java index 20d40fd4be..c2f7faff58 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/FinishedUpdatePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/FinishedUpdatePacket.java @@ -20,33 +20,39 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class FinishedUpdatePacket implements MinecraftPacket { +public final class FinishedUpdatePacket implements MinecraftPacket { public static final FinishedUpdatePacket INSTANCE = new FinishedUpdatePacket(); private FinishedUpdatePacket() { } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - } - - @Override - public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion version) { - return 0; - } - @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public FinishedUpdatePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return FinishedUpdatePacket.INSTANCE; + } + + @Override + public void encode(FinishedUpdatePacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + } + + @Override + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return 0; + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/KnownPacksPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/KnownPacksPacket.java index b3fb0de4fb..5be96c2255 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/KnownPacksPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/KnownPacksPacket.java @@ -20,43 +20,15 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.util.except.QuietDecoderException; import io.netty.buffer.ByteBuf; -public class KnownPacksPacket implements MinecraftPacket { +public record KnownPacksPacket(KnownPack[] packs) implements MinecraftPacket { - private static final int MAX_LENGTH_PACKS = Integer.getInteger("velocity.max-known-packs", 64); - private static final QuietDecoderException TOO_MANY_PACKS = - new QuietDecoderException("too many known packs"); - - private KnownPack[] packs; - - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - final int packCount = ProtocolUtils.readVarInt(buf); - if (direction == ProtocolUtils.Direction.SERVERBOUND && packCount > MAX_LENGTH_PACKS) { - throw TOO_MANY_PACKS; - } - - final KnownPack[] packs = new KnownPack[packCount]; - - for (int i = 0; i < packCount; i++) { - packs[i] = KnownPack.read(buf); - } - - this.packs = packs; - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - ProtocolUtils.writeVarInt(buf, packs.length); - - for (KnownPack pack : packs) { - pack.write(buf); - } + public KnownPacksPacket() { + this(new KnownPack[0]); } @Override @@ -75,4 +47,39 @@ private void write(ByteBuf buf) { ProtocolUtils.writeString(buf, version); } } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + private static final int MAX_LENGTH_PACKS = Integer.getInteger("velocity.max-known-packs", 64); + private static final QuietDecoderException TOO_MANY_PACKS = + new QuietDecoderException("too many known packs"); + + @Override + public KnownPacksPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + final int packCount = ProtocolUtils.readVarInt(buf); + if (direction == ProtocolUtils.Direction.SERVERBOUND && packCount > MAX_LENGTH_PACKS) { + throw TOO_MANY_PACKS; + } + + final KnownPack[] packs = new KnownPack[packCount]; + + for (int i = 0; i < packCount; i++) { + packs[i] = KnownPack.read(buf); + } + + return new KnownPacksPacket(packs); + } + + @Override + public void encode(KnownPacksPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, packet.packs.length); + + for (KnownPack pack : packet.packs) { + pack.write(buf); + } + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/RegistrySyncPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/RegistrySyncPacket.java index 2d9ed230e6..945f085a95 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/RegistrySyncPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/RegistrySyncPacket.java @@ -20,28 +20,16 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; -import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; +import io.netty.buffer.DefaultByteBufHolder; -public class RegistrySyncPacket extends DeferredByteBufHolder implements MinecraftPacket { +public final class RegistrySyncPacket extends DefaultByteBufHolder implements MinecraftPacket { - public RegistrySyncPacket() { - super(null); - } - - // NBT change in 1.20.2 makes it difficult to parse this packet. - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - this.replace(buf.readRetainedSlice(buf.readableBytes())); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - buf.writeBytes(content()); + public RegistrySyncPacket(ByteBuf backing) { + super(backing); } @Override @@ -49,8 +37,24 @@ public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } - @Override public int encodeSizeHint(Direction direction, ProtocolVersion version) { return content().readableBytes(); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public RegistrySyncPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + // NBT change in 1.20.2 makes it difficult to parse this packet. + return new RegistrySyncPacket(buf.readRetainedSlice(buf.readableBytes())); + } + + @Override + public void encode(RegistrySyncPacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + buf.writeBytes(packet.content()); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/StartUpdatePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/StartUpdatePacket.java index d41265ce56..f1b8825d34 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/StartUpdatePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/StartUpdatePacket.java @@ -20,33 +20,39 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class StartUpdatePacket implements MinecraftPacket { +public final class StartUpdatePacket implements MinecraftPacket { public static final StartUpdatePacket INSTANCE = new StartUpdatePacket(); private StartUpdatePacket() { } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - } - - @Override - public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion version) { - return 0; - } - @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public StartUpdatePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return StartUpdatePacket.INSTANCE; + } + + @Override + public void encode(StartUpdatePacket packet, ByteBuf buf, + ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + } + + @Override + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + return 0; + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/TagsUpdatePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/TagsUpdatePacket.java index c0cf414e0c..1520296f75 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/TagsUpdatePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/TagsUpdatePacket.java @@ -21,81 +21,87 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; import java.util.Map; -public class TagsUpdatePacket implements MinecraftPacket { +public final class TagsUpdatePacket implements MinecraftPacket { - private Map> tags; + private final Map> tags; public TagsUpdatePacket(Map> tags) { - this.tags = tags; + this.tags = ImmutableMap.copyOf(tags); } - public TagsUpdatePacket() { - this.tags = Map.of(); + public Map> getTags() { + return tags; } @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - ImmutableMap.Builder> builder = ImmutableMap.builder(); - int size = ProtocolUtils.readVarInt(buf); - for (int i = 0; i < size; i++) { - String key = ProtocolUtils.readString(buf); + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } - int innerSize = ProtocolUtils.readVarInt(buf); - ImmutableMap.Builder innerBuilder = ImmutableMap.builder(); - for (int j = 0; j < innerSize; j++) { - String innerKey = ProtocolUtils.readString(buf); - int[] innerValue = ProtocolUtils.readVarIntArray(buf); - innerBuilder.put(innerKey, innerValue); - } + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); - builder.put(key, innerBuilder.build()); - } - tags = builder.build(); - } + @Override + public TagsUpdatePacket decode(ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int size = ProtocolUtils.readVarInt(buf); + for (int i = 0; i < size; i++) { + String key = ProtocolUtils.readString(buf); - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion protocolVersion) { - ProtocolUtils.writeVarInt(buf, tags.size()); - for (Map.Entry> entry : tags.entrySet()) { - ProtocolUtils.writeString(buf, entry.getKey()); - // Oh, joy - ProtocolUtils.writeVarInt(buf, entry.getValue().size()); - for (Map.Entry innerEntry : entry.getValue().entrySet()) { - // Yea, object oriented programming be damned - ProtocolUtils.writeString(buf, innerEntry.getKey()); - ProtocolUtils.writeVarIntArray(buf, innerEntry.getValue()); + int innerSize = ProtocolUtils.readVarInt(buf); + ImmutableMap.Builder innerBuilder = ImmutableMap.builder(); + for (int j = 0; j < innerSize; j++) { + String innerKey = ProtocolUtils.readString(buf); + int[] innerValue = ProtocolUtils.readVarIntArray(buf); + innerBuilder.put(innerKey, innerValue); + } + + builder.put(key, innerBuilder.build()); } + return new TagsUpdatePacket(builder.build()); } - } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } - - @Override - public int encodeSizeHint(Direction direction, ProtocolVersion version) { - int size = ProtocolUtils.varIntBytes(tags.size()); - for (Map.Entry> entry : tags.entrySet()) { - size += ProtocolUtils.stringSizeHint(entry.getKey()); - size += ProtocolUtils.varIntBytes(entry.getValue().size()); - for (Map.Entry innerEntry : entry.getValue().entrySet()) { - size += ProtocolUtils.stringSizeHint(innerEntry.getKey()); - size += ProtocolUtils.varIntBytes(innerEntry.getValue().length); - for (int innerEntryValue : innerEntry.getValue()) { - size += ProtocolUtils.varIntBytes(innerEntryValue); + @Override + public void encode(TagsUpdatePacket packet, ByteBuf buf, Direction direction, + ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, packet.tags.size()); + for (Map.Entry> entry : packet.tags.entrySet()) { + ProtocolUtils.writeString(buf, entry.getKey()); + // Oh, joy + ProtocolUtils.writeVarInt(buf, entry.getValue().size()); + for (Map.Entry innerEntry : entry.getValue().entrySet()) { + // Yea, object oriented programming be damned + ProtocolUtils.writeString(buf, innerEntry.getKey()); + ProtocolUtils.writeVarIntArray(buf, innerEntry.getValue()); } } } - return size; + @Override + public int encodeSizeHint(TagsUpdatePacket packet, Direction direction, ProtocolVersion version) { + var tags = packet.tags; + int size = ProtocolUtils.varIntBytes(tags.size()); + for (Map.Entry> entry : tags.entrySet()) { + size += ProtocolUtils.stringSizeHint(entry.getKey()); + size += ProtocolUtils.varIntBytes(entry.getValue().size()); + for (Map.Entry innerEntry : entry.getValue().entrySet()) { + size += ProtocolUtils.stringSizeHint(innerEntry.getKey()); + size += ProtocolUtils.varIntBytes(innerEntry.getValue().length); + for (int innerEntryValue : innerEntry.getValue()) { + size += ProtocolUtils.varIntBytes(innerEntryValue); + } + } + } + + return size; + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/GenericTitlePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/GenericTitlePacket.java index d6f5e0e540..7f0174ac86 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/GenericTitlePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/GenericTitlePacket.java @@ -19,9 +19,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.protocol.MinecraftPacket; -import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; -import io.netty.buffer.ByteBuf; public abstract class GenericTitlePacket implements MinecraftPacket { @@ -45,10 +43,9 @@ public int getAction(ProtocolVersion version) { } } + private final ActionType action; - private ActionType action; - - protected void setAction(ActionType action) { + protected GenericTitlePacket(ActionType action) { this.action = action; } @@ -60,76 +57,77 @@ public ComponentHolder getComponent() { throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); } - public void setComponent(ComponentHolder component) { - throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); - } - public int getFadeIn() { throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); } - public void setFadeIn(int fadeIn) { - throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); - } - public int getStay() { throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); } - public void setStay(int stay) { - throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); - } - public int getFadeOut() { throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); } - public void setFadeOut(int fadeOut) { - throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); - } - - - @Override - public final void decode(ByteBuf buf, ProtocolUtils.Direction direction, - ProtocolVersion version) { - throw new UnsupportedOperationException(); // encode only + /** + * Creates a version and type dependent TitlePacket for HIDE/RESET actions. + * + * @param type Action the packet should invoke (HIDE or RESET) + * @param version Protocol version of the target player + * @return GenericTitlePacket instance that follows the invoker type/version + */ + public static GenericTitlePacket createClearTitlePacket(ActionType type, ProtocolVersion version) { + if (type != ActionType.HIDE && type != ActionType.RESET) { + throw new IllegalArgumentException("createClearTitlePacket only accepts HIDE and RESET actions"); + } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { + return new TitleClearPacket(type == ActionType.RESET); + } else { + return new LegacyTitlePacket(type, null, 0, 0, 0); + } } /** - * Creates a version and type dependent TitlePacket. + * Creates a version and type dependent TitlePacket for component-based actions. * - * @param type Action the packet should invoke - * @param version Protocol version of the target player + * @param type Action the packet should invoke + * @param component Component to display + * @param version Protocol version of the target player * @return GenericTitlePacket instance that follows the invoker type/version */ - public static GenericTitlePacket constructTitlePacket(ActionType type, ProtocolVersion version) { - GenericTitlePacket packet = null; + public static GenericTitlePacket createComponentTitlePacket(ActionType type, + ComponentHolder component, ProtocolVersion version) { if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { switch (type) { case SET_ACTION_BAR: - packet = new TitleActionbarPacket(); - break; + return new TitleActionbarPacket(component); case SET_SUBTITLE: - packet = new TitleSubtitlePacket(); - break; - case SET_TIMES: - packet = new TitleTimesPacket(); - break; + return new TitleSubtitlePacket(component); case SET_TITLE: - packet = new TitleTextPacket(); - break; - case HIDE: - case RESET: - packet = new TitleClearPacket(); - break; + return new TitleTextPacket(component); default: - throw new IllegalArgumentException("Invalid ActionType"); + throw new IllegalArgumentException("Invalid ActionType for component title: " + type); } } else { - packet = new LegacyTitlePacket(); + return new LegacyTitlePacket(type, component, 0, 0, 0); } - packet.setAction(type); - return packet; } + /** + * Creates a version dependent TitlePacket for times. + * + * @param fadeIn Fade in time + * @param stay Stay time + * @param fadeOut Fade out time + * @param version Protocol version of the target player + * @return GenericTitlePacket instance that follows the invoker type/version + */ + public static GenericTitlePacket createTimesTitlePacket(int fadeIn, int stay, int fadeOut, + ProtocolVersion version) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { + return new TitleTimesPacket(fadeIn, stay, fadeOut); + } else { + return new LegacyTitlePacket(ActionType.SET_TIMES, null, fadeIn, stay, fadeOut); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/LegacyTitlePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/LegacyTitlePacket.java index 0425f2d3c2..fcdc14af31 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/LegacyTitlePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/LegacyTitlePacket.java @@ -19,52 +19,26 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import io.netty.buffer.ByteBuf; import org.checkerframework.checker.nullness.qual.Nullable; -public class LegacyTitlePacket extends GenericTitlePacket { +public final class LegacyTitlePacket extends GenericTitlePacket { - private @Nullable ComponentHolder component; - private int fadeIn; - private int stay; - private int fadeOut; + private final @Nullable ComponentHolder component; + private final int fadeIn; + private final int stay; + private final int fadeOut; - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - if (version.lessThan(ProtocolVersion.MINECRAFT_1_11) - && getAction() == ActionType.SET_ACTION_BAR) { - throw new IllegalStateException("Action bars are only supported on 1.11 and newer"); - } - ProtocolUtils.writeVarInt(buf, getAction().getAction(version)); - - switch (getAction()) { - case SET_TITLE: - case SET_SUBTITLE: - case SET_ACTION_BAR: - if (component == null) { - throw new IllegalStateException("No component found for " + getAction()); - } - component.write(buf); - break; - case SET_TIMES: - buf.writeInt(fadeIn); - buf.writeInt(stay); - buf.writeInt(fadeOut); - break; - case HIDE: - case RESET: - break; - default: - throw new UnsupportedOperationException("Unknown action " + getAction()); - } - - } - - @Override - public void setAction(ActionType action) { - super.setAction(action); + public LegacyTitlePacket(ActionType action, @Nullable ComponentHolder component, + int fadeIn, int stay, int fadeOut) { + super(action); + this.component = component; + this.fadeIn = fadeIn; + this.stay = stay; + this.fadeOut = fadeOut; } @Override @@ -72,44 +46,24 @@ public void setAction(ActionType action) { return component; } - @Override - public void setComponent(@Nullable ComponentHolder component) { - this.component = component; - } - @Override public int getFadeIn() { return fadeIn; } - @Override - public void setFadeIn(int fadeIn) { - this.fadeIn = fadeIn; - } - @Override public int getStay() { return stay; } - @Override - public void setStay(int stay) { - this.stay = stay; - } - @Override public int getFadeOut() { return fadeOut; } - @Override - public void setFadeOut(int fadeOut) { - this.fadeOut = fadeOut; - } - @Override public String toString() { - return "GenericTitlePacket{" + return "LegacyTitlePacket{" + "action=" + getAction() + ", component='" + component + '\'' + ", fadeIn=" + fadeIn @@ -122,4 +76,45 @@ public String toString() { public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public LegacyTitlePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + throw new UnsupportedOperationException(); // encode only + } + + @Override + public void encode(LegacyTitlePacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_11) + && packet.getAction() == ActionType.SET_ACTION_BAR) { + throw new IllegalStateException("Action bars are only supported on 1.11 and newer"); + } + ProtocolUtils.writeVarInt(buf, packet.getAction().getAction(protocolVersion)); + + switch (packet.getAction()) { + case SET_TITLE: + case SET_SUBTITLE: + case SET_ACTION_BAR: + if (packet.component == null) { + throw new IllegalStateException("No component found for " + packet.getAction()); + } + packet.component.write(buf); + break; + case SET_TIMES: + buf.writeInt(packet.fadeIn); + buf.writeInt(packet.stay); + buf.writeInt(packet.fadeOut); + break; + case HIDE: + case RESET: + break; + default: + throw new UnsupportedOperationException("Unknown action " + packet.getAction()); + } + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleActionbarPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleActionbarPacket.java index f34983eaf9..4ed5771d75 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleActionbarPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleActionbarPacket.java @@ -19,21 +19,18 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import io.netty.buffer.ByteBuf; -public class TitleActionbarPacket extends GenericTitlePacket { +public final class TitleActionbarPacket extends GenericTitlePacket { - private ComponentHolder component; + private final ComponentHolder component; - public TitleActionbarPacket() { - setAction(ActionType.SET_TITLE); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - component.write(buf); + public TitleActionbarPacket(ComponentHolder component) { + super(ActionType.SET_ACTION_BAR); + this.component = component; } @Override @@ -41,15 +38,10 @@ public ComponentHolder getComponent() { return component; } - @Override - public void setComponent(ComponentHolder component) { - this.component = component; - } - @Override public String toString() { return "TitleActionbarPacket{" - + ", component='" + component + '\'' + + "component='" + component + '\'' + '}'; } @@ -57,4 +49,20 @@ public String toString() { public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public TitleActionbarPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + throw new UnsupportedOperationException(); // encode only + } + + @Override + public void encode(TitleActionbarPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + packet.component.write(buf); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleClearPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleClearPacket.java index 1b3489691f..c343f1f392 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleClearPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleClearPacket.java @@ -19,32 +19,27 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class TitleClearPacket extends GenericTitlePacket { +public final class TitleClearPacket extends GenericTitlePacket { - public TitleClearPacket() { - setAction(ActionType.HIDE); - } + private final boolean reset; - @Override - public void setAction(ActionType action) { - if (action != ActionType.HIDE && action != ActionType.RESET) { - throw new IllegalArgumentException("TitleClearPacket only accepts CLEAR and RESET actions"); - } - super.setAction(action); + public TitleClearPacket(boolean reset) { + super(reset ? ActionType.RESET : ActionType.HIDE); + this.reset = reset; } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - buf.writeBoolean(getAction() == ActionType.RESET); + public boolean isReset() { + return reset; } @Override public String toString() { return "TitleClearPacket{" - + ", resetTimes=" + (getAction() == ActionType.RESET) + + "resetTimes=" + reset + '}'; } @@ -52,4 +47,20 @@ public String toString() { public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public TitleClearPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + throw new UnsupportedOperationException(); // encode only + } + + @Override + public void encode(TitleClearPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + buf.writeBoolean(packet.reset); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleSubtitlePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleSubtitlePacket.java index 0f375ae20a..5914c1dfce 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleSubtitlePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleSubtitlePacket.java @@ -19,21 +19,18 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import io.netty.buffer.ByteBuf; -public class TitleSubtitlePacket extends GenericTitlePacket { +public final class TitleSubtitlePacket extends GenericTitlePacket { - private ComponentHolder component; + private final ComponentHolder component; - public TitleSubtitlePacket() { - setAction(ActionType.SET_SUBTITLE); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - component.write(buf); + public TitleSubtitlePacket(ComponentHolder component) { + super(ActionType.SET_SUBTITLE); + this.component = component; } @Override @@ -41,15 +38,10 @@ public ComponentHolder getComponent() { return component; } - @Override - public void setComponent(ComponentHolder component) { - this.component = component; - } - @Override public String toString() { return "TitleSubtitlePacket{" - + ", component='" + component + '\'' + + "component='" + component + '\'' + '}'; } @@ -57,4 +49,20 @@ public String toString() { public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public TitleSubtitlePacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + throw new UnsupportedOperationException(); // encode only + } + + @Override + public void encode(TitleSubtitlePacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + packet.component.write(buf); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleTextPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleTextPacket.java index ae75f5d618..7ec462da55 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleTextPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleTextPacket.java @@ -19,21 +19,18 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import io.netty.buffer.ByteBuf; -public class TitleTextPacket extends GenericTitlePacket { +public final class TitleTextPacket extends GenericTitlePacket { - private ComponentHolder component; + private final ComponentHolder component; - public TitleTextPacket() { - setAction(ActionType.SET_TITLE); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - component.write(buf); + public TitleTextPacket(ComponentHolder component) { + super(ActionType.SET_TITLE); + this.component = component; } @Override @@ -41,15 +38,10 @@ public ComponentHolder getComponent() { return component; } - @Override - public void setComponent(ComponentHolder component) { - this.component = component; - } - @Override public String toString() { return "TitleTextPacket{" - + ", component='" + component + '\'' + + "component='" + component + '\'' + '}'; } @@ -57,4 +49,20 @@ public String toString() { public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public TitleTextPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + throw new UnsupportedOperationException(); // encode only + } + + @Override + public void encode(TitleTextPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + packet.component.write(buf); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleTimesPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleTimesPacket.java index 8764a12fab..81294deb5f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleTimesPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/title/TitleTimesPacket.java @@ -19,24 +19,21 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.PacketCodec; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -public class TitleTimesPacket extends GenericTitlePacket { +public final class TitleTimesPacket extends GenericTitlePacket { - private int fadeIn; - private int stay; - private int fadeOut; + private final int fadeIn; + private final int stay; + private final int fadeOut; - public TitleTimesPacket() { - setAction(ActionType.SET_TIMES); - } - - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - buf.writeInt(fadeIn); - buf.writeInt(stay); - buf.writeInt(fadeOut); + public TitleTimesPacket(int fadeIn, int stay, int fadeOut) { + super(ActionType.SET_TIMES); + this.fadeIn = fadeIn; + this.stay = stay; + this.fadeOut = fadeOut; } @Override @@ -44,35 +41,20 @@ public int getFadeIn() { return fadeIn; } - @Override - public void setFadeIn(int fadeIn) { - this.fadeIn = fadeIn; - } - @Override public int getStay() { return stay; } - @Override - public void setStay(int stay) { - this.stay = stay; - } - @Override public int getFadeOut() { return fadeOut; } - @Override - public void setFadeOut(int fadeOut) { - this.fadeOut = fadeOut; - } - @Override public String toString() { return "TitleTimesPacket{" - + ", fadeIn=" + fadeIn + + "fadeIn=" + fadeIn + ", stay=" + stay + ", fadeOut=" + fadeOut + '}'; @@ -82,4 +64,22 @@ public String toString() { public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + public static class Codec implements PacketCodec { + public static final Codec INSTANCE = new Codec(); + + @Override + public TitleTimesPacket decode(ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + throw new UnsupportedOperationException(); // encode only + } + + @Override + public void encode(TitleTimesPacket packet, ByteBuf buf, ProtocolUtils.Direction direction, + ProtocolVersion protocolVersion) { + buf.writeInt(packet.fadeIn); + buf.writeInt(packet.stay); + buf.writeInt(packet.fadeOut); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/MultiVersionPacketRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/MultiVersionPacketRegistry.java new file mode 100644 index 0000000000..448d417782 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/MultiVersionPacketRegistry.java @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.registry; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; +import io.netty.util.collection.IntObjectHashMap; +import io.netty.util.collection.IntObjectMap; +import it.unimi.dsi.fastutil.Hash.Strategy; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * A multi-version packet registry that supports different packet ID mappings across protocol + * versions. This implementation optimizes memory usage by reusing mapping data structures across + * versions when the mappings are identical. + */ +public class MultiVersionPacketRegistry implements ProtocolToPacketRegistry { + + private final Map versionMappings; + + private MultiVersionPacketRegistry( + Map versionMappings) { + this.versionMappings = versionMappings; + } + + @Override + public PacketRegistry forVersion(ProtocolVersion version) { + VersionMapping mapping = this.versionMappings.get(version); + if (mapping == null) { + throw new IllegalArgumentException(String.format("Invalid version %s", version)); + } + return mapping; + } + + /** + * Holds the packet mappings for a specific protocol version or a range of versions. + * Instances are shared across versions when the mappings are identical. + */ + private static class VersionMapping implements PacketRegistry { + + final IntObjectMap> packetIdToCodec; + final Map, PacketCodec> + packetClassToCodec; + final Object2IntMap> packetClassToId; + final Direction direction; + final ProtocolVersion version; + + VersionMapping(Direction direction, ProtocolVersion version) { + this.direction = direction; + this.version = version; + this.packetIdToCodec = new IntObjectHashMap<>(16, 0.5f); + this.packetClassToCodec = new HashMap<>(16, 0.5f); + this.packetClassToId = new Object2IntOpenHashMap<>(16, 0.5f); + this.packetClassToId.defaultReturnValue(Integer.MIN_VALUE); + } + + void register(int id, Class packet, + PacketCodec codec, boolean encodeOnly) { + if (!encodeOnly) { + this.packetIdToCodec.put(id, codec); + } + this.packetClassToCodec.put(packet, codec); + this.packetClassToId.put(packet, id); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + VersionMapping that = (VersionMapping) o; + return Objects.equals(packetIdToCodec, that.packetIdToCodec) + && Objects.equals(packetClassToCodec, that.packetClassToCodec) + && Objects.equals(packetClassToId, that.packetClassToId) + && Objects.equals(direction, that.direction) + && Objects.equals(version, that.version); + } + + public boolean equalsNonStrict(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + VersionMapping that = (VersionMapping) o; + return Objects.equals(packetIdToCodec, that.packetIdToCodec) + && Objects.equals(packetClassToCodec, that.packetClassToCodec) + && Objects.equals(packetClassToId, that.packetClassToId); + } + + @Override + public int hashCode() { + return Objects.hash(packetIdToCodec, packetClassToCodec, packetClassToId, direction, version); + } + + public int hashCodeNonStrict() { + return Objects.hash(packetIdToCodec, packetClassToCodec, packetClassToId); + } + + @Override + public @Nullable PacketCodec getCodec(int id) { + return packetIdToCodec.get(id); + } + + @Override + @SuppressWarnings("unchecked") + public @Nullable PacketCodec getCodec(Class packetClass) { + return (PacketCodec) packetClassToCodec.get(packetClass); + } + + @Override + public int getPacketId(MinecraftPacket packet) { + final int id = packetClassToId.getInt(packet.getClass()); + if (id == Integer.MIN_VALUE) { + throw new IllegalArgumentException(String.format( + "Unable to find id for packet of type %s in %s protocol %s", + packet.getClass().getName(), direction, version + )); + } + return id; + } + + @Override + public boolean canDecodePacket(MinecraftPacket packet) { + return packetClassToId.containsKey(packet.getClass()); + } + } + + /** + * Creates a new builder for constructing a multi-version packet registry. + * + * @param direction the packet direction (clientbound or serverbound) + * @return a new builder instance + */ + public static Builder builder(Direction direction) { + return new Builder(direction); + } + + /** + * Builder for constructing a MultiVersionPacketRegistry with optimized memory usage. + */ + public static class Builder { + + private final Direction direction; + private final Map workingMappings = + new EnumMap<>(ProtocolVersion.class); + + private Builder(Direction direction) { + this.direction = direction; + } + + /** + * Registers a packet for a specific protocol version with a given ID. + * + * @param packetClass the packet class + * @param codec the packet codec + * @param version the protocol version + * @param packetId the packet ID + * @param the packet type + * @return this builder + */ + public Builder register( + Class packetClass, + PacketCodec codec, + ProtocolVersion version, + int packetId) { + return register(packetClass, codec, version, null, packetId, false); + } + + /** + * Registers a packet for a range of protocol versions with a given ID. + * + * @param packetClass the packet class + * @param codec the packet codec + * @param startVersion the starting protocol version (inclusive) + * @param endVersion the ending protocol version (inclusive), or null for all future versions + * @param packetId the packet ID + * @param encodeOnly if true, the packet will only be registered for encoding, not decoding + * @param the packet type + * @return this builder + */ + public Builder register( + Class packetClass, + PacketCodec codec, + ProtocolVersion startVersion, + @Nullable ProtocolVersion endVersion, + int packetId, + boolean encodeOnly) { + + ProtocolVersion end = endVersion != null ? endVersion : ProtocolVersion.MAXIMUM_VERSION; + + // Validate version range + if (startVersion.greaterThan(end)) { + throw new IllegalArgumentException(String.format( + "Start version %s is greater than end version %s", startVersion, end + )); + } + + // Register for all versions in the range + for (ProtocolVersion version : ProtocolVersion.values()) { + if (version.isLegacy() || version.isUnknown()) { + continue; + } + if (version.noLessThan(startVersion) && version.noGreaterThan(end)) { + VersionMapping versionMapping = workingMappings.computeIfAbsent(version, v -> new VersionMapping(direction, version)); + if (versionMapping.packetClassToCodec.containsKey(packetClass)) { + throw new IllegalArgumentException(String.format( + "Packet %s already registered for version %s", packetClass.getName(), version + )); + } + versionMapping.register(packetId, packetClass, codec, encodeOnly); + } + } + + return this; + } + + /** + * Registers a packet for multiple version ranges. + * + * @param packetClass the packet class + * @param codec the packet codec + * @param ranges the version ranges to register + * @param the packet type + * @return this builder + */ + public Builder register( + Class packetClass, + PacketCodec codec, + VersionRange... ranges) { + if (ranges.length == 0) { + throw new IllegalArgumentException("At least one version range must be provided"); + } + for (VersionRange range : ranges) { + register(packetClass, codec, range.start, range.end, range.packetId, range.encodeOnly); + } + return this; + } + + /** + * Builds the multi-version packet registry, optimizing memory by reusing identical mappings + * across versions. + * + * @return the constructed registry + */ + public MultiVersionPacketRegistry build() { + // Optimize by reusing identical mappings across versions + Map optimized = new EnumMap<>(ProtocolVersion.class); + Map deduplicationMap = new Object2ObjectOpenCustomHashMap<>( + new Strategy<>() { + @Override + public int hashCode(VersionMapping o) { + return o.hashCodeNonStrict(); + } + + @Override + public boolean equals(VersionMapping a, VersionMapping b) { + return a.equalsNonStrict(b); + } + }); + + for (Map.Entry entry : workingMappings.entrySet()) { + VersionMapping mapping = entry.getValue(); + VersionMapping canonical = deduplicationMap.get(mapping); + if (canonical == null) { + // First time seeing this mapping, use it as the canonical version + deduplicationMap.put(mapping, mapping); + canonical = mapping; + } + // Reuse the canonical mapping + optimized.put(entry.getKey(), canonical); + } + + return new MultiVersionPacketRegistry(Collections.unmodifiableMap(optimized)); + } + } + + /** + * Represents a version range for packet registration. + */ + public static class VersionRange { + + final ProtocolVersion start; + final @Nullable ProtocolVersion end; + final int packetId; + final boolean encodeOnly; + + private VersionRange( + ProtocolVersion start, + @Nullable ProtocolVersion end, + int packetId, + boolean encodeOnly) { + this.start = start; + this.end = end; + this.packetId = packetId; + this.encodeOnly = encodeOnly; + } + + /** + * Creates a version range starting from the specified version and continuing to all future + * versions. + * + * @param start the starting version + * @param packetId the packet ID + * @return a new version range + */ + public static VersionRange of(ProtocolVersion start, int packetId) { + return new VersionRange(start, null, packetId, false); + } + + /** + * Creates a version range from start to end (inclusive). + * + * @param start the starting version + * @param end the ending version + * @param packetId the packet ID + * @return a new version range + */ + public static VersionRange of(ProtocolVersion start, ProtocolVersion end, int packetId) { + return new VersionRange(start, end, packetId, false); + } + + /** + * Creates an encode-only version range starting from the specified version. + * + * @param start the starting version + * @param packetId the packet ID + * @return a new version range + */ + public static VersionRange encodeOnly(ProtocolVersion start, int packetId) { + return new VersionRange(start, null, packetId, true); + } + + /** + * Creates an encode-only version range from start to end (inclusive). + * + * @param start the starting version + * @param end the ending version + * @param packetId the packet ID + * @return a new version range + */ + public static VersionRange encodeOnly( + ProtocolVersion start, ProtocolVersion end, int packetId) { + return new VersionRange(start, end, packetId, true); + } + } +} \ No newline at end of file diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/PacketRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/PacketRegistry.java new file mode 100644 index 0000000000..b9034d8bd9 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/PacketRegistry.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.registry; + +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Interface for retrieving packet codecs and IDs for a specific protocol version. + */ +public interface PacketRegistry { + @Nullable + PacketCodec getCodec(final int id); + + @Nullable PacketCodec getCodec( + final Class packetClass); + + int getPacketId(final MinecraftPacket packet); + + boolean canDecodePacket(MinecraftPacket packet); +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/ProtocolToPacketRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/ProtocolToPacketRegistry.java new file mode 100644 index 0000000000..85282a7bb7 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/ProtocolToPacketRegistry.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.registry; + +import com.velocitypowered.api.network.ProtocolVersion; + +/** + * Registry mapping protocol versions to their packet registries. + */ +public interface ProtocolToPacketRegistry { + PacketRegistry forVersion(ProtocolVersion version); +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/SimplePacketRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/SimplePacketRegistry.java new file mode 100644 index 0000000000..eb20f205e3 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/registry/SimplePacketRegistry.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.registry; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.PacketCodec; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; +import io.netty.util.collection.IntObjectHashMap; +import io.netty.util.collection.IntObjectMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import java.util.HashMap; +import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Extremely simple packet registry implementation that assumes every protocol version is + * the same. + */ +public class SimplePacketRegistry implements PacketRegistry, ProtocolToPacketRegistry { + private final IntObjectMap> packetIdToCodec = + new IntObjectHashMap<>(16, 0.5f); + private final Map, PacketCodec> packetClassToCodec = + new HashMap<>(16, 0.5f); + private final Object2IntMap> packetClassToId = + new Object2IntOpenHashMap<>(16, 0.5f); + private final Direction direction; + + public SimplePacketRegistry(Direction direction) { + this.direction = direction; + this.packetClassToId.defaultReturnValue(Integer.MIN_VALUE); + } + + /** + * Registers a packet for this protocol version. + * + * @param id the packet ID + * @param packet the packet class + * @param codec the packet codec + */ + public void register(int id, Class packet, PacketCodec codec) { + this.packetIdToCodec.put(id, codec); + this.packetClassToCodec.put(packet, codec); + this.packetClassToId.put(packet, id); + } + + @Override + public @Nullable PacketCodec getCodec(int id) { + return this.packetIdToCodec.get(id); + } + + @Override + public @Nullable PacketCodec getCodec(Class packetClass) { + return (PacketCodec) this.packetClassToCodec.get(packetClass); + } + + @Override + public int getPacketId(MinecraftPacket packet) { + final int id = this.packetClassToId.getInt(packet.getClass()); + if (id == Integer.MIN_VALUE) { + throw new IllegalArgumentException(String.format( + "Unable to find id for packet of type %s in %s phase %s", + packet.getClass().getName(), direction, this + )); + } + return id; + } + + @Override + public boolean canDecodePacket(MinecraftPacket packet) { + return packetClassToId.containsKey(packet.getClass()); + } + + @Override + public PacketRegistry forVersion(ProtocolVersion version) { + return this; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/DeferredByteBufHolder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/DeferredByteBufHolder.java deleted file mode 100644 index 368ac503dc..0000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/DeferredByteBufHolder.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2019-2021 Velocity Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.velocitypowered.proxy.protocol.util; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.util.IllegalReferenceCountException; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; - -/** - * A special-purpose implementation of {@code ByteBufHolder} that can defer accepting its buffer. - * This is required because Velocity packets are, for better or worse, mutable. - */ -public class DeferredByteBufHolder implements ByteBufHolder { - - @MonotonicNonNull - private ByteBuf backing; - - public DeferredByteBufHolder( - @MonotonicNonNull ByteBuf backing) { - this.backing = backing; - } - - @Override - public ByteBuf content() { - if (backing == null) { - throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); - } - if (backing.refCnt() <= 0) { - throw new IllegalReferenceCountException(backing.refCnt()); - } - return backing; - } - - @Override - public ByteBufHolder copy() { - if (backing == null) { - throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); - } - return new DeferredByteBufHolder(backing.copy()); - } - - @Override - public ByteBufHolder duplicate() { - if (backing == null) { - throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); - } - return new DeferredByteBufHolder(backing.duplicate()); - } - - @Override - public ByteBufHolder retainedDuplicate() { - if (backing == null) { - throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); - } - return new DeferredByteBufHolder(backing.retainedDuplicate()); - } - - @Override - public ByteBufHolder replace(ByteBuf content) { - if (content == null) { - throw new NullPointerException("content"); - } - this.backing = content; - return this; - } - - @Override - public int refCnt() { - if (backing == null) { - throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); - } - return backing.refCnt(); - } - - @Override - public ByteBufHolder retain() { - if (backing == null) { - throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); - } - backing.retain(); - return this; - } - - @Override - public ByteBufHolder retain(int increment) { - if (backing == null) { - throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); - } - backing.retain(increment); - return this; - } - - @Override - public ByteBufHolder touch() { - if (backing == null) { - throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); - } - backing.touch(); - return this; - } - - @Override - public ByteBufHolder touch(Object hint) { - if (backing == null) { - throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); - } - backing.touch(hint); - return this; - } - - @Override - public boolean release() { - if (backing == null) { - throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); - } - return backing.release(); - } - - @Override - public boolean release(int decrement) { - if (backing == null) { - throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); - } - return backing.release(decrement); - } - - @Override - public String toString() { - String str = "DeferredByteBufHolder["; - if (backing == null) { - str += "null"; - } else { - str += backing.toString(); - } - return str + "]"; - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java index e58c72b446..0491e6480d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java @@ -56,12 +56,10 @@ public class PingSessionHandler implements MinecraftSessionHandler { @Override public void activated() { - HandshakePacket handshake = new HandshakePacket(); - handshake.setIntent(HandshakeIntent.STATUS); - handshake.setServerAddress(this.virtualHostString == null || this.virtualHostString.isEmpty() - ? server.getServerInfo().getAddress().getHostString() : this.virtualHostString); - handshake.setPort(server.getServerInfo().getAddress().getPort()); - handshake.setProtocolVersion(version); + String serverAddress = this.virtualHostString == null || this.virtualHostString.isEmpty() + ? server.getServerInfo().getAddress().getHostString() : this.virtualHostString; + HandshakePacket handshake = new HandshakePacket(version, serverAddress, + server.getServerInfo().getAddress().getPort(), HandshakeIntent.STATUS); connection.delayedWrite(handshake); connection.setActiveSessionHandler(StateRegistry.STATUS); diff --git a/proxy/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java b/proxy/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java index f65674ea1e..bcb24f2667 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java @@ -18,6 +18,7 @@ package com.velocitypowered.proxy.protocol; import static com.google.common.collect.Iterables.getLast; +import static com.velocitypowered.api.network.ProtocolVersion.MAXIMUM_VERSION; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_11; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12_1; @@ -25,135 +26,414 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14_2; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14_4; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_15; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.protocol.packet.HandshakePacket; import com.velocitypowered.proxy.protocol.packet.StatusPingPacket; +import com.velocitypowered.proxy.protocol.registry.MultiVersionPacketRegistry; +import com.velocitypowered.proxy.protocol.registry.MultiVersionPacketRegistry.VersionRange; +import com.velocitypowered.proxy.protocol.registry.SimplePacketRegistry; import org.junit.jupiter.api.Test; class PacketRegistryTest { - private StateRegistry.PacketRegistry setupRegistry() { - StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( - ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); - registry.register(HandshakePacket.class, HandshakePacket::new, - new StateRegistry.PacketMapping(0x01, MINECRAFT_1_8, null, false), - new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, null, false), - new StateRegistry.PacketMapping(0x00, MINECRAFT_1_15, MINECRAFT_1_16, false)); - return registry; + /** + * Sets up a multi-version registry with HandshakePacket mapped to different IDs across version + * ranges. + */ + private MultiVersionPacketRegistry setupRegistry() { + return MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_8, MINECRAFT_1_11, 0x01), + VersionRange.of(MINECRAFT_1_12, MINECRAFT_1_14_4, 0x00), + VersionRange.of(MINECRAFT_1_15, MAXIMUM_VERSION, 0x00)) + .build(); } + // ==================== Basic Functionality Tests ==================== + @Test void packetRegistryWorks() { - StateRegistry.PacketRegistry registry = setupRegistry(); - MinecraftPacket packet = registry.getProtocolRegistry(MINECRAFT_1_12).createPacket(0); + MultiVersionPacketRegistry registry = setupRegistry(); + PacketCodec packet = registry.forVersion(MINECRAFT_1_12).getCodec(0); assertNotNull(packet, "Packet was not found in registry"); - assertEquals(HandshakePacket.class, packet.getClass(), "Registry returned wrong class"); + assertEquals(HandshakePacket.Codec.class, packet.getClass(), "Registry returned wrong class"); - assertEquals(0, registry.getProtocolRegistry(MINECRAFT_1_12).getPacketId(packet), + assertEquals(0, registry.forVersion(MINECRAFT_1_12).getPacketId(HandshakePacket.DEFAULT), "Registry did not return the correct packet ID"); } @Test void packetRegistryLinkingWorks() { - StateRegistry.PacketRegistry registry = setupRegistry(); - MinecraftPacket packet = registry.getProtocolRegistry(MINECRAFT_1_12_1).createPacket(0); + MultiVersionPacketRegistry registry = setupRegistry(); + PacketCodec packet = registry.forVersion(MINECRAFT_1_12_1).getCodec(0); assertNotNull(packet, "Packet was not found in registry"); - assertEquals(HandshakePacket.class, packet.getClass(), "Registry returned wrong class"); - assertEquals(0, registry.getProtocolRegistry(MINECRAFT_1_12_1).getPacketId(packet), + assertEquals(HandshakePacket.Codec.class, packet.getClass(), "Registry returned wrong class"); + HandshakePacket handshakePacket = HandshakePacket.DEFAULT; + assertEquals(0, registry.forVersion(MINECRAFT_1_12_1).getPacketId(handshakePacket), "Registry did not return the correct packet ID"); - assertEquals(0, registry.getProtocolRegistry(MINECRAFT_1_14_2).getPacketId(packet), + assertEquals(0, registry.forVersion(MINECRAFT_1_14_2).getPacketId(handshakePacket), "Registry did not return the correct packet ID"); - assertEquals(1, registry.getProtocolRegistry(MINECRAFT_1_11).getPacketId(packet), + assertEquals(1, registry.forVersion(MINECRAFT_1_11).getPacketId(handshakePacket), "Registry did not return the correct packet ID"); - assertNull(registry.getProtocolRegistry(MINECRAFT_1_14_2).createPacket(0x01), + assertNull(registry.forVersion(MINECRAFT_1_14_2).getCodec(0x01), "Registry should return a null"); - assertNull(registry.getProtocolRegistry(MINECRAFT_1_16_2).createPacket(0), - "Registry should return null"); + // 1.16_2 is >= 1.15, so it should have codec for 0x00 from the last range + assertNotNull(registry.forVersion(MINECRAFT_1_16_2).getCodec(0), + "1.16_2 should have codec for 0x00 from 1.15+ range"); } + @Test + void registrySuppliesCorrectPacketsByProtocol() { + MultiVersionPacketRegistry registry = MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_12, MINECRAFT_1_12, 0x00), + VersionRange.of(MINECRAFT_1_12_1, MINECRAFT_1_12_2, 0x01), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_14_4, 0x02)) + .build(); + assertNotNull(registry.forVersion(MINECRAFT_1_12).getCodec(0x00)); + assertEquals(HandshakePacket.Codec.class, + registry.forVersion(MINECRAFT_1_12).getCodec(0x00).getClass()); + assertNotNull(registry.forVersion(MINECRAFT_1_12_1).getCodec(0x01)); + assertEquals(HandshakePacket.Codec.class, + registry.forVersion(MINECRAFT_1_12_1).getCodec(0x01).getClass()); + assertNotNull(registry.forVersion(MINECRAFT_1_12_2).getCodec(0x01)); + assertEquals(HandshakePacket.Codec.class, + registry.forVersion(MINECRAFT_1_12_2).getCodec(0x01).getClass()); + assertNotNull(registry.forVersion(MINECRAFT_1_13).getCodec(0x02)); + assertEquals(HandshakePacket.Codec.class, + registry.forVersion(MINECRAFT_1_13).getCodec(0x02).getClass()); + assertNotNull(registry.forVersion(MINECRAFT_1_14_2).getCodec(0x02)); + assertEquals(HandshakePacket.Codec.class, + registry.forVersion(MINECRAFT_1_14_2).getCodec(0x02).getClass()); + } + + // ==================== Error Handling Tests ==================== + @Test void failOnNoMappings() { - StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( - ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); - assertThrows(IllegalArgumentException.class, - () -> registry.register(HandshakePacket.class, HandshakePacket::new)); assertThrows(IllegalArgumentException.class, - () -> registry.getProtocolRegistry(ProtocolVersion.UNKNOWN) - .getPacketId(new HandshakePacket())); + () -> MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .build() + .forVersion(ProtocolVersion.UNKNOWN)); } @Test - void failOnWrongOrder() { - StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( - ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); + void failOnWrongVersionOrder() { + // End version before start version assertThrows(IllegalArgumentException.class, - () -> registry.register(HandshakePacket.class, HandshakePacket::new, - new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false), - new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false))); + () -> MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_13, 0x01), + VersionRange.of(MINECRAFT_1_8, 0x00)) + .build()); + } + + @Test + void failOnInvalidRangeStartGreaterThanEnd() { + // Single VersionRange with start > end assertThrows(IllegalArgumentException.class, - () -> registry.register(HandshakePacket.class, HandshakePacket::new, - new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false), - new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false))); + () -> MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_13, MINECRAFT_1_8, 0x01)) + .build()); + } + + @Test + void failOnDuplicateVersionRanges() { + // Two ranges covering the same version assertThrows(IllegalArgumentException.class, - () -> registry.register(HandshakePacket.class, HandshakePacket::new, - new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, MINECRAFT_1_8, false))); + () -> MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_13, 0x01), + VersionRange.of(MINECRAFT_1_13, 0x01)) + .build()); + } + + @Test + void failOnOverlappingVersionRanges() { + // Same packet registered twice with overlapping versions should fail assertThrows(IllegalArgumentException.class, - () -> registry.register(HandshakePacket.class, HandshakePacket::new, - new StateRegistry.PacketMapping(0x01, MINECRAFT_1_8, MINECRAFT_1_14, false), - new StateRegistry.PacketMapping(0x00, MINECRAFT_1_16, null, false))); + () -> MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_8, MINECRAFT_1_14, 0x01)) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_12, MINECRAFT_1_16, 0x00)) + .build()); } @Test - void failOnDuplicate() { - StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( - ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); - registry.register(HandshakePacket.class, HandshakePacket::new, - new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false)); + void failOnDuplicatePacketClass() { + // Same packet class registered twice for overlapping versions assertThrows(IllegalArgumentException.class, - () -> registry.register(HandshakePacket.class, HandshakePacket::new, - new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12, null, false))); + () -> MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_8, MINECRAFT_1_14, 0x00)) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_12, 0x01)) + .build()); + } + + @Test + void allowDifferentPacketsWithSameIdInDifferentVersions() { + // Different packets can have the same ID in different version ranges + assertDoesNotThrow( + () -> MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_8, MINECRAFT_1_12, 0x00)) + .register(StatusPingPacket.class, new StatusPingPacket.Codec(), + VersionRange.of(MINECRAFT_1_13, MAXIMUM_VERSION, 0x00)) + .build()); + } + + @Test + void failOnInvalidPacketIdLookup() { + MultiVersionPacketRegistry registry = setupRegistry(); + // Try to get packet ID for a packet type that doesn't exist in the registry assertThrows(IllegalArgumentException.class, - () -> registry.register(StatusPingPacket.class, StatusPingPacket::new, - new StateRegistry.PacketMapping(0x00, MINECRAFT_1_13, null, false))); + () -> registry.forVersion(MINECRAFT_1_12).getPacketId(new StatusPingPacket(0L))); } + // ==================== Edge Cases and Boundaries ==================== + @Test void shouldNotFailWhenRegisterLatestProtocolVersion() { - StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( - ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); - assertDoesNotThrow(() -> registry.register(HandshakePacket.class, HandshakePacket::new, - new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false), - new StateRegistry.PacketMapping(0x01, getLast(ProtocolVersion.SUPPORTED_VERSIONS), - null, false))); + assertDoesNotThrow(() -> MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_8, MINECRAFT_1_14_4, 0x00), + VersionRange.of(getLast(ProtocolVersion.SUPPORTED_VERSIONS), 0x01)) + .build()); } @Test - void registrySuppliesCorrectPacketsByProtocol() { - StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( - ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); - registry.register(HandshakePacket.class, HandshakePacket::new, - new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, null, false), - new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12_1, null, false), - new StateRegistry.PacketMapping(0x02, MINECRAFT_1_13, null, false)); - assertEquals(HandshakePacket.class, - registry.getProtocolRegistry(MINECRAFT_1_12).createPacket(0x00).getClass()); - assertEquals(HandshakePacket.class, - registry.getProtocolRegistry(MINECRAFT_1_12_1).createPacket(0x01).getClass()); - assertEquals(HandshakePacket.class, - registry.getProtocolRegistry(MINECRAFT_1_12_2).createPacket(0x01).getClass()); - assertEquals(HandshakePacket.class, - registry.getProtocolRegistry(MINECRAFT_1_13).createPacket(0x02).getClass()); - assertEquals(HandshakePacket.class, - registry.getProtocolRegistry(MINECRAFT_1_14_2).createPacket(0x02).getClass()); + void versionBoundaryTransition() { + // Test that version boundaries are handled correctly + MultiVersionPacketRegistry registry = MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_12, MINECRAFT_1_12, 0x00), + VersionRange.of(MINECRAFT_1_13, MAXIMUM_VERSION, 0x01)) + .build(); + + // 1.12 should have ID 0x00 + assertEquals(0x00, registry.forVersion(MINECRAFT_1_12).getPacketId(HandshakePacket.DEFAULT)); + // 1.13 should have ID 0x01 + assertEquals(0x01, registry.forVersion(MINECRAFT_1_13).getPacketId(HandshakePacket.DEFAULT)); + } + + @Test + void multiplePacketsInSingleRegistry() { + // Test registry with multiple packet types + MultiVersionPacketRegistry registry = MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_12, 0x00)) + .register(StatusPingPacket.class, new StatusPingPacket.Codec(), + VersionRange.of(MINECRAFT_1_12, 0x01)) + .build(); + + assertEquals(0x00, registry.forVersion(MINECRAFT_1_12).getPacketId(HandshakePacket.DEFAULT)); + assertEquals(0x01, registry.forVersion(MINECRAFT_1_12).getPacketId(new StatusPingPacket(0L))); + assertNotNull(registry.forVersion(MINECRAFT_1_12).getCodec(0x00)); + assertNotNull(registry.forVersion(MINECRAFT_1_12).getCodec(0x01)); + } + + // ==================== Encode-Only Tests ==================== + + @Test + void encodeOnlyPacketsCanBeEncoded() { + // Encode-only packets should be decodable by class but not by ID + MultiVersionPacketRegistry registry = MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.encodeOnly(MINECRAFT_1_12, 0x00)) + .build(); + + // Should be able to get codec by class + assertNotNull(registry.forVersion(MINECRAFT_1_12).getCodec(HandshakePacket.class)); + // Should NOT be able to get codec by ID + assertNull(registry.forVersion(MINECRAFT_1_12).getCodec(0x00)); + } + + @Test + void encodeOnlyPacketMultipleVersions() { + MultiVersionPacketRegistry registry = MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.encodeOnly(MINECRAFT_1_8, MINECRAFT_1_12, 0x00), + VersionRange.of(MINECRAFT_1_13, 0x01)) + .build(); + + // 1.8-1.12 should be encode-only (no decode by ID) + assertNull(registry.forVersion(MINECRAFT_1_12).getCodec(0x00)); + assertNotNull(registry.forVersion(MINECRAFT_1_12).getCodec(HandshakePacket.class)); + + // 1.13+ should be decodable by ID + assertNotNull(registry.forVersion(MINECRAFT_1_13).getCodec(0x01)); + } + + // ==================== Codec Retrieval Tests ==================== + + @Test + void getCodecByClassWorks() { + MultiVersionPacketRegistry registry = setupRegistry(); + PacketCodec codec = registry.forVersion(MINECRAFT_1_12) + .getCodec(HandshakePacket.class); + assertNotNull(codec, "Should be able to retrieve codec by class"); + assertEquals(HandshakePacket.Codec.class, codec.getClass()); + } + + @Test + void getCodecByClassReturnsNullForUnregisteredPacket() { + MultiVersionPacketRegistry registry = setupRegistry(); + PacketCodec codec = registry.forVersion(MINECRAFT_1_12) + .getCodec(StatusPingPacket.class); + assertNull(codec, "Should return null for unregistered packet class"); + } + + @Test + void getCodecByIdReturnsNullForInvalidId() { + MultiVersionPacketRegistry registry = setupRegistry(); + PacketCodec codec = registry.forVersion(MINECRAFT_1_12).getCodec(0xFF); + assertNull(codec, "Should return null for invalid packet ID"); + } + + // ==================== CanDecodePacket Tests ==================== + + @Test + void canDecodePacketReturnsTrue() { + MultiVersionPacketRegistry registry = setupRegistry(); + assertTrue(registry.forVersion(MINECRAFT_1_12).canDecodePacket(HandshakePacket.DEFAULT), + "Should be able to decode HandshakePacket in 1.12"); + } + + @Test + void canDecodePacketReturnsFalse() { + MultiVersionPacketRegistry registry = setupRegistry(); + assertFalse(registry.forVersion(MINECRAFT_1_12).canDecodePacket(new StatusPingPacket(0L)), + "Should not be able to decode StatusPingPacket in 1.12"); + } + + @Test + void encodeOnlyPacketCanBeIdentified() { + MultiVersionPacketRegistry registry = MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.encodeOnly(MINECRAFT_1_12, 0x00)) + .build(); + + // Encode-only packets can still be identified by class for encoding purposes + assertTrue(registry.forVersion(MINECRAFT_1_12).canDecodePacket(HandshakePacket.DEFAULT), + "Encode-only packets can be identified for encoding"); + // But cannot be decoded by ID (not in packetIdToCodec) + assertNull(registry.forVersion(MINECRAFT_1_12).getCodec(0x00), + "Encode-only packets cannot be decoded from ID"); + } + + // ==================== SimplePacketRegistry Tests ==================== + + @Test + void simplePacketRegistryWorks() { + SimplePacketRegistry registry = new SimplePacketRegistry(ProtocolUtils.Direction.CLIENTBOUND); + registry.register(0x00, HandshakePacket.class, new HandshakePacket.Codec()); + + PacketCodec codec = registry.getCodec(0x00); + assertNotNull(codec, "Should retrieve codec by ID"); + assertEquals(HandshakePacket.Codec.class, codec.getClass()); + } + + @Test + void simplePacketRegistryCodecByClass() { + SimplePacketRegistry registry = new SimplePacketRegistry(ProtocolUtils.Direction.CLIENTBOUND); + registry.register(0x00, HandshakePacket.class, new HandshakePacket.Codec()); + + PacketCodec codec = registry.getCodec(HandshakePacket.class); + assertNotNull(codec, "Should retrieve codec by class"); + assertEquals(HandshakePacket.Codec.class, codec.getClass()); + } + + @Test + void simplePacketRegistryGetPacketId() { + SimplePacketRegistry registry = new SimplePacketRegistry(ProtocolUtils.Direction.CLIENTBOUND); + registry.register(0x42, HandshakePacket.class, new HandshakePacket.Codec()); + + int id = registry.getPacketId(HandshakePacket.DEFAULT); + assertEquals(0x42, id, "Should return correct packet ID"); + } + + @Test + void simplePacketRegistryFailsOnUnregisteredPacket() { + SimplePacketRegistry registry = new SimplePacketRegistry(ProtocolUtils.Direction.CLIENTBOUND); + registry.register(0x00, HandshakePacket.class, new HandshakePacket.Codec()); + + assertThrows(IllegalArgumentException.class, + () -> registry.getPacketId(new StatusPingPacket(0L)), + "Should fail when packet is not registered"); + } + + @Test + void simplePacketRegistryCanDecodePacket() { + SimplePacketRegistry registry = new SimplePacketRegistry(ProtocolUtils.Direction.CLIENTBOUND); + registry.register(0x00, HandshakePacket.class, new HandshakePacket.Codec()); + + assertTrue(registry.canDecodePacket(HandshakePacket.DEFAULT), + "Should be able to decode registered packet"); + assertFalse(registry.canDecodePacket(new StatusPingPacket(0L)), + "Should not be able to decode unregistered packet"); + } + + // ==================== Direction Tests ==================== + + @Test + void registriesWithDifferentDirections() { + // CLIENTBOUND registry + MultiVersionPacketRegistry clientbound = MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_12, 0x00)) + .build(); + + // SERVERBOUND registry with different ID + MultiVersionPacketRegistry serverbound = MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.SERVERBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_12, 0x01)) + .build(); + + assertEquals(0x00, clientbound.forVersion(MINECRAFT_1_12).getPacketId(HandshakePacket.DEFAULT)); + assertEquals(0x01, serverbound.forVersion(MINECRAFT_1_12).getPacketId(HandshakePacket.DEFAULT)); + } + + // ==================== Wide Version Range Tests ==================== + + @Test + void wideVersionRangeSpanningManyVersions() { + // Register packet across many versions with same ID + MultiVersionPacketRegistry registry = MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_8, MAXIMUM_VERSION, 0x00)) + .build(); + + // Should work for all supported versions in range + assertEquals(0x00, registry.forVersion(MINECRAFT_1_8).getPacketId(HandshakePacket.DEFAULT)); + assertEquals(0x00, registry.forVersion(MINECRAFT_1_12).getPacketId(HandshakePacket.DEFAULT)); + assertEquals(0x00, registry.forVersion(MINECRAFT_1_16_2).getPacketId(HandshakePacket.DEFAULT)); + } + + @Test + void nullEndVersionRangeHandling() { + // Test with null end version (should extend to MAXIMUM_VERSION) + MultiVersionPacketRegistry registry = MultiVersionPacketRegistry.builder(ProtocolUtils.Direction.CLIENTBOUND) + .register(HandshakePacket.class, new HandshakePacket.Codec(), + VersionRange.of(MINECRAFT_1_15, 0x00)) + .build(); + + assertEquals(0x00, registry.forVersion(MINECRAFT_1_15).getPacketId(HandshakePacket.DEFAULT)); + assertEquals(0x00, registry.forVersion(MINECRAFT_1_16_2).getPacketId(HandshakePacket.DEFAULT)); } } \ No newline at end of file