Make packets immutable

This commit is contained in:
Andrew Steinborn
2025-10-18 22:05:18 -04:00
parent 67b988e6d2
commit 287a9b28f1
98 changed files with 4132 additions and 4068 deletions

View File

@@ -69,7 +69,7 @@ public final class VelocityBossBarImplementation implements BossBar.Listener,
public boolean viewerRemove(final ConnectedPlayer viewer) { public boolean viewerRemove(final ConnectedPlayer viewer) {
if (this.viewers.remove(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 true;
} }
return false; return false;

View File

@@ -55,6 +55,7 @@ import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.ClickEvent;
@@ -70,6 +71,7 @@ import org.apache.logging.log4j.Logger;
*/ */
public final class VelocityCommand { public final class VelocityCommand {
private static final String USAGE = "/velocity <%s>"; private static final String USAGE = "/velocity <%s>";
private static final JoinConfiguration COMMA_JOINER = JoinConfiguration.commas(true);
@SuppressWarnings("checkstyle:MissingJavadocMethod") @SuppressWarnings("checkstyle:MissingJavadocMethod")
public static BrigadierCommand create(final VelocityServer server) { public static BrigadierCommand create(final VelocityServer server) {
@@ -208,19 +210,14 @@ public final class VelocityCommand {
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
} }
final TextComponent.Builder listBuilder = Component.text(); final Component pluginListComponents = plugins.stream()
for (int i = 0; i < pluginCount; i++) { .map(container -> componentForPlugin(container.getDescription()))
final PluginContainer plugin = plugins.get(i); .collect(Component.toComponent(Component.text(", ")));
listBuilder.append(componentForPlugin(plugin.getDescription()));
if (i + 1 < pluginCount) {
listBuilder.append(Component.text(", "));
}
}
final TranslatableComponent output = Component.translatable() final TranslatableComponent output = Component.translatable()
.key("velocity.command.plugins-list") .key("velocity.command.plugins-list")
.color(NamedTextColor.YELLOW) .color(NamedTextColor.YELLOW)
.arguments(listBuilder.build()) .arguments(pluginListComponents)
.build(); .build();
source.sendMessage(output); source.sendMessage(output);
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;

View File

@@ -420,7 +420,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(ClientboundStoreCookiePacket packet) { public boolean handle(ClientboundStoreCookiePacket packet) {
server.getEventManager() server.getEventManager()
.fire(new CookieStoreEvent(serverConn.getPlayer(), packet.getKey(), packet.getPayload())) .fire(new CookieStoreEvent(serverConn.getPlayer(), packet.key(), packet.payload()))
.thenAcceptAsync(event -> { .thenAcceptAsync(event -> {
if (event.getResult().isAllowed()) { if (event.getResult().isAllowed()) {
final Key resultedKey = event.getResult().getKey() == null final Key resultedKey = event.getResult().getKey() == null

View File

@@ -328,7 +328,7 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(ClientboundStoreCookiePacket packet) { public boolean handle(ClientboundStoreCookiePacket packet) {
server.getEventManager() server.getEventManager()
.fire(new CookieStoreEvent(serverConn.getPlayer(), packet.getKey(), packet.getPayload())) .fire(new CookieStoreEvent(serverConn.getPlayer(), packet.key(), packet.payload()))
.thenAcceptAsync(event -> { .thenAcceptAsync(event -> {
if (event.getResult().isAllowed()) { if (event.getResult().isAllowed()) {
final Key resultedKey = event.getResult().getKey() == null final Key resultedKey = event.getResult().getKey() == null

View File

@@ -157,7 +157,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
if (smc.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) { if (smc.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
smc.setActiveSessionHandler(StateRegistry.PLAY, new TransitionSessionHandler(server, serverConn, resultFuture)); smc.setActiveSessionHandler(StateRegistry.PLAY, new TransitionSessionHandler(server, serverConn, resultFuture));
} else { } else {
smc.write(new LoginAcknowledgedPacket()); smc.write(LoginAcknowledgedPacket.INSTANCE);
smc.setActiveSessionHandler(StateRegistry.CONFIG, new ConfigSessionHandler(server, serverConn, resultFuture)); smc.setActiveSessionHandler(StateRegistry.CONFIG, new ConfigSessionHandler(server, serverConn, resultFuture));
ConnectedPlayer player = serverConn.getPlayer(); ConnectedPlayer player = serverConn.getPlayer();
if (player.getClientSettingsPacket() != null) { if (player.getClientSettingsPacket() != null) {

View File

@@ -170,26 +170,26 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
.orElseGet(() -> registeredServer.getServerInfo().getAddress()) .orElseGet(() -> registeredServer.getServerInfo().getAddress())
.getHostString(); .getHostString();
HandshakePacket handshake = new HandshakePacket(); String serverAddress;
handshake.setIntent(HandshakeIntent.LOGIN);
handshake.setProtocolVersion(protocolVersion);
if (forwardingMode == PlayerInfoForwarding.LEGACY) { if (forwardingMode == PlayerInfoForwarding.LEGACY) {
handshake.setServerAddress(createLegacyForwardingAddress()); serverAddress = createLegacyForwardingAddress();
} else if (forwardingMode == PlayerInfoForwarding.BUNGEEGUARD) { } else if (forwardingMode == PlayerInfoForwarding.BUNGEEGUARD) {
byte[] secret = server.getConfiguration().getForwardingSecret(); byte[] secret = server.getConfiguration().getForwardingSecret();
handshake.setServerAddress(createBungeeGuardForwardingAddress(secret)); serverAddress = createBungeeGuardForwardingAddress(secret);
} else if (proxyPlayer.getConnection().getType() == ConnectionTypes.LEGACY_FORGE) { } 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) { } else if (proxyPlayer.getConnection().getType() instanceof ModernForgeConnectionType) {
handshake.setServerAddress(playerVhost + ((ModernForgeConnectionType) proxyPlayer serverAddress = playerVhost + ((ModernForgeConnectionType) proxyPlayer
.getConnection().getType()).getModernToken()); .getConnection().getType()).getModernToken();
} else { } else {
handshake.setServerAddress(playerVhost); serverAddress = playerVhost;
} }
handshake.setPort(proxyPlayer.getVirtualHost() int port = proxyPlayer.getVirtualHost()
.orElseGet(() -> registeredServer.getServerInfo().getAddress()) .orElseGet(() -> registeredServer.getServerInfo().getAddress())
.getPort()); .getPort();
HandshakePacket handshake = new HandshakePacket(protocolVersion, serverAddress, port, HandshakeIntent.LOGIN);
mc.delayedWrite(handshake); mc.delayedWrite(handshake);
mc.setProtocolVersion(protocolVersion); mc.setProtocolVersion(protocolVersion);

View File

@@ -230,10 +230,9 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
return; return;
} }
ServerLoginSuccessPacket success = new ServerLoginSuccessPacket(); ServerLoginSuccessPacket success = new ServerLoginSuccessPacket(
success.setUsername(player.getUsername()); player.getUniqueId(), player.getUsername(), player.getGameProfileProperties()
success.setProperties(player.getGameProfileProperties()); );
success.setUuid(player.getUniqueId());
mcConnection.write(success); mcConnection.write(success);
loginState = State.SUCCESS_SENT; loginState = State.SUCCESS_SENT;

View File

@@ -387,8 +387,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
public boolean handle(ResourcePackResponsePacket packet) { public boolean handle(ResourcePackResponsePacket packet) {
return player.resourcePackHandler().onResourcePackResponse( return player.resourcePackHandler().onResourcePackResponse(
new ResourcePackResponseBundle(packet.getId(), new ResourcePackResponseBundle(packet.getId(),
packet.getHash(), packet.hash(),
packet.getStatus())); packet.status()));
} }
@Override @Override
@@ -428,7 +428,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(ServerboundCookieResponsePacket packet) { public boolean handle(ServerboundCookieResponsePacket packet) {
server.getEventManager() server.getEventManager()
.fire(new CookieReceiveEvent(player, packet.getKey(), packet.getPayload())) .fire(new CookieReceiveEvent(player, packet.key(), packet.payload()))
.thenAcceptAsync(event -> { .thenAcceptAsync(event -> {
if (event.getResult().isAllowed()) { if (event.getResult().isAllowed()) {
final VelocityServerConnection serverConnection = player.getConnectedServer(); final VelocityServerConnection serverConnection = player.getConnectedServer();
@@ -586,10 +586,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// Remove previous boss bars. These don't get cleared when sending JoinGame (up until 1.20.2), // Remove previous boss bars. These don't get cleared when sending JoinGame (up until 1.20.2),
// thus the need to track them. // thus the need to track them.
for (UUID serverBossBar : serverBossBars) { for (UUID serverBossBar : serverBossBars) {
BossBarPacket deletePacket = new BossBarPacket(); player.getConnection().delayedWrite(BossBarPacket.createRemovePacket(serverBossBar));
deletePacket.setUuid(serverBossBar);
deletePacket.setAction(BossBarPacket.REMOVE);
player.getConnection().delayedWrite(deletePacket);
} }
serverBossBars.clear(); serverBossBars.clear();
} }
@@ -615,7 +612,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// Clear any title from the previous server. // Clear any title from the previous server.
if (player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) { if (player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
player.getConnection().delayedWrite( player.getConnection().delayedWrite(
GenericTitlePacket.constructTitlePacket(GenericTitlePacket.ActionType.RESET, GenericTitlePacket.createClearTitlePacket(GenericTitlePacket.ActionType.RESET,
player.getProtocolVersion())); player.getProtocolVersion()));
} }
@@ -634,14 +631,15 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// Most notably, by having the client accept the join game packet, we can work around the need // 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 // to perform entity ID rewrites, eliminating potential issues from rewriting packets and
// improving compatibility with mods. // improving compatibility with mods.
final RespawnPacket respawn = RespawnPacket.fromJoinGame(joinGame); int dim = joinGame.getDimension();
if (player.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_16)) { if (player.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_16)) {
// Before Minecraft 1.16, we could not switch to the same dimension without sending an // 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 // additional respawn. On older versions of Minecraft this forces the client to perform
// garbage collection which adds additional latency. // 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(joinGame);
player.getConnection().delayedWrite(respawn); player.getConnection().delayedWrite(respawn);
} }
@@ -655,8 +653,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
player.getConnection().delayedWrite(joinGame); player.getConnection().delayedWrite(joinGame);
// Send a respawn packet in a different dimension. // Send a respawn packet in a different dimension.
final RespawnPacket fakeSwitchPacket = RespawnPacket.fromJoinGame(joinGame); final RespawnPacket fakeSwitchPacket = RespawnPacket.fromJoinGame(joinGame, joinGame.getDimension() == 0 ? -1 : 0);
fakeSwitchPacket.setDimension(joinGame.getDimension() == 0 ? -1 : 0);
player.getConnection().delayedWrite(fakeSwitchPacket); player.getConnection().delayedWrite(fakeSwitchPacket);
// Now send a respawn packet in the correct dimension. // Now send a respawn packet in the correct dimension.
@@ -728,11 +725,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
offers.add(new Offer(offer, tooltip)); offers.add(new Offer(offer, tooltip));
} }
TabCompleteResponsePacket resp = new TabCompleteResponsePacket(); TabCompleteResponsePacket resp = new TabCompleteResponsePacket(
resp.setTransactionId(packet.getTransactionId()); packet.transactionId(), startPos + 1, packet.getCommand().length() - startPos - 1, offers
resp.setStart(startPos + 1); );
resp.setLength(packet.getCommand().length() - startPos - 1);
resp.getOffers().addAll(offers);
player.getConnection().write(resp); player.getConnection().write(resp);
} }
}, player.getConnection().eventLoop()).exceptionally((ex) -> { }, player.getConnection().eventLoop()).exceptionally((ex) -> {
@@ -778,6 +773,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
.thenAcceptAsync(offers -> { .thenAcceptAsync(offers -> {
boolean legacy = boolean legacy =
player.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_13); player.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_13);
List<Offer> extendedOffers = new ArrayList<>(response.offers());
try { try {
for (Suggestion suggestion : offers.getList()) { for (Suggestion suggestion : offers.getList()) {
String offer = suggestion.getText(); String offer = suggestion.getText();
@@ -791,10 +787,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
} else if (suggestion.getTooltip() != null) { } else if (suggestion.getTooltip() != null) {
tooltip = new ComponentHolder(player.getProtocolVersion(), Component.text(suggestion.getTooltip().getString())); 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); extendedOffers.sort(null);
player.getConnection().write(response); player.getConnection().write(response.withOffers(extendedOffers));
} catch (Exception e) { } catch (Exception e) {
logger.error("Unable to provide tab list completions for {} for command '{}'", logger.error("Unable to provide tab list completions for {} for command '{}'",
player.getUsername(), command, player.getUsername(), command,
@@ -811,17 +807,17 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
private void finishRegularTabComplete(TabCompleteRequestPacket request, private void finishRegularTabComplete(TabCompleteRequestPacket request,
TabCompleteResponsePacket response) { TabCompleteResponsePacket response) {
List<String> offers = new ArrayList<>(); List<String> textOffers = new ArrayList<>();
for (Offer offer : response.getOffers()) { for (Offer offer : response.offers()) {
offers.add(offer.getText()); textOffers.add(offer.getText());
} }
server.getEventManager().fire(new TabCompleteEvent(player, request.getCommand(), offers)) server.getEventManager().fire(new TabCompleteEvent(player, request.getCommand(), textOffers))
.thenAcceptAsync(e -> { .thenAcceptAsync(e -> {
response.getOffers().clear(); List<Offer> newOffers = new ArrayList<>();
for (String s : e.getSuggestions()) { 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) -> { }, player.getConnection().eventLoop()).exceptionally((ex) -> {
logger.error( logger.error(
"Exception while finishing regular tab completion," "Exception while finishing regular tab completion,"

View File

@@ -38,25 +38,25 @@ public class ClientSettingsWrapper implements PlayerSettings {
ClientSettingsWrapper(ClientSettingsPacket settings) { ClientSettingsWrapper(ClientSettingsPacket settings) {
this.settings = settings; this.settings = settings;
this.parts = new SkinParts((byte) settings.getSkinParts()); this.parts = new SkinParts((byte) settings.skinParts());
} }
@Override @Override
public Locale getLocale() { public Locale getLocale() {
if (locale == null) { if (locale == null) {
locale = Locale.forLanguageTag(settings.getLocale().replaceAll("_", "-")); locale = Locale.forLanguageTag(settings.locale().replaceAll("_", "-"));
} }
return locale; return locale;
} }
@Override @Override
public byte getViewDistance() { public byte getViewDistance() {
return settings.getViewDistance(); return settings.viewDistance();
} }
@Override @Override
public ChatMode getChatMode() { public ChatMode getChatMode() {
return switch (settings.getChatVisibility()) { return switch (settings.chatVisibility()) {
case 1 -> ChatMode.COMMANDS_ONLY; case 1 -> ChatMode.COMMANDS_ONLY;
case 2 -> ChatMode.HIDDEN; case 2 -> ChatMode.HIDDEN;
default -> ChatMode.SHOWN; default -> ChatMode.SHOWN;
@@ -65,7 +65,7 @@ public class ClientSettingsWrapper implements PlayerSettings {
@Override @Override
public boolean hasChatColors() { public boolean hasChatColors() {
return settings.isChatColors(); return settings.chatColors();
} }
@Override @Override
@@ -75,22 +75,22 @@ public class ClientSettingsWrapper implements PlayerSettings {
@Override @Override
public MainHand getMainHand() { public MainHand getMainHand() {
return settings.getMainHand() == 1 ? MainHand.RIGHT : MainHand.LEFT; return settings.mainHand() == 1 ? MainHand.RIGHT : MainHand.LEFT;
} }
@Override @Override
public boolean isClientListingAllowed() { public boolean isClientListingAllowed() {
return settings.isClientListingAllowed(); return settings.clientListingAllowed();
} }
@Override @Override
public boolean isTextFilteringEnabled() { public boolean isTextFilteringEnabled() {
return settings.isTextFilteringEnabled(); return settings.textFilteringEnabled();
} }
@Override @Override
public ParticleStatus getParticleStatus() { public ParticleStatus getParticleStatus() {
return switch (settings.getParticleStatus()) { return switch (settings.particleStatus()) {
case 1 -> ParticleStatus.DECREASED; case 1 -> ParticleStatus.DECREASED;
case 2 -> ParticleStatus.MINIMAL; case 2 -> ParticleStatus.MINIMAL;
default -> ParticleStatus.ALL; default -> ParticleStatus.ALL;

View File

@@ -451,9 +451,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
ProtocolVersion playerVersion = getProtocolVersion(); ProtocolVersion playerVersion = getProtocolVersion();
if (playerVersion.noLessThan(ProtocolVersion.MINECRAFT_1_11)) { if (playerVersion.noLessThan(ProtocolVersion.MINECRAFT_1_11)) {
// Use the title packet instead. // Use the title packet instead.
GenericTitlePacket pkt = GenericTitlePacket.constructTitlePacket( GenericTitlePacket pkt = GenericTitlePacket.createComponentTitlePacket(
GenericTitlePacket.ActionType.SET_ACTION_BAR, playerVersion); GenericTitlePacket.ActionType.SET_ACTION_BAR,
pkt.setComponent(new ComponentHolder(playerVersion, translated)); new ComponentHolder(playerVersion, translated), playerVersion);
connection.write(pkt); connection.write(pkt);
} else { } else {
// Due to issues with action bar packets, we'll need to convert the text message into a // Due to issues with action bar packets, we'll need to convert the text message into a
@@ -461,9 +461,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
JsonObject object = new JsonObject(); JsonObject object = new JsonObject();
object.addProperty("text", LegacyComponentSerializer.legacySection() object.addProperty("text", LegacyComponentSerializer.legacySection()
.serialize(translated)); .serialize(translated));
LegacyChatPacket legacyChat = new LegacyChatPacket(); LegacyChatPacket legacyChat = new LegacyChatPacket(object.toString(), LegacyChatPacket.GAME_INFO_TYPE, null);
legacyChat.setMessage(object.toString());
legacyChat.setType(LegacyChatPacket.GAME_INFO_TYPE);
connection.write(legacyChat); connection.write(legacyChat);
} }
} }
@@ -504,26 +502,26 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
@Override @Override
public void showTitle(net.kyori.adventure.title.@NonNull Title title) { public void showTitle(net.kyori.adventure.title.@NonNull Title title) {
if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) { 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(); net.kyori.adventure.title.Title.Times times = title.times();
if (times != null) { if (times != null) {
timesPkt.setFadeIn((int) DurationUtils.toTicks(times.fadeIn())); GenericTitlePacket timesPkt = GenericTitlePacket.createTimesTitlePacket(
timesPkt.setStay((int) DurationUtils.toTicks(times.stay())); (int) DurationUtils.toTicks(times.fadeIn()),
timesPkt.setFadeOut((int) DurationUtils.toTicks(times.fadeOut())); (int) DurationUtils.toTicks(times.stay()),
(int) DurationUtils.toTicks(times.fadeOut()),
this.getProtocolVersion());
connection.delayedWrite(timesPkt);
} }
connection.delayedWrite(timesPkt);
GenericTitlePacket subtitlePkt = GenericTitlePacket.constructTitlePacket( GenericTitlePacket subtitlePkt = GenericTitlePacket.createComponentTitlePacket(
GenericTitlePacket.ActionType.SET_SUBTITLE, this.getProtocolVersion()); GenericTitlePacket.ActionType.SET_SUBTITLE,
subtitlePkt.setComponent(new ComponentHolder( new ComponentHolder(this.getProtocolVersion(), translateMessage(title.subtitle())),
this.getProtocolVersion(), translateMessage(title.subtitle()))); this.getProtocolVersion());
connection.delayedWrite(subtitlePkt); connection.delayedWrite(subtitlePkt);
GenericTitlePacket titlePkt = GenericTitlePacket.constructTitlePacket( GenericTitlePacket titlePkt = GenericTitlePacket.createComponentTitlePacket(
GenericTitlePacket.ActionType.SET_TITLE, this.getProtocolVersion()); GenericTitlePacket.ActionType.SET_TITLE,
titlePkt.setComponent(new ComponentHolder( new ComponentHolder(this.getProtocolVersion(), translateMessage(title.title())),
this.getProtocolVersion(), translateMessage(title.title()))); this.getProtocolVersion());
connection.delayedWrite(titlePkt); connection.delayedWrite(titlePkt);
connection.flush(); connection.flush();
@@ -545,24 +543,24 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
} }
if (part == TitlePart.TITLE) { if (part == TitlePart.TITLE) {
GenericTitlePacket titlePkt = GenericTitlePacket.constructTitlePacket( GenericTitlePacket titlePkt = GenericTitlePacket.createComponentTitlePacket(
GenericTitlePacket.ActionType.SET_TITLE, this.getProtocolVersion()); GenericTitlePacket.ActionType.SET_TITLE,
titlePkt.setComponent(new ComponentHolder( new ComponentHolder(this.getProtocolVersion(), translateMessage((Component) value)),
this.getProtocolVersion(), translateMessage((Component) value))); this.getProtocolVersion());
connection.write(titlePkt); connection.write(titlePkt);
} else if (part == TitlePart.SUBTITLE) { } else if (part == TitlePart.SUBTITLE) {
GenericTitlePacket titlePkt = GenericTitlePacket.constructTitlePacket( GenericTitlePacket titlePkt = GenericTitlePacket.createComponentTitlePacket(
GenericTitlePacket.ActionType.SET_SUBTITLE, this.getProtocolVersion()); GenericTitlePacket.ActionType.SET_SUBTITLE,
titlePkt.setComponent(new ComponentHolder( new ComponentHolder(this.getProtocolVersion(), translateMessage((Component) value)),
this.getProtocolVersion(), translateMessage((Component) value))); this.getProtocolVersion());
connection.write(titlePkt); connection.write(titlePkt);
} else if (part == TitlePart.TIMES) { } else if (part == TitlePart.TIMES) {
Times times = (Times) value; Times times = (Times) value;
GenericTitlePacket timesPkt = GenericTitlePacket.constructTitlePacket( GenericTitlePacket timesPkt = GenericTitlePacket.createTimesTitlePacket(
GenericTitlePacket.ActionType.SET_TIMES, this.getProtocolVersion()); (int) DurationUtils.toTicks(times.fadeIn()),
timesPkt.setFadeIn((int) DurationUtils.toTicks(times.fadeIn())); (int) DurationUtils.toTicks(times.stay()),
timesPkt.setStay((int) DurationUtils.toTicks(times.stay())); (int) DurationUtils.toTicks(times.fadeOut()),
timesPkt.setFadeOut((int) DurationUtils.toTicks(times.fadeOut())); this.getProtocolVersion());
connection.write(timesPkt); connection.write(timesPkt);
} else { } else {
throw new IllegalArgumentException("Title part " + part + " is not valid"); throw new IllegalArgumentException("Title part " + part + " is not valid");
@@ -572,7 +570,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
@Override @Override
public void clearTitle() { public void clearTitle() {
if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) { if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
connection.write(GenericTitlePacket.constructTitlePacket( connection.write(GenericTitlePacket.createClearTitlePacket(
GenericTitlePacket.ActionType.HIDE, this.getProtocolVersion())); GenericTitlePacket.ActionType.HIDE, this.getProtocolVersion()));
} }
} }
@@ -580,7 +578,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
@Override @Override
public void resetTitle() { public void resetTitle() {
if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) { if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
connection.write(GenericTitlePacket.constructTitlePacket( connection.write(GenericTitlePacket.createClearTitlePacket(
GenericTitlePacket.ActionType.RESET, this.getProtocolVersion())); GenericTitlePacket.ActionType.RESET, this.getProtocolVersion()));
} }
} }
@@ -1262,7 +1260,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
@Override @Override
public void clearResourcePacks() { public void clearResourcePacks() {
if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) {
connection.write(new RemoveResourcePackPacket()); connection.write(new RemoveResourcePackPacket(null));
this.resourcePackHandler.clearAppliedResourcePacks(); this.resourcePackHandler.clearAppliedResourcePacks();
} }
} }
@@ -1333,8 +1331,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
public void sendKeepAlive() { public void sendKeepAlive() {
if (connection.getState() == StateRegistry.PLAY if (connection.getState() == StateRegistry.PLAY
|| connection.getState() == StateRegistry.CONFIG) { || connection.getState() == StateRegistry.CONFIG) {
KeepAlivePacket keepAlive = new KeepAlivePacket(); KeepAlivePacket keepAlive = new KeepAlivePacket(ThreadLocalRandom.current().nextLong());
keepAlive.setRandomId(ThreadLocalRandom.current().nextLong());
connection.write(keepAlive); connection.write(keepAlive);
} }
} }

View File

@@ -87,19 +87,19 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(final HandshakePacket handshake) { public boolean handle(final HandshakePacket handshake) {
final StateRegistry nextState = getStateForProtocol(handshake.getNextStatus()); final StateRegistry nextState = getStateForProtocol(handshake.nextStatus());
if (nextState == null) { if (nextState == null) {
LOGGER.error("{} provided invalid protocol {}", this, handshake.getNextStatus()); LOGGER.error("{} provided invalid protocol {}", this, handshake.nextStatus());
connection.close(true); connection.close(true);
} else { } else {
final InitialInboundConnection ic = new InitialInboundConnection(connection, final InitialInboundConnection ic = new InitialInboundConnection(connection,
cleanVhost(handshake.getServerAddress()), handshake); cleanVhost(handshake.serverAddress()), handshake);
if (handshake.getIntent() == HandshakeIntent.TRANSFER if (handshake.intent() == HandshakeIntent.TRANSFER
&& !server.getConfiguration().isAcceptTransfers()) { && !server.getConfiguration().isAcceptTransfers()) {
ic.disconnect(Component.translatable("multiplayer.disconnect.transfers_disabled")); ic.disconnect(Component.translatable("multiplayer.disconnect.transfers_disabled"));
return true; return true;
} }
connection.setProtocolVersion(handshake.getProtocolVersion()); connection.setProtocolVersion(handshake.protocolVersion());
connection.setAssociation(ic); connection.setAssociation(ic);
switch (nextState) { switch (nextState) {
@@ -124,7 +124,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
} }
private void handleLogin(HandshakePacket handshake, InitialInboundConnection ic) { 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. // Bump connection into correct protocol state so that we can send the disconnect packet.
connection.setState(StateRegistry.LOGIN); connection.setState(StateRegistry.LOGIN);
ic.disconnectQuietly(Component.translatable() ic.disconnectQuietly(Component.translatable()
@@ -147,7 +147,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
// If the proxy is configured for modern forwarding, we must deny connections from 1.12.2 // 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. // and lower, otherwise IP information will never get forwarded.
if (server.getConfiguration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN 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. // Bump connection into correct protocol state so that we can send the disconnect packet.
connection.setState(StateRegistry.LOGIN); connection.setState(StateRegistry.LOGIN);
ic.disconnectQuietly( ic.disconnectQuietly(
@@ -157,21 +157,23 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
final LoginInboundConnection lic = new LoginInboundConnection(ic); final LoginInboundConnection lic = new LoginInboundConnection(ic);
server.getEventManager().fireAndForget( server.getEventManager().fireAndForget(
new ConnectionHandshakeEvent(lic, handshake.getIntent())); new ConnectionHandshakeEvent(lic, handshake.intent()));
connection.setActiveSessionHandler(StateRegistry.LOGIN, connection.setActiveSessionHandler(StateRegistry.LOGIN,
new InitialLoginSessionHandler(server, connection, lic)); new InitialLoginSessionHandler(server, connection, lic));
} }
private ConnectionType getHandshakeConnectionType(HandshakePacket handshake) { private ConnectionType getHandshakeConnectionType(HandshakePacket handshake) {
if (handshake.getServerAddress().contains(ModernForgeConstants.MODERN_FORGE_TOKEN) final String serverAddress = handshake.serverAddress();
&& handshake.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { final ProtocolVersion protocolVersion = handshake.protocolVersion();
return new ModernForgeConnectionType(handshake.getServerAddress()); 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). // 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) if (serverAddress.endsWith(LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN)
&& handshake.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_13)) { && protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_13)) {
return ConnectionTypes.LEGACY_FORGE; 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 // 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. // forge handshake attempts. Also sends a reset handshake packet on every transition.
return ConnectionTypes.UNDETERMINED_17; return ConnectionTypes.UNDETERMINED_17;

View File

@@ -91,7 +91,7 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
public boolean handle(ServerLoginPacket packet) { public boolean handle(ServerLoginPacket packet) {
assertState(LoginState.LOGIN_PACKET_EXPECTED); assertState(LoginState.LOGIN_PACKET_EXPECTED);
this.currentState = LoginState.LOGIN_PACKET_RECEIVED; this.currentState = LoginState.LOGIN_PACKET_RECEIVED;
IdentifiedKey playerKey = packet.getPlayerKey(); IdentifiedKey playerKey = packet.playerKey();
if (playerKey != null) { if (playerKey != null) {
if (playerKey.hasExpired()) { if (playerKey.hasExpired()) {
inbound.disconnect( inbound.disconnect(
@@ -102,7 +102,7 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
boolean isKeyValid; boolean isKeyValid;
if (playerKey.getKeyRevision() == IdentifiedKey.Revision.LINKED_V2 if (playerKey.getKeyRevision() == IdentifiedKey.Revision.LINKED_V2
&& playerKey instanceof final IdentifiedKeyImpl keyImpl) { && playerKey instanceof final IdentifiedKeyImpl keyImpl) {
isKeyValid = keyImpl.internalAddHolder(packet.getHolderUuid()); isKeyValid = keyImpl.internalAddHolder(packet.holderUuid());
} else { } else {
isKeyValid = playerKey.isSignatureValid(); isKeyValid = playerKey.isSignatureValid();
} }
@@ -120,7 +120,7 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
inbound.setPlayerKey(playerKey); inbound.setPlayerKey(playerKey);
this.login = packet; this.login = packet;
final PreLoginEvent event = new PreLoginEvent(inbound, login.getUsername(), login.getHolderUuid()); final PreLoginEvent event = new PreLoginEvent(inbound, login.getUsername(), login.holderUuid());
server.getEventManager().fire(event).thenRunAsync(() -> { server.getEventManager().fire(event).thenRunAsync(() -> {
if (mcConnection.isClosed()) { if (mcConnection.isClosed()) {
// The player was disconnected // The player was disconnected
@@ -289,9 +289,8 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
byte[] verify = new byte[4]; byte[] verify = new byte[4];
ThreadLocalRandom.current().nextBytes(verify); ThreadLocalRandom.current().nextBytes(verify);
EncryptionRequestPacket request = new EncryptionRequestPacket(); EncryptionRequestPacket request = new EncryptionRequestPacket("",
request.setPublicKey(server.getServerKeyPair().getPublic().getEncoded()); server.getServerKeyPair().getPublic().getEncoded(), verify, true);
request.setVerifyToken(verify);
return request; return request;
} }

View File

@@ -84,9 +84,9 @@ class LegacyForgeUtil {
* @return A copy of the reset packet * @return A copy of the reset packet
*/ */
static PluginMessagePacket resetPacket() { static PluginMessagePacket resetPacket() {
PluginMessagePacket msg = new PluginMessagePacket(); return new PluginMessagePacket(
msg.setChannel(FORGE_LEGACY_HANDSHAKE_CHANNEL); FORGE_LEGACY_HANDSHAKE_CHANNEL,
msg.replace(Unpooled.wrappedBuffer(FORGE_LEGACY_HANDSHAKE_RESET_DATA.clone())); Unpooled.wrappedBuffer(FORGE_LEGACY_HANDSHAKE_RESET_DATA.clone())
return msg; );
} }
} }

View File

@@ -101,17 +101,15 @@ public abstract sealed class ResourcePackHandler
} }
protected void sendResourcePackRequestPacket(final @NotNull ResourcePackInfo queued) { protected void sendResourcePackRequestPacket(final @NotNull ResourcePackInfo queued) {
final ResourcePackRequestPacket request = new ResourcePackRequestPacket(); final String hash = queued.getHash() != null ? ByteBufUtil.hexDump(queued.getHash()) : "";
request.setId(queued.getId()); final ComponentHolder prompt = queued.getPrompt() == null ? null :
request.setUrl(queued.getUrl()); new ComponentHolder(player.getProtocolVersion(), player.translateMessage(queued.getPrompt()));
if (queued.getHash() != null) { final ResourcePackRequestPacket request = new ResourcePackRequestPacket(
request.setHash(ByteBufUtil.hexDump(queued.getHash())); queued.getId(),
} else { queued.getUrl(),
request.setHash(""); hash,
} queued.getShouldForce(),
request.setRequired(queued.getShouldForce()); prompt);
request.setPrompt(queued.getPrompt() == null ? null :
new ComponentHolder(player.getProtocolVersion(), player.translateMessage(queued.getPrompt())));
player.getConnection().write(request); player.getConnection().write(request);
} }

View File

@@ -17,33 +17,23 @@
package com.velocitypowered.proxy.protocol; package com.velocitypowered.proxy.protocol;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; 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 { public interface MinecraftPacket {
void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion); /**
* Handles this packet using the visitor pattern.
void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion); *
* @param handler the session handler
* @return true if the packet was handled
*/
boolean handle(MinecraftSessionHandler handler); 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;
}
} }

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol;
/**
* A combined codec that can both encode and decode packets.
*
* @param <T> the packet type
*/
public interface PacketCodec<T extends MinecraftPacket>
extends PacketEncoder<T>, PacketDecoder<T> {
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol;
import com.velocitypowered.api.network.ProtocolVersion;
import io.netty.buffer.ByteBuf;
/**
* Decodes a packet from a ByteBuf.
*
* @param <T> the packet type
*/
public interface PacketDecoder<T extends MinecraftPacket> {
/**
* 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;
}
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol;
import com.velocitypowered.api.network.ProtocolVersion;
import io.netty.buffer.ByteBuf;
/**
* Encodes a packet into a ByteBuf.
*
* @param <T> the packet type
*/
public interface PacketEncoder<T extends MinecraftPacket> {
/**
* 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;
}
}

View File

@@ -128,9 +128,9 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.Collections; import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Supplier;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
@@ -140,122 +140,122 @@ public enum StateRegistry {
HANDSHAKE { HANDSHAKE {
{ {
serverbound.register(HandshakePacket.class, HandshakePacket::new, serverbound.register(HandshakePacket.class, new HandshakePacket.Codec(),
map(0x00, MINECRAFT_1_7_2, false)); map(0x00, MINECRAFT_1_7_2, false));
} }
}, },
STATUS { STATUS {
{ {
serverbound.register( serverbound.register(
StatusRequestPacket.class, () -> StatusRequestPacket.INSTANCE, StatusRequestPacket.class, new StatusRequestPacket.Codec(),
map(0x00, MINECRAFT_1_7_2, false)); map(0x00, MINECRAFT_1_7_2, false));
serverbound.register(StatusPingPacket.class, StatusPingPacket::new, serverbound.register(StatusPingPacket.class, new StatusPingPacket.Codec(),
map(0x01, MINECRAFT_1_7_2, false)); map(0x01, MINECRAFT_1_7_2, false));
clientbound.register( clientbound.register(
StatusResponsePacket.class, StatusResponsePacket::new, StatusResponsePacket.class, new StatusResponsePacket.Codec(),
map(0x00, MINECRAFT_1_7_2, false)); map(0x00, MINECRAFT_1_7_2, false));
clientbound.register(StatusPingPacket.class, StatusPingPacket::new, clientbound.register(StatusPingPacket.class, new StatusPingPacket.Codec(),
map(0x01, MINECRAFT_1_7_2, false)); map(0x01, MINECRAFT_1_7_2, false));
} }
}, },
CONFIG { CONFIG {
{ {
serverbound.register( serverbound.register(
ClientSettingsPacket.class, ClientSettingsPacket::new, ClientSettingsPacket.class, new ClientSettingsPacket.Codec(),
map(0x00, MINECRAFT_1_20_2, false)); map(0x00, MINECRAFT_1_20_2, false));
serverbound.register( serverbound.register(
ServerboundCookieResponsePacket.class, ServerboundCookieResponsePacket::new, ServerboundCookieResponsePacket.class, new ServerboundCookieResponsePacket.Codec(),
map(0x01, MINECRAFT_1_20_5, false)); map(0x01, MINECRAFT_1_20_5, false));
serverbound.register( serverbound.register(
PluginMessagePacket.class, PluginMessagePacket::new, PluginMessagePacket.class, new PluginMessagePacket.Codec(),
map(0x01, MINECRAFT_1_20_2, false), map(0x01, MINECRAFT_1_20_2, false),
map(0x02, MINECRAFT_1_20_5, false)); map(0x02, MINECRAFT_1_20_5, false));
serverbound.register( serverbound.register(
FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE, FinishedUpdatePacket.class, new FinishedUpdatePacket.Codec(),
map(0x02, MINECRAFT_1_20_2, false), map(0x02, MINECRAFT_1_20_2, false),
map(0x03, MINECRAFT_1_20_5, false)); map(0x03, MINECRAFT_1_20_5, false));
serverbound.register(KeepAlivePacket.class, KeepAlivePacket::new, serverbound.register(KeepAlivePacket.class, new KeepAlivePacket.Codec(),
map(0x03, MINECRAFT_1_20_2, false), map(0x03, MINECRAFT_1_20_2, false),
map(0x04, MINECRAFT_1_20_5, false)); map(0x04, MINECRAFT_1_20_5, false));
serverbound.register( serverbound.register(
PingIdentifyPacket.class, PingIdentifyPacket::new, PingIdentifyPacket.class, new PingIdentifyPacket.Codec(),
map(0x04, MINECRAFT_1_20_2, false), map(0x04, MINECRAFT_1_20_2, false),
map(0x05, MINECRAFT_1_20_5, false)); map(0x05, MINECRAFT_1_20_5, false));
serverbound.register( serverbound.register(
ResourcePackResponsePacket.class, ResourcePackResponsePacket.class,
ResourcePackResponsePacket::new, new ResourcePackResponsePacket.Codec(),
map(0x05, MINECRAFT_1_20_2, false), map(0x05, MINECRAFT_1_20_2, false),
map(0x06, MINECRAFT_1_20_5, false)); map(0x06, MINECRAFT_1_20_5, false));
serverbound.register( serverbound.register(
KnownPacksPacket.class, KnownPacksPacket.class,
KnownPacksPacket::new, new KnownPacksPacket.Codec(),
map(0x07, MINECRAFT_1_20_5, false)); map(0x07, MINECRAFT_1_20_5, false));
serverbound.register(ServerboundCustomClickActionPacket.class, ServerboundCustomClickActionPacket::new, serverbound.register(ServerboundCustomClickActionPacket.class, new ServerboundCustomClickActionPacket.Codec(),
map(0x08, MINECRAFT_1_21_6, false)); map(0x08, MINECRAFT_1_21_6, false));
serverbound.register( serverbound.register(
CodeOfConductAcceptPacket.class, CodeOfConductAcceptPacket.class,
() -> CodeOfConductAcceptPacket.INSTANCE, new CodeOfConductAcceptPacket.Codec(),
map(0x09, MINECRAFT_1_21_9, false)); map(0x09, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new, ClientboundCookieRequestPacket.class, new ClientboundCookieRequestPacket.Codec(),
map(0x00, MINECRAFT_1_20_5, false)); map(0x00, MINECRAFT_1_20_5, false));
clientbound.register( clientbound.register(
PluginMessagePacket.class, PluginMessagePacket::new, PluginMessagePacket.class, new PluginMessagePacket.Codec(),
map(0x00, MINECRAFT_1_20_2, false), map(0x00, MINECRAFT_1_20_2, false),
map(0x01, MINECRAFT_1_20_5, false)); map(0x01, MINECRAFT_1_20_5, false));
clientbound.register( clientbound.register(
DisconnectPacket.class, () -> new DisconnectPacket(this), DisconnectPacket.class, new DisconnectPacket.Codec(this),
map(0x01, MINECRAFT_1_20_2, false), map(0x01, MINECRAFT_1_20_2, false),
map(0x02, MINECRAFT_1_20_5, false)); map(0x02, MINECRAFT_1_20_5, false));
clientbound.register( clientbound.register(
FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE, FinishedUpdatePacket.class, new FinishedUpdatePacket.Codec(),
map(0x02, MINECRAFT_1_20_2, false), map(0x02, MINECRAFT_1_20_2, false),
map(0x03, MINECRAFT_1_20_5, false)); map(0x03, MINECRAFT_1_20_5, false));
clientbound.register(KeepAlivePacket.class, KeepAlivePacket::new, clientbound.register(KeepAlivePacket.class, new KeepAlivePacket.Codec(),
map(0x03, MINECRAFT_1_20_2, false), map(0x03, MINECRAFT_1_20_2, false),
map(0x04, MINECRAFT_1_20_5, false)); map(0x04, MINECRAFT_1_20_5, false));
clientbound.register( clientbound.register(
PingIdentifyPacket.class, PingIdentifyPacket::new, PingIdentifyPacket.class, new PingIdentifyPacket.Codec(),
map(0x04, MINECRAFT_1_20_2, false), map(0x04, MINECRAFT_1_20_2, false),
map(0x05, MINECRAFT_1_20_5, false)); map(0x05, MINECRAFT_1_20_5, false));
clientbound.register( clientbound.register(
RegistrySyncPacket.class, RegistrySyncPacket::new, RegistrySyncPacket.class, new RegistrySyncPacket.Codec(),
map(0x05, MINECRAFT_1_20_2, false), map(0x05, MINECRAFT_1_20_2, false),
map(0x07, MINECRAFT_1_20_5, false)); map(0x07, MINECRAFT_1_20_5, false));
clientbound.register( clientbound.register(
RemoveResourcePackPacket.class, RemoveResourcePackPacket::new, RemoveResourcePackPacket.class, new RemoveResourcePackPacket.Codec(),
map(0x06, MINECRAFT_1_20_3, false), map(0x06, MINECRAFT_1_20_3, false),
map(0x08, MINECRAFT_1_20_5, false)); map(0x08, MINECRAFT_1_20_5, false));
clientbound.register(ResourcePackRequestPacket.class, ResourcePackRequestPacket::new, clientbound.register(ResourcePackRequestPacket.class, new ResourcePackRequestPacket.Codec(),
map(0x06, MINECRAFT_1_20_2, false), map(0x06, MINECRAFT_1_20_2, false),
map(0x07, MINECRAFT_1_20_3, false), map(0x07, MINECRAFT_1_20_3, false),
map(0x09, MINECRAFT_1_20_5, false)); map(0x09, MINECRAFT_1_20_5, false));
clientbound.register( clientbound.register(
ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new, ClientboundStoreCookiePacket.class, new ClientboundStoreCookiePacket.Codec(),
map(0x0A, MINECRAFT_1_20_5, false)); map(0x0A, MINECRAFT_1_20_5, false));
clientbound.register(TransferPacket.class, TransferPacket::new, clientbound.register(TransferPacket.class, new TransferPacket.Codec(),
map(0x0B, MINECRAFT_1_20_5, false)); map(0x0B, MINECRAFT_1_20_5, false));
clientbound.register(ActiveFeaturesPacket.class, ActiveFeaturesPacket::new, clientbound.register(ActiveFeaturesPacket.class, new ActiveFeaturesPacket.Codec(),
map(0x07, MINECRAFT_1_20_2, false), map(0x07, MINECRAFT_1_20_2, false),
map(0x08, MINECRAFT_1_20_3, false), map(0x08, MINECRAFT_1_20_3, false),
map(0x0C, MINECRAFT_1_20_5, false)); map(0x0C, MINECRAFT_1_20_5, false));
clientbound.register(TagsUpdatePacket.class, TagsUpdatePacket::new, clientbound.register(TagsUpdatePacket.class, new TagsUpdatePacket.Codec(),
map(0x08, MINECRAFT_1_20_2, false), map(0x08, MINECRAFT_1_20_2, false),
map(0x09, MINECRAFT_1_20_3, false), map(0x09, MINECRAFT_1_20_3, false),
map(0x0D, MINECRAFT_1_20_5, false)); map(0x0D, MINECRAFT_1_20_5, false));
clientbound.register(KnownPacksPacket.class, KnownPacksPacket::new, clientbound.register(KnownPacksPacket.class, new KnownPacksPacket.Codec(),
map(0x0E, MINECRAFT_1_20_5, false)); map(0x0E, MINECRAFT_1_20_5, false));
clientbound.register(ClientboundCustomReportDetailsPacket.class, ClientboundCustomReportDetailsPacket::new, clientbound.register(ClientboundCustomReportDetailsPacket.class, new ClientboundCustomReportDetailsPacket.Codec(),
map(0x0F, MINECRAFT_1_21, false)); map(0x0F, MINECRAFT_1_21, false));
clientbound.register(ClientboundServerLinksPacket.class, ClientboundServerLinksPacket::new, clientbound.register(ClientboundServerLinksPacket.class, new ClientboundServerLinksPacket.Codec(),
map(0x10, MINECRAFT_1_21, false)); map(0x10, MINECRAFT_1_21, false));
clientbound.register(DialogClearPacket.class, () -> DialogClearPacket.INSTANCE, clientbound.register(DialogClearPacket.class, new DialogClearPacket.Codec(),
map(0x11, MINECRAFT_1_21_6, false)); map(0x11, MINECRAFT_1_21_6, false));
clientbound.register(DialogShowPacket.class, () -> new DialogShowPacket(this), clientbound.register(DialogShowPacket.class, new DialogShowPacket.Codec(this),
map(0x12, MINECRAFT_1_21_6, false)); map(0x12, MINECRAFT_1_21_6, false));
clientbound.register(CodeOfConductPacket.class, CodeOfConductPacket::new, clientbound.register(CodeOfConductPacket.class, new CodeOfConductPacket.Codec(),
map(0x13, MINECRAFT_1_21_9, false)); map(0x13, MINECRAFT_1_21_9, false));
} }
}, },
@@ -264,7 +264,7 @@ public enum StateRegistry {
serverbound.fallback = false; serverbound.fallback = false;
clientbound.fallback = false; clientbound.fallback = false;
serverbound.register(TabCompleteRequestPacket.class, TabCompleteRequestPacket::new, serverbound.register(TabCompleteRequestPacket.class, new TabCompleteRequestPacket.Codec(),
map(0x14, MINECRAFT_1_7_2, false), map(0x14, MINECRAFT_1_7_2, false),
map(0x01, MINECRAFT_1_9, false), map(0x01, MINECRAFT_1_9, false),
map(0x02, MINECRAFT_1_12, false), map(0x02, MINECRAFT_1_12, false),
@@ -281,7 +281,7 @@ public enum StateRegistry {
map(0x0E, MINECRAFT_1_21_6, false)); map(0x0E, MINECRAFT_1_21_6, false));
serverbound.register( serverbound.register(
LegacyChatPacket.class, LegacyChatPacket.class,
LegacyChatPacket::new, new LegacyChatPacket.Codec(),
map(0x01, MINECRAFT_1_7_2, false), map(0x01, MINECRAFT_1_7_2, false),
map(0x02, MINECRAFT_1_9, false), map(0x02, MINECRAFT_1_9, false),
map(0x03, MINECRAFT_1_12, false), map(0x03, MINECRAFT_1_12, false),
@@ -289,35 +289,35 @@ public enum StateRegistry {
map(0x03, MINECRAFT_1_14, MINECRAFT_1_18_2, false)); map(0x03, MINECRAFT_1_14, MINECRAFT_1_18_2, false));
serverbound.register( serverbound.register(
ChatAcknowledgementPacket.class, ChatAcknowledgementPacket.class,
ChatAcknowledgementPacket::new, new ChatAcknowledgementPacket.Codec(),
map(0x03, MINECRAFT_1_19_3, false), map(0x03, MINECRAFT_1_19_3, false),
map(0x04, MINECRAFT_1_21_2, false), map(0x04, MINECRAFT_1_21_2, false),
map(0x05, MINECRAFT_1_21_6, false)); map(0x05, MINECRAFT_1_21_6, false));
serverbound.register(KeyedPlayerCommandPacket.class, KeyedPlayerCommandPacket::new, serverbound.register(KeyedPlayerCommandPacket.class, new KeyedPlayerCommandPacket.Codec(),
map(0x03, MINECRAFT_1_19, false), map(0x03, MINECRAFT_1_19, false),
map(0x04, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false)); map(0x04, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false));
serverbound.register(KeyedPlayerChatPacket.class, KeyedPlayerChatPacket::new, serverbound.register(KeyedPlayerChatPacket.class, new KeyedPlayerChatPacket.Codec(),
map(0x04, MINECRAFT_1_19, false), map(0x04, MINECRAFT_1_19, false),
map(0x05, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false)); map(0x05, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false));
serverbound.register(SessionPlayerCommandPacket.class, SessionPlayerCommandPacket::new, serverbound.register(SessionPlayerCommandPacket.class, new SessionPlayerCommandPacket.Codec(),
map(0x04, MINECRAFT_1_19_3, false), map(0x04, MINECRAFT_1_19_3, false),
map(0x05, MINECRAFT_1_20_5, false), map(0x05, MINECRAFT_1_20_5, false),
map(0x06, MINECRAFT_1_21_2, false), map(0x06, MINECRAFT_1_21_2, false),
map(0x07, MINECRAFT_1_21_6, false)); map(0x07, MINECRAFT_1_21_6, false));
serverbound.register(UnsignedPlayerCommandPacket.class, UnsignedPlayerCommandPacket::new, serverbound.register(UnsignedPlayerCommandPacket.class, new UnsignedPlayerCommandPacket.Codec(),
map(0x04, MINECRAFT_1_20_5, false), map(0x04, MINECRAFT_1_20_5, false),
map(0x05, MINECRAFT_1_21_2, false), map(0x05, MINECRAFT_1_21_2, false),
map(0x06, MINECRAFT_1_21_6, false)); map(0x06, MINECRAFT_1_21_6, false));
serverbound.register( serverbound.register(
SessionPlayerChatPacket.class, SessionPlayerChatPacket.class,
SessionPlayerChatPacket::new, new SessionPlayerChatPacket.Codec(),
map(0x05, MINECRAFT_1_19_3, false), map(0x05, MINECRAFT_1_19_3, false),
map(0x06, MINECRAFT_1_20_5, false), map(0x06, MINECRAFT_1_20_5, false),
map(0x07, MINECRAFT_1_21_2, false), map(0x07, MINECRAFT_1_21_2, false),
map(0x08, MINECRAFT_1_21_6, false)); map(0x08, MINECRAFT_1_21_6, false));
serverbound.register( serverbound.register(
ClientSettingsPacket.class, ClientSettingsPacket.class,
ClientSettingsPacket::new, new ClientSettingsPacket.Codec(),
map(0x15, MINECRAFT_1_7_2, false), map(0x15, MINECRAFT_1_7_2, false),
map(0x04, MINECRAFT_1_9, false), map(0x04, MINECRAFT_1_9, false),
map(0x05, MINECRAFT_1_12, false), map(0x05, MINECRAFT_1_12, false),
@@ -332,13 +332,13 @@ public enum StateRegistry {
map(0x0C, MINECRAFT_1_21_2, false), map(0x0C, MINECRAFT_1_21_2, false),
map(0x0D, MINECRAFT_1_21_6, false)); map(0x0D, MINECRAFT_1_21_6, false));
serverbound.register( serverbound.register(
ServerboundCookieResponsePacket.class, ServerboundCookieResponsePacket::new, ServerboundCookieResponsePacket.class, new ServerboundCookieResponsePacket.Codec(),
map(0x11, MINECRAFT_1_20_5, false), map(0x11, MINECRAFT_1_20_5, false),
map(0x13, MINECRAFT_1_21_2, false), map(0x13, MINECRAFT_1_21_2, false),
map(0x14, MINECRAFT_1_21_6, false)); map(0x14, MINECRAFT_1_21_6, false));
serverbound.register( serverbound.register(
PluginMessagePacket.class, PluginMessagePacket.class,
PluginMessagePacket::new, new PluginMessagePacket.Codec(),
map(0x17, MINECRAFT_1_7_2, false), map(0x17, MINECRAFT_1_7_2, false),
map(0x09, MINECRAFT_1_9, false), map(0x09, MINECRAFT_1_9, false),
map(0x0A, MINECRAFT_1_12, false), map(0x0A, MINECRAFT_1_12, false),
@@ -357,7 +357,7 @@ public enum StateRegistry {
map(0x15, MINECRAFT_1_21_6, false)); map(0x15, MINECRAFT_1_21_6, false));
serverbound.register( serverbound.register(
KeepAlivePacket.class, KeepAlivePacket.class,
KeepAlivePacket::new, new KeepAlivePacket.Codec(),
map(0x00, MINECRAFT_1_7_2, false), map(0x00, MINECRAFT_1_7_2, false),
map(0x0B, MINECRAFT_1_9, false), map(0x0B, MINECRAFT_1_9, false),
map(0x0C, MINECRAFT_1_12, false), map(0x0C, MINECRAFT_1_12, false),
@@ -377,7 +377,7 @@ public enum StateRegistry {
map(0x1B, MINECRAFT_1_21_6, false)); map(0x1B, MINECRAFT_1_21_6, false));
serverbound.register( serverbound.register(
ResourcePackResponsePacket.class, ResourcePackResponsePacket.class,
ResourcePackResponsePacket::new, new ResourcePackResponsePacket.Codec(),
map(0x19, MINECRAFT_1_8, false), map(0x19, MINECRAFT_1_8, false),
map(0x16, MINECRAFT_1_9, false), map(0x16, MINECRAFT_1_9, false),
map(0x18, MINECRAFT_1_12, false), map(0x18, MINECRAFT_1_12, false),
@@ -394,7 +394,7 @@ public enum StateRegistry {
map(0x2F, MINECRAFT_1_21_4, false), map(0x2F, MINECRAFT_1_21_4, false),
map(0x30, MINECRAFT_1_21_6, false)); map(0x30, MINECRAFT_1_21_6, false));
serverbound.register( serverbound.register(
FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE, FinishedUpdatePacket.class, new FinishedUpdatePacket.Codec(),
map(0x0B, MINECRAFT_1_20_2, false), map(0x0B, MINECRAFT_1_20_2, false),
map(0x0C, MINECRAFT_1_20_5, false), map(0x0C, MINECRAFT_1_20_5, false),
map(0x0E, MINECRAFT_1_21_2, false), map(0x0E, MINECRAFT_1_21_2, false),
@@ -402,7 +402,7 @@ public enum StateRegistry {
clientbound.register( clientbound.register(
BossBarPacket.class, BossBarPacket.class,
BossBarPacket::new, new BossBarPacket.Codec(),
map(0x0C, MINECRAFT_1_9, false), map(0x0C, MINECRAFT_1_9, false),
map(0x0D, MINECRAFT_1_15, false), map(0x0D, MINECRAFT_1_15, false),
map(0x0C, MINECRAFT_1_16, false), map(0x0C, MINECRAFT_1_16, false),
@@ -413,14 +413,14 @@ public enum StateRegistry {
map(0x09, MINECRAFT_1_21_5, false)); map(0x09, MINECRAFT_1_21_5, false));
clientbound.register( clientbound.register(
LegacyChatPacket.class, LegacyChatPacket.class,
LegacyChatPacket::new, new LegacyChatPacket.Codec(),
map(0x02, MINECRAFT_1_7_2, true), map(0x02, MINECRAFT_1_7_2, true),
map(0x0F, MINECRAFT_1_9, true), map(0x0F, MINECRAFT_1_9, true),
map(0x0E, MINECRAFT_1_13, true), map(0x0E, MINECRAFT_1_13, true),
map(0x0F, MINECRAFT_1_15, true), map(0x0F, MINECRAFT_1_15, true),
map(0x0E, MINECRAFT_1_16, true), map(0x0E, MINECRAFT_1_16, true),
map(0x0F, MINECRAFT_1_17, MINECRAFT_1_18_2, true)); map(0x0F, MINECRAFT_1_17, MINECRAFT_1_18_2, true));
clientbound.register(TabCompleteResponsePacket.class, TabCompleteResponsePacket::new, clientbound.register(TabCompleteResponsePacket.class, new TabCompleteResponsePacket.Codec(),
map(0x3A, MINECRAFT_1_7_2, false), map(0x3A, MINECRAFT_1_7_2, false),
map(0x0E, MINECRAFT_1_9, false), map(0x0E, MINECRAFT_1_9, false),
map(0x10, MINECRAFT_1_13, false), map(0x10, MINECRAFT_1_13, false),
@@ -435,7 +435,7 @@ public enum StateRegistry {
map(0x0F, MINECRAFT_1_21_5, false)); map(0x0F, MINECRAFT_1_21_5, false));
clientbound.register( clientbound.register(
AvailableCommandsPacket.class, AvailableCommandsPacket.class,
AvailableCommandsPacket::new, new AvailableCommandsPacket.Codec(),
map(0x11, MINECRAFT_1_13, false), map(0x11, MINECRAFT_1_13, false),
map(0x12, MINECRAFT_1_15, false), map(0x12, MINECRAFT_1_15, false),
map(0x11, MINECRAFT_1_16, false), map(0x11, MINECRAFT_1_16, false),
@@ -447,11 +447,11 @@ public enum StateRegistry {
map(0x11, MINECRAFT_1_20_2, false), map(0x11, MINECRAFT_1_20_2, false),
map(0x10, MINECRAFT_1_21_5, false)); map(0x10, MINECRAFT_1_21_5, false));
clientbound.register( clientbound.register(
ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new, ClientboundCookieRequestPacket.class, new ClientboundCookieRequestPacket.Codec(),
map(0x16, MINECRAFT_1_20_5, false), map(0x16, MINECRAFT_1_20_5, false),
map(0x15, MINECRAFT_1_21_5, false)); map(0x15, MINECRAFT_1_21_5, false));
clientbound.register( clientbound.register(
ClientboundSoundEntityPacket.class, ClientboundSoundEntityPacket::new, ClientboundSoundEntityPacket.class, new ClientboundSoundEntityPacket.Codec(),
map(0x5D, MINECRAFT_1_19_3, true), map(0x5D, MINECRAFT_1_19_3, true),
map(0x61, MINECRAFT_1_19_4, true), map(0x61, MINECRAFT_1_19_4, true),
map(0x63, MINECRAFT_1_20_2, true), map(0x63, MINECRAFT_1_20_2, true),
@@ -461,7 +461,7 @@ public enum StateRegistry {
map(0x6D, MINECRAFT_1_21_5, true), map(0x6D, MINECRAFT_1_21_5, true),
map(0x72, MINECRAFT_1_21_9, true)); map(0x72, MINECRAFT_1_21_9, true));
clientbound.register( clientbound.register(
ClientboundStopSoundPacket.class, ClientboundStopSoundPacket::new, ClientboundStopSoundPacket.class, new ClientboundStopSoundPacket.Codec(),
map(0x5F, MINECRAFT_1_19_3, true), map(0x5F, MINECRAFT_1_19_3, true),
map(0x63, MINECRAFT_1_19_4, true), map(0x63, MINECRAFT_1_19_4, true),
map(0x66, MINECRAFT_1_20_2, true), map(0x66, MINECRAFT_1_20_2, true),
@@ -472,7 +472,7 @@ public enum StateRegistry {
map(0x75, MINECRAFT_1_21_9, true)); map(0x75, MINECRAFT_1_21_9, true));
clientbound.register( clientbound.register(
PluginMessagePacket.class, PluginMessagePacket.class,
PluginMessagePacket::new, new PluginMessagePacket.Codec(),
map(0x3F, MINECRAFT_1_7_2, false), map(0x3F, MINECRAFT_1_7_2, false),
map(0x18, MINECRAFT_1_9, false), map(0x18, MINECRAFT_1_9, false),
map(0x19, MINECRAFT_1_13, false), map(0x19, MINECRAFT_1_13, false),
@@ -490,7 +490,7 @@ public enum StateRegistry {
map(0x18, MINECRAFT_1_21_5, false)); map(0x18, MINECRAFT_1_21_5, false));
clientbound.register( clientbound.register(
DisconnectPacket.class, DisconnectPacket.class,
() -> new DisconnectPacket(this), new DisconnectPacket.Codec(this),
map(0x40, MINECRAFT_1_7_2, false), map(0x40, MINECRAFT_1_7_2, false),
map(0x1A, MINECRAFT_1_9, false), map(0x1A, MINECRAFT_1_9, false),
map(0x1B, MINECRAFT_1_13, false), map(0x1B, MINECRAFT_1_13, false),
@@ -509,7 +509,7 @@ public enum StateRegistry {
map(0x20, MINECRAFT_1_21_9, false)); map(0x20, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
KeepAlivePacket.class, KeepAlivePacket.class,
KeepAlivePacket::new, new KeepAlivePacket.Codec(),
map(0x00, MINECRAFT_1_7_2, false), map(0x00, MINECRAFT_1_7_2, false),
map(0x1F, MINECRAFT_1_9, false), map(0x1F, MINECRAFT_1_9, false),
map(0x21, MINECRAFT_1_13, false), map(0x21, MINECRAFT_1_13, false),
@@ -529,7 +529,7 @@ public enum StateRegistry {
map(0x2B, MINECRAFT_1_21_9, false)); map(0x2B, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
JoinGamePacket.class, JoinGamePacket.class,
JoinGamePacket::new, new JoinGamePacket.Codec(),
map(0x01, MINECRAFT_1_7_2, false), map(0x01, MINECRAFT_1_7_2, false),
map(0x23, MINECRAFT_1_9, false), map(0x23, MINECRAFT_1_9, false),
map(0x25, MINECRAFT_1_13, false), map(0x25, MINECRAFT_1_13, false),
@@ -549,7 +549,7 @@ public enum StateRegistry {
map(0x30, MINECRAFT_1_21_9, false)); map(0x30, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
RespawnPacket.class, RespawnPacket.class,
RespawnPacket::new, new RespawnPacket.Codec(),
map(0x07, MINECRAFT_1_7_2, true), map(0x07, MINECRAFT_1_7_2, true),
map(0x33, MINECRAFT_1_9, true), map(0x33, MINECRAFT_1_9, true),
map(0x34, MINECRAFT_1_12, true), map(0x34, MINECRAFT_1_12, true),
@@ -572,7 +572,7 @@ public enum StateRegistry {
map(0x50, MINECRAFT_1_21_9, true)); map(0x50, MINECRAFT_1_21_9, true));
clientbound.register( clientbound.register(
RemoveResourcePackPacket.class, RemoveResourcePackPacket.class,
RemoveResourcePackPacket::new, new RemoveResourcePackPacket.Codec(),
map(0x43, MINECRAFT_1_20_3, false), map(0x43, MINECRAFT_1_20_3, false),
map(0x45, MINECRAFT_1_20_5, false), map(0x45, MINECRAFT_1_20_5, false),
map(0x4A, MINECRAFT_1_21_2, false), map(0x4A, MINECRAFT_1_21_2, false),
@@ -580,7 +580,7 @@ public enum StateRegistry {
map(0x4E, MINECRAFT_1_21_9, false)); map(0x4E, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
ResourcePackRequestPacket.class, ResourcePackRequestPacket.class,
ResourcePackRequestPacket::new, new ResourcePackRequestPacket.Codec(),
map(0x48, MINECRAFT_1_8, false), map(0x48, MINECRAFT_1_8, false),
map(0x32, MINECRAFT_1_9, false), map(0x32, MINECRAFT_1_9, false),
map(0x33, MINECRAFT_1_12, false), map(0x33, MINECRAFT_1_12, false),
@@ -603,7 +603,7 @@ public enum StateRegistry {
map(0x4F, MINECRAFT_1_21_9, false)); map(0x4F, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
HeaderAndFooterPacket.class, HeaderAndFooterPacket.class,
HeaderAndFooterPacket::new, new HeaderAndFooterPacket.Codec(),
map(0x47, MINECRAFT_1_8, true), map(0x47, MINECRAFT_1_8, true),
map(0x48, MINECRAFT_1_9, true), map(0x48, MINECRAFT_1_9, true),
map(0x47, MINECRAFT_1_9_4, true), map(0x47, MINECRAFT_1_9_4, true),
@@ -627,7 +627,7 @@ public enum StateRegistry {
map(0x78, MINECRAFT_1_21_9, true)); map(0x78, MINECRAFT_1_21_9, true));
clientbound.register( clientbound.register(
LegacyTitlePacket.class, LegacyTitlePacket.class,
LegacyTitlePacket::new, new LegacyTitlePacket.Codec(),
map(0x45, MINECRAFT_1_8, true), map(0x45, MINECRAFT_1_8, true),
map(0x45, MINECRAFT_1_9, true), map(0x45, MINECRAFT_1_9, true),
map(0x47, MINECRAFT_1_12, true), map(0x47, MINECRAFT_1_12, true),
@@ -636,7 +636,7 @@ public enum StateRegistry {
map(0x4F, MINECRAFT_1_14, true), map(0x4F, MINECRAFT_1_14, true),
map(0x50, MINECRAFT_1_15, true), map(0x50, MINECRAFT_1_15, true),
map(0x4F, MINECRAFT_1_16, MINECRAFT_1_16_4, true)); map(0x4F, MINECRAFT_1_16, MINECRAFT_1_16_4, true));
clientbound.register(TitleSubtitlePacket.class, TitleSubtitlePacket::new, clientbound.register(TitleSubtitlePacket.class, new TitleSubtitlePacket.Codec(),
map(0x57, MINECRAFT_1_17, true), map(0x57, MINECRAFT_1_17, true),
map(0x58, MINECRAFT_1_18, true), map(0x58, MINECRAFT_1_18, true),
map(0x5B, MINECRAFT_1_19_1, true), map(0x5B, MINECRAFT_1_19_1, true),
@@ -650,7 +650,7 @@ public enum StateRegistry {
map(0x6E, MINECRAFT_1_21_9, true)); map(0x6E, MINECRAFT_1_21_9, true));
clientbound.register( clientbound.register(
TitleTextPacket.class, TitleTextPacket.class,
TitleTextPacket::new, new TitleTextPacket.Codec(),
map(0x59, MINECRAFT_1_17, true), map(0x59, MINECRAFT_1_17, true),
map(0x5A, MINECRAFT_1_18, true), map(0x5A, MINECRAFT_1_18, true),
map(0x5D, MINECRAFT_1_19_1, true), map(0x5D, MINECRAFT_1_19_1, true),
@@ -664,7 +664,7 @@ public enum StateRegistry {
map(0x70, MINECRAFT_1_21_9, true)); map(0x70, MINECRAFT_1_21_9, true));
clientbound.register( clientbound.register(
TitleActionbarPacket.class, TitleActionbarPacket.class,
TitleActionbarPacket::new, new TitleActionbarPacket.Codec(),
map(0x41, MINECRAFT_1_17, true), map(0x41, MINECRAFT_1_17, true),
map(0x40, MINECRAFT_1_19, true), map(0x40, MINECRAFT_1_19, true),
map(0x43, MINECRAFT_1_19_1, true), map(0x43, MINECRAFT_1_19_1, true),
@@ -678,7 +678,7 @@ public enum StateRegistry {
map(0x55, MINECRAFT_1_21_9, true)); map(0x55, MINECRAFT_1_21_9, true));
clientbound.register( clientbound.register(
TitleTimesPacket.class, TitleTimesPacket.class,
TitleTimesPacket::new, new TitleTimesPacket.Codec(),
map(0x5A, MINECRAFT_1_17, true), map(0x5A, MINECRAFT_1_17, true),
map(0x5B, MINECRAFT_1_18, true), map(0x5B, MINECRAFT_1_18, true),
map(0x5E, MINECRAFT_1_19_1, true), map(0x5E, MINECRAFT_1_19_1, true),
@@ -692,7 +692,7 @@ public enum StateRegistry {
map(0x71, MINECRAFT_1_21_9, true)); map(0x71, MINECRAFT_1_21_9, true));
clientbound.register( clientbound.register(
TitleClearPacket.class, TitleClearPacket.class,
TitleClearPacket::new, new TitleClearPacket.Codec(),
map(0x10, MINECRAFT_1_17, true), map(0x10, MINECRAFT_1_17, true),
map(0x0D, MINECRAFT_1_19, true), map(0x0D, MINECRAFT_1_19, true),
map(0x0C, MINECRAFT_1_19_3, true), map(0x0C, MINECRAFT_1_19_3, true),
@@ -701,7 +701,7 @@ public enum StateRegistry {
map(0x0E, MINECRAFT_1_21_5, true)); map(0x0E, MINECRAFT_1_21_5, true));
clientbound.register( clientbound.register(
LegacyPlayerListItemPacket.class, LegacyPlayerListItemPacket.class,
LegacyPlayerListItemPacket::new, new LegacyPlayerListItemPacket.Codec(),
map(0x38, MINECRAFT_1_7_2, false), map(0x38, MINECRAFT_1_7_2, false),
map(0x2D, MINECRAFT_1_9, false), map(0x2D, MINECRAFT_1_9, false),
map(0x2E, MINECRAFT_1_12_1, false), map(0x2E, MINECRAFT_1_12_1, false),
@@ -713,7 +713,7 @@ public enum StateRegistry {
map(0x36, MINECRAFT_1_17, false), map(0x36, MINECRAFT_1_17, false),
map(0x34, MINECRAFT_1_19, false), map(0x34, MINECRAFT_1_19, false),
map(0x37, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false)); map(0x37, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false));
clientbound.register(RemovePlayerInfoPacket.class, RemovePlayerInfoPacket::new, clientbound.register(RemovePlayerInfoPacket.class, new RemovePlayerInfoPacket.Codec(),
map(0x35, MINECRAFT_1_19_3, false), map(0x35, MINECRAFT_1_19_3, false),
map(0x39, MINECRAFT_1_19_4, false), map(0x39, MINECRAFT_1_19_4, false),
map(0x3B, MINECRAFT_1_20_2, false), map(0x3B, MINECRAFT_1_20_2, false),
@@ -723,7 +723,7 @@ public enum StateRegistry {
map(0x43, MINECRAFT_1_21_9, false)); map(0x43, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
UpsertPlayerInfoPacket.class, UpsertPlayerInfoPacket.class,
UpsertPlayerInfoPacket::new, new UpsertPlayerInfoPacket.Codec(),
map(0x36, MINECRAFT_1_19_3, false), map(0x36, MINECRAFT_1_19_3, false),
map(0x3A, MINECRAFT_1_19_4, false), map(0x3A, MINECRAFT_1_19_4, false),
map(0x3C, MINECRAFT_1_20_2, false), map(0x3C, MINECRAFT_1_20_2, false),
@@ -732,14 +732,14 @@ public enum StateRegistry {
map(0x3F, MINECRAFT_1_21_5, false), map(0x3F, MINECRAFT_1_21_5, false),
map(0x44, MINECRAFT_1_21_9, false)); map(0x44, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new, ClientboundStoreCookiePacket.class, new ClientboundStoreCookiePacket.Codec(),
map(0x6B, MINECRAFT_1_20_5, false), map(0x6B, MINECRAFT_1_20_5, false),
map(0x72, MINECRAFT_1_21_2, false), map(0x72, MINECRAFT_1_21_2, false),
map(0x71, MINECRAFT_1_21_5, false), map(0x71, MINECRAFT_1_21_5, false),
map(0x76, MINECRAFT_1_21_9, false)); map(0x76, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
SystemChatPacket.class, SystemChatPacket.class,
SystemChatPacket::new, new SystemChatPacket.Codec(),
map(0x5F, MINECRAFT_1_19, true), map(0x5F, MINECRAFT_1_19, true),
map(0x62, MINECRAFT_1_19_1, true), map(0x62, MINECRAFT_1_19_1, true),
map(0x60, MINECRAFT_1_19_3, true), map(0x60, MINECRAFT_1_19_3, true),
@@ -752,7 +752,7 @@ public enum StateRegistry {
map(0x77, MINECRAFT_1_21_9, true)); map(0x77, MINECRAFT_1_21_9, true));
clientbound.register( clientbound.register(
PlayerChatCompletionPacket.class, PlayerChatCompletionPacket.class,
PlayerChatCompletionPacket::new, new PlayerChatCompletionPacket.Codec(),
map(0x15, MINECRAFT_1_19_1, true), map(0x15, MINECRAFT_1_19_1, true),
map(0x14, MINECRAFT_1_19_3, true), map(0x14, MINECRAFT_1_19_3, true),
map(0x16, MINECRAFT_1_19_4, true), map(0x16, MINECRAFT_1_19_4, true),
@@ -761,7 +761,7 @@ public enum StateRegistry {
map(0x17, MINECRAFT_1_21_5, true)); map(0x17, MINECRAFT_1_21_5, true));
clientbound.register( clientbound.register(
ServerDataPacket.class, ServerDataPacket.class,
ServerDataPacket::new, new ServerDataPacket.Codec(),
map(0x3F, MINECRAFT_1_19, false), map(0x3F, MINECRAFT_1_19, false),
map(0x42, MINECRAFT_1_19_1, false), map(0x42, MINECRAFT_1_19_1, false),
map(0x41, MINECRAFT_1_19_3, false), map(0x41, MINECRAFT_1_19_3, false),
@@ -774,7 +774,7 @@ public enum StateRegistry {
map(0x54, MINECRAFT_1_21_9, false)); map(0x54, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
StartUpdatePacket.class, StartUpdatePacket.class,
() -> StartUpdatePacket.INSTANCE, new StartUpdatePacket.Codec(),
map(0x65, MINECRAFT_1_20_2, false), map(0x65, MINECRAFT_1_20_2, false),
map(0x67, MINECRAFT_1_20_3, false), map(0x67, MINECRAFT_1_20_3, false),
map(0x69, MINECRAFT_1_20_5, false), map(0x69, MINECRAFT_1_20_5, false),
@@ -783,23 +783,23 @@ public enum StateRegistry {
map(0x74, MINECRAFT_1_21_9, false)); map(0x74, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
BundleDelimiterPacket.class, BundleDelimiterPacket.class,
() -> BundleDelimiterPacket.INSTANCE, new BundleDelimiterPacket.Codec(),
map(0x00, MINECRAFT_1_19_4, false)); map(0x00, MINECRAFT_1_19_4, false));
clientbound.register( clientbound.register(
TransferPacket.class, TransferPacket.class,
TransferPacket::new, new TransferPacket.Codec(),
map(0x73, MINECRAFT_1_20_5, false), map(0x73, MINECRAFT_1_20_5, false),
map(0x7A, MINECRAFT_1_21_2, false), map(0x7A, MINECRAFT_1_21_2, false),
map(0x7F, MINECRAFT_1_21_9, false)); map(0x7F, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
ClientboundCustomReportDetailsPacket.class, ClientboundCustomReportDetailsPacket.class,
ClientboundCustomReportDetailsPacket::new, new ClientboundCustomReportDetailsPacket.Codec(),
map(0x7A, MINECRAFT_1_21, false), map(0x7A, MINECRAFT_1_21, false),
map(0x81, MINECRAFT_1_21_2, false), map(0x81, MINECRAFT_1_21_2, false),
map(0x86, MINECRAFT_1_21_9, false)); map(0x86, MINECRAFT_1_21_9, false));
clientbound.register( clientbound.register(
ClientboundServerLinksPacket.class, ClientboundServerLinksPacket.class,
ClientboundServerLinksPacket::new, new ClientboundServerLinksPacket.Codec(),
map(0x7B, MINECRAFT_1_21, false), map(0x7B, MINECRAFT_1_21, false),
map(0x82, MINECRAFT_1_21_2, false), map(0x82, MINECRAFT_1_21_2, false),
map(0x87, MINECRAFT_1_21_9, false)); map(0x87, MINECRAFT_1_21_9, false));
@@ -808,39 +808,39 @@ public enum StateRegistry {
LOGIN { LOGIN {
{ {
serverbound.register(ServerLoginPacket.class, serverbound.register(ServerLoginPacket.class,
ServerLoginPacket::new, new ServerLoginPacket.Codec(),
map(0x00, MINECRAFT_1_7_2, false)); map(0x00, MINECRAFT_1_7_2, false));
serverbound.register( serverbound.register(
EncryptionResponsePacket.class, EncryptionResponsePacket::new, EncryptionResponsePacket.class, new EncryptionResponsePacket.Codec(),
map(0x01, MINECRAFT_1_7_2, false)); map(0x01, MINECRAFT_1_7_2, false));
serverbound.register( serverbound.register(
LoginPluginResponsePacket.class, LoginPluginResponsePacket::new, LoginPluginResponsePacket.class, new LoginPluginResponsePacket.Codec(),
map(0x02, MINECRAFT_1_13, false)); map(0x02, MINECRAFT_1_13, false));
serverbound.register( serverbound.register(
LoginAcknowledgedPacket.class, LoginAcknowledgedPacket::new, LoginAcknowledgedPacket.class, new LoginAcknowledgedPacket.Codec(),
map(0x03, MINECRAFT_1_20_2, false)); map(0x03, MINECRAFT_1_20_2, false));
serverbound.register( serverbound.register(
ServerboundCookieResponsePacket.class, ServerboundCookieResponsePacket::new, ServerboundCookieResponsePacket.class, new ServerboundCookieResponsePacket.Codec(),
map(0x04, MINECRAFT_1_20_5, false)); map(0x04, MINECRAFT_1_20_5, false));
clientbound.register( clientbound.register(
DisconnectPacket.class, () -> new DisconnectPacket(this), DisconnectPacket.class, new DisconnectPacket.Codec(this),
map(0x00, MINECRAFT_1_7_2, false)); map(0x00, MINECRAFT_1_7_2, false));
clientbound.register( clientbound.register(
EncryptionRequestPacket.class, EncryptionRequestPacket::new, EncryptionRequestPacket.class, new EncryptionRequestPacket.Codec(),
map(0x01, MINECRAFT_1_7_2, false)); map(0x01, MINECRAFT_1_7_2, false));
clientbound.register( clientbound.register(
ServerLoginSuccessPacket.class, ServerLoginSuccessPacket::new, ServerLoginSuccessPacket.class, new ServerLoginSuccessPacket.Codec(),
map(0x02, MINECRAFT_1_7_2, false)); map(0x02, MINECRAFT_1_7_2, false));
clientbound.register( clientbound.register(
SetCompressionPacket.class, SetCompressionPacket::new, SetCompressionPacket.class, new SetCompressionPacket.Codec(),
map(0x03, MINECRAFT_1_8, false)); map(0x03, MINECRAFT_1_8, false));
clientbound.register( clientbound.register(
LoginPluginMessagePacket.class, LoginPluginMessagePacket.class,
LoginPluginMessagePacket::new, new LoginPluginMessagePacket.Codec(),
map(0x04, MINECRAFT_1_13, false)); map(0x04, MINECRAFT_1_13, false));
clientbound.register( clientbound.register(
ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new, ClientboundCookieRequestPacket.class, new ClientboundCookieRequestPacket.Codec(),
map(0x05, MINECRAFT_1_20_5, false)); map(0x05, MINECRAFT_1_20_5, false));
} }
}; };
@@ -906,7 +906,7 @@ public enum StateRegistry {
return registry; return registry;
} }
<P extends MinecraftPacket> void register(Class<P> clazz, Supplier<P> packetSupplier, <P extends MinecraftPacket> void register(Class<P> clazz, PacketCodec<P> codec,
PacketMapping... mappings) { PacketMapping... mappings) {
if (mappings.length == 0) { if (mappings.length == 0) {
throw new IllegalArgumentException("At least one mapping must be provided."); throw new IllegalArgumentException("At least one mapping must be provided.");
@@ -947,7 +947,7 @@ public enum StateRegistry {
"Unknown protocol version " + current.protocolVersion); "Unknown protocol version " + current.protocolVersion);
} }
if (registry.packetIdToSupplier.containsKey(current.id)) { if (registry.packetIdToCodec.containsKey(current.id)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Can not register class " "Can not register class "
+ clazz.getSimpleName() + clazz.getSimpleName()
@@ -964,8 +964,9 @@ public enum StateRegistry {
} }
if (!current.encodeOnly) { if (!current.encodeOnly) {
registry.packetIdToSupplier.put(current.id, packetSupplier); registry.packetIdToCodec.put(current.id, codec);
} }
registry.packetClassToCodec.put(clazz, codec);
registry.packetClassToId.put(clazz, current.id); registry.packetClassToId.put(clazz, current.id);
} }
} }
@@ -977,8 +978,11 @@ public enum StateRegistry {
public class ProtocolRegistry { public class ProtocolRegistry {
public final ProtocolVersion version; public final ProtocolVersion version;
final IntObjectMap<Supplier<? extends MinecraftPacket>> packetIdToSupplier = final IntObjectMap<PacketCodec<? extends MinecraftPacket>> packetIdToCodec =
new IntObjectHashMap<>(16, 0.5f); new IntObjectHashMap<>(16, 0.5f);
final Map<Class<? extends MinecraftPacket>, PacketCodec<? extends MinecraftPacket>> packetClassToCodec =
new HashMap<>(16, 0.5f);
final Object2IntMap<Class<? extends MinecraftPacket>> packetClassToId = final Object2IntMap<Class<? extends MinecraftPacket>> packetClassToId =
new Object2IntOpenHashMap<>(16, 0.5f); new Object2IntOpenHashMap<>(16, 0.5f);
@@ -988,17 +992,25 @@ public enum StateRegistry {
} }
/** /**
* Attempts to create a packet from the specified {@code id}. * Gets the codec for the specified packet {@code id}.
* *
* @param id the packet ID * @param id the packet ID
* @return the packet instance, or {@code null} if the ID is not registered * @return the packet codec, or {@code null} if the ID is not registered
*/ */
public @Nullable MinecraftPacket createPacket(final int id) { public @Nullable PacketCodec<? extends MinecraftPacket> getCodec(final int id) {
final Supplier<? extends MinecraftPacket> supplier = this.packetIdToSupplier.get(id); return this.packetIdToCodec.get(id);
if (supplier == null) { }
return null;
} /**
return supplier.get(); * Gets the codec for the specified packet class.
*
* @param packetClass the packet class
* @return the packet codec, or {@code null} if the class is not registered
*/
@SuppressWarnings("unchecked")
public <T extends MinecraftPacket> @Nullable PacketCodec<T> getCodec(
final Class<T> packetClass) {
return (PacketCodec<T>) this.packetClassToCodec.get(packetClass);
} }
/** /**

View File

@@ -66,7 +66,7 @@ public class LegacyPingDecoder extends ByteToMessageDecoder {
out.add(readExtended16Data(in)); out.add(readExtended16Data(in));
} else if (first == 0x02 && in.isReadable()) { } else if (first == 0x02 && in.isReadable()) {
in.skipBytes(in.readableBytes()); in.skipBytes(in.readableBytes());
out.add(new LegacyHandshakePacket()); out.add(LegacyHandshakePacket.INSTANCE);
} else { } else {
in.readerIndex(originalReaderIndex); in.readerIndex(originalReaderIndex);
ctx.pipeline().remove(this); ctx.pipeline().remove(this);

View File

@@ -71,18 +71,20 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter {
int originalReaderIndex = buf.readerIndex(); int originalReaderIndex = buf.readerIndex();
int packetId = ProtocolUtils.readVarInt(buf); int packetId = ProtocolUtils.readVarInt(buf);
MinecraftPacket packet = this.registry.createPacket(packetId); com.velocitypowered.proxy.protocol.PacketCodec<? extends MinecraftPacket> codec =
if (packet == null) { this.registry.getCodec(packetId);
if (codec == null) {
buf.readerIndex(originalReaderIndex); buf.readerIndex(originalReaderIndex);
ctx.fireChannelRead(buf); ctx.fireChannelRead(buf);
} else { } else {
try { try {
doLengthSanityChecks(buf, packet); doLengthSanityChecks(buf, codec);
MinecraftPacket packet;
try { try {
packet.decode(buf, direction, registry.version); packet = codec.decode(buf, direction, registry.version);
} catch (Exception e) { } catch (Exception e) {
throw handleDecodeFailure(e, packet, packetId); throw handleDecodeFailure(e, codec, packetId);
} }
if (buf.isReadable()) { if (buf.isReadable()) {
@@ -95,14 +97,16 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter {
} }
} }
private void doLengthSanityChecks(ByteBuf buf, MinecraftPacket packet) throws Exception { private void doLengthSanityChecks(ByteBuf buf,
int expectedMinLen = packet.decodeExpectedMinLength(buf, direction, registry.version); com.velocitypowered.proxy.protocol.PacketCodec<? extends MinecraftPacket> codec)
int expectedMaxLen = packet.decodeExpectedMaxLength(buf, direction, registry.version); throws Exception {
int expectedMinLen = codec.decodeExpectedMinLength(buf, direction, registry.version);
int expectedMaxLen = codec.decodeExpectedMaxLength(buf, direction, registry.version);
if (expectedMaxLen != -1 && buf.readableBytes() > expectedMaxLen) { if (expectedMaxLen != -1 && buf.readableBytes() > expectedMaxLen) {
throw handleOverflow(packet, expectedMaxLen, buf.readableBytes()); throw handleOverflow(codec, expectedMaxLen, buf.readableBytes());
} }
if (buf.readableBytes() < expectedMinLen) { if (buf.readableBytes() < expectedMinLen) {
throw handleUnderflow(packet, expectedMaxLen, buf.readableBytes()); throw handleUnderflow(codec, expectedMaxLen, buf.readableBytes());
} }
} }
@@ -115,19 +119,34 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter {
} }
} }
private Exception handleUnderflow(MinecraftPacket packet, int expected, int actual) { private Exception handleOverflow(
com.velocitypowered.proxy.protocol.PacketCodec<? extends MinecraftPacket> codec,
int expected, int actual) {
if (DEBUG) { 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<? extends MinecraftPacket> codec,
int expected, int actual) {
if (DEBUG) {
return new CorruptedFrameException("Packet sent for " + codec.getClass() + " was too "
+ "small (expected " + expected + " bytes, got " + actual + " bytes)"); + "small (expected " + expected + " bytes, got " + actual + " bytes)");
} else { } else {
return DECODE_FAILED; return DECODE_FAILED;
} }
} }
private Exception handleDecodeFailure(Exception cause, MinecraftPacket packet, int packetId) { private Exception handleDecodeFailure(Exception cause,
com.velocitypowered.proxy.protocol.PacketCodec<? extends MinecraftPacket> codec,
int packetId) {
if (DEBUG) { if (DEBUG) {
return new CorruptedFrameException( return new CorruptedFrameException(
"Error decoding " + packet.getClass() + " " + getExtraConnectionDetail(packetId), cause); "Error decoding " + codec.getClass() + " " + getExtraConnectionDetail(packetId), cause);
} else { } else {
return DECODE_FAILED; return DECODE_FAILED;
} }

View File

@@ -20,6 +20,7 @@ package com.velocitypowered.proxy.protocol.netty;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.StateRegistry;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@@ -48,16 +49,31 @@ public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> {
} }
@Override @Override
@SuppressWarnings("unchecked")
protected void encode(ChannelHandlerContext ctx, MinecraftPacket msg, ByteBuf out) { protected void encode(ChannelHandlerContext ctx, MinecraftPacket msg, ByteBuf out) {
PacketCodec<MinecraftPacket> codec = (PacketCodec<MinecraftPacket>) this.registry.getCodec(msg.getClass());
if (codec == null) {
throw new IllegalArgumentException("No codec found for packet: " + msg.getClass());
}
int packetId = this.registry.getPacketId(msg); int packetId = this.registry.getPacketId(msg);
ProtocolUtils.writeVarInt(out, packetId); ProtocolUtils.writeVarInt(out, packetId);
msg.encode(out, direction, registry.version); codec.encode(msg, out, direction, registry.version);
} }
@Override @Override
@SuppressWarnings("unchecked")
protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, MinecraftPacket msg, protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, MinecraftPacket msg,
boolean preferDirect) throws Exception { boolean preferDirect) throws Exception {
int hint = msg.encodeSizeHint(direction, registry.version); PacketCodec<MinecraftPacket> codec =
(PacketCodec<MinecraftPacket>)
this.registry.getCodec(msg.getClass());
int hint = -1;
if (codec != null) {
hint = codec.encodeSizeHint(msg, direction, registry.version);
}
if (hint < 0) { if (hint < 0) {
return super.allocateBuffer(ctx, msg, preferDirect); return super.allocateBuffer(ctx, msg, preferDirect);
} }

View File

@@ -20,7 +20,7 @@ package com.velocitypowered.proxy.protocol.netty;
import static io.netty.util.ByteProcessor.FIND_NON_NUL; import static io.netty.util.ByteProcessor.FIND_NON_NUL;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.util.except.QuietDecoderException; import com.velocitypowered.proxy.util.except.QuietDecoderException;
@@ -122,22 +122,21 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
} }
final int payloadLength = length - ProtocolUtils.varIntBytes(packetId); 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 // 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; throw UNKNOWN_PACKET;
} }
// We 'technically' have the incoming bytes of a payload here, and so, these can actually parse // 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 // the packet if needed, so, we'll take advantage of the existing methods
int expectedMinLen = packet.decodeExpectedMinLength(in, direction, registry.version); int expectedMinLen = codec.decodeExpectedMinLength(in, direction, registry.version);
int expectedMaxLen = packet.decodeExpectedMaxLength(in, direction, registry.version); int expectedMaxLen = codec.decodeExpectedMaxLength(in, direction, registry.version);
if (expectedMaxLen != -1 && payloadLength > expectedMaxLen) { if (expectedMaxLen != -1 && payloadLength > expectedMaxLen) {
throw handleOverflow(packet, expectedMaxLen, in.readableBytes()); throw handleOverflow(expectedMaxLen, in.readableBytes());
} }
if (payloadLength < expectedMinLen) { if (payloadLength < expectedMinLen) {
throw handleUnderflow(packet, expectedMaxLen, in.readableBytes()); throw handleUnderflow(expectedMaxLen, in.readableBytes());
} }
in.readerIndex(index); in.readerIndex(index);
@@ -224,18 +223,18 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
return result | (tmp & 0x7F) << 14; return result | (tmp & 0x7F) << 14;
} }
private Exception handleOverflow(MinecraftPacket packet, int expected, int actual) { private Exception handleOverflow(int expected, int actual) {
if (MinecraftDecoder.DEBUG) { 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)"); + "big (expected " + expected + " bytes, got " + actual + " bytes)");
} else { } else {
return FRAME_DECODER_FAILED; return FRAME_DECODER_FAILED;
} }
} }
private Exception handleUnderflow(MinecraftPacket packet, int expected, int actual) { private Exception handleUnderflow(int expected, int actual) {
if (MinecraftDecoder.DEBUG) { 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)"); + "small (expected " + expected + " bytes, got " + actual + " bytes)");
} else { } else {
return FRAME_DECODER_FAILED; return FRAME_DECODER_FAILED;

View File

@@ -37,6 +37,7 @@ import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentPropertyRegistry; import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentPropertyRegistry;
@@ -54,7 +55,7 @@ import java.util.function.Predicate;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class AvailableCommandsPacket implements MinecraftPacket { public final class AvailableCommandsPacket implements MinecraftPacket {
private static final Command<CommandSource> PLACEHOLDER_COMMAND = source -> 0; private static final Command<CommandSource> PLACEHOLDER_COMMAND = source -> 0;
private static final Predicate<CommandSource> PLACEHOLDER_REQUIREMENT = source -> true; private static final Predicate<CommandSource> PLACEHOLDER_REQUIREMENT = source -> true;
@@ -69,160 +70,169 @@ public class AvailableCommandsPacket implements MinecraftPacket {
private static final byte FLAG_HAS_SUGGESTIONS = 0x10; private static final byte FLAG_HAS_SUGGESTIONS = 0x10;
private static final byte FLAG_IS_RESTRICTED = 0x20; private static final byte FLAG_IS_RESTRICTED = 0x20;
private @MonotonicNonNull RootCommandNode<CommandSource> rootNode; private final RootCommandNode<CommandSource> rootNode;
public AvailableCommandsPacket(RootCommandNode<CommandSource> rootNode) {
this.rootNode = rootNode;
}
/**
* Returns the root node.
*
* @return the root node
*/
public RootCommandNode<CommandSource> getRootNode() { public RootCommandNode<CommandSource> getRootNode() {
if (rootNode == null) {
throw new IllegalStateException("Packet not yet deserialized");
}
return rootNode; 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);
}
// Iterate over the deserialized nodes and attempt to form a graph. We also resolve any cycles
// that exist.
Queue<WireNode> nodeQueue = new ArrayDeque<>(Arrays.asList(wireNodes));
while (!nodeQueue.isEmpty()) {
boolean cycling = false;
for (Iterator<WireNode> it = nodeQueue.iterator(); it.hasNext(); ) {
WireNode node = it.next();
if (node.toNode(wireNodes)) {
cycling = true;
it.remove();
}
}
if (!cycling) {
// Uh-oh. We can't cycle. This is bad.
throw new IllegalStateException("Stopped cycling; the root node can't be built.");
}
}
int rootIdx = ProtocolUtils.readVarInt(buf);
rootNode = (RootCommandNode<CommandSource>) wireNodes[rootIdx].built;
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
// Assign all the children an index.
Deque<CommandNode<CommandSource>> childrenQueue = new ArrayDeque<>(ImmutableList.of(rootNode));
Object2IntMap<CommandNode<CommandSource>> idMappings = new Object2IntLinkedOpenCustomHashMap<>(
IdentityHashStrategy.instance());
while (!childrenQueue.isEmpty()) {
CommandNode<CommandSource> child = childrenQueue.poll();
if (!idMappings.containsKey(child)) {
idMappings.put(child, idMappings.size());
childrenQueue.addAll(child.getChildren());
if (child.getRedirect() != null) {
childrenQueue.add(child.getRedirect());
}
}
}
// Now serialize the children.
ProtocolUtils.writeVarInt(buf, idMappings.size());
for (CommandNode<CommandSource> child : idMappings.keySet()) {
serializeNode(child, buf, idMappings, protocolVersion);
}
ProtocolUtils.writeVarInt(buf, idMappings.getInt(rootNode));
}
private static void serializeNode(CommandNode<CommandSource> node, ByteBuf buf,
Object2IntMap<CommandNode<CommandSource>> 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 LiteralCommandNode<?>) {
flags |= NODE_TYPE_LITERAL;
} else if (node instanceof ArgumentCommandNode<?, ?>) {
flags |= NODE_TYPE_ARGUMENT;
if (((ArgumentCommandNode<CommandSource, ?>) node).getCustomSuggestions() != null) {
flags |= FLAG_HAS_SUGGESTIONS;
}
} 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<CommandSource> 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<CommandSource, ?>) node).getType(), protocolVersion);
if (((ArgumentCommandNode<CommandSource, ?>) node).getCustomSuggestions() != null) {
SuggestionProvider<CommandSource> provider = ((ArgumentCommandNode<CommandSource, ?>) 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());
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
private static WireNode deserializeNode(ByteBuf buf, int idx, ProtocolVersion version) { public static class Codec implements PacketCodec<AvailableCommandsPacket> {
byte flags = buf.readByte(); @Override
int[] children = ProtocolUtils.readIntegerArray(buf); public AvailableCommandsPacket decode(ByteBuf buf, Direction direction,
int redirectTo = -1; ProtocolVersion protocolVersion) {
if ((flags & FLAG_IS_REDIRECT) > 0) { int commands = ProtocolUtils.readVarInt(buf);
redirectTo = ProtocolUtils.readVarInt(buf); WireNode[] wireNodes = new WireNode[commands];
for (int i = 0; i < commands; i++) {
wireNodes[i] = deserializeNode(buf, i, protocolVersion);
}
// Iterate over the deserialized nodes and attempt to form a graph. We also resolve any cycles
// that exist.
Queue<WireNode> nodeQueue = new ArrayDeque<>(Arrays.asList(wireNodes));
while (!nodeQueue.isEmpty()) {
boolean cycling = false;
for (Iterator<WireNode> it = nodeQueue.iterator(); it.hasNext(); ) {
WireNode node = it.next();
if (node.toNode(wireNodes)) {
cycling = true;
it.remove();
}
}
if (!cycling) {
// Uh-oh. We can't cycle. This is bad.
throw new IllegalStateException("Stopped cycling; the root node can't be built.");
}
}
int rootIdx = ProtocolUtils.readVarInt(buf);
RootCommandNode<CommandSource> rootNode = (RootCommandNode<CommandSource>) wireNodes[rootIdx].built;
return new AvailableCommandsPacket(rootNode);
} }
switch (flags & FLAG_NODE_TYPE) { @Override
case NODE_TYPE_ROOT: public void encode(AvailableCommandsPacket packet, ByteBuf buf, Direction direction,
return new WireNode(idx, flags, children, redirectTo, null); ProtocolVersion protocolVersion) {
case NODE_TYPE_LITERAL: // Assign all the children an index.
return new WireNode(idx, flags, children, redirectTo, LiteralArgumentBuilder Deque<CommandNode<CommandSource>> childrenQueue = new ArrayDeque<>(ImmutableList.of(packet.rootNode));
.literal(ProtocolUtils.readString(buf))); Object2IntMap<CommandNode<CommandSource>> idMappings = new Object2IntLinkedOpenCustomHashMap<>(
case NODE_TYPE_ARGUMENT: IdentityHashStrategy.instance());
String name = ProtocolUtils.readString(buf); while (!childrenQueue.isEmpty()) {
ArgumentType<?> argumentType = ArgumentPropertyRegistry.deserialize(buf, version); CommandNode<CommandSource> child = childrenQueue.poll();
if (!idMappings.containsKey(child)) {
RequiredArgumentBuilder<CommandSource, ?> argumentBuilder = RequiredArgumentBuilder idMappings.put(child, idMappings.size());
.argument(name, argumentType); childrenQueue.addAll(child.getChildren());
if ((flags & FLAG_HAS_SUGGESTIONS) != 0) { if (child.getRedirect() != null) {
argumentBuilder.suggests(new ProtocolSuggestionProvider(ProtocolUtils.readString(buf))); childrenQueue.add(child.getRedirect());
}
} }
return new WireNode(idx, flags, children, redirectTo, argumentBuilder); }
default:
throw new IllegalArgumentException("Unknown node type " + (flags & FLAG_NODE_TYPE)); // Now serialize the children.
ProtocolUtils.writeVarInt(buf, idMappings.size());
for (CommandNode<CommandSource> child : idMappings.keySet()) {
serializeNode(child, buf, idMappings, protocolVersion);
}
ProtocolUtils.writeVarInt(buf, idMappings.getInt(packet.rootNode));
}
private static void serializeNode(CommandNode<CommandSource> node, ByteBuf buf,
Object2IntMap<CommandNode<CommandSource>> 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 LiteralCommandNode<?>) {
flags |= NODE_TYPE_LITERAL;
} else if (node instanceof ArgumentCommandNode<?, ?>) {
flags |= NODE_TYPE_ARGUMENT;
if (((ArgumentCommandNode<CommandSource, ?>) node).getCustomSuggestions() != null) {
flags |= FLAG_HAS_SUGGESTIONS;
}
} 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<CommandSource> 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<CommandSource, ?>) node).getType(), protocolVersion);
if (((ArgumentCommandNode<CommandSource, ?>) node).getCustomSuggestions() != null) {
SuggestionProvider<CommandSource> provider = ((ArgumentCommandNode<CommandSource, ?>) 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());
}
}
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<CommandSource, ?> 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 +372,4 @@ public class AvailableCommandsPacket implements MinecraftPacket {
return builder.buildFuture(); return builder.buildFuture();
} }
} }
@Override
public int encodeSizeHint(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;
}
} }

View File

@@ -20,6 +20,7 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import com.velocitypowered.proxy.util.collect.Enum2IntMap; import com.velocitypowered.proxy.util.collect.Enum2IntMap;
@@ -29,7 +30,8 @@ import java.util.UUID;
import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.bossbar.BossBar;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class BossBarPacket implements MinecraftPacket { public record BossBarPacket(UUID uuid, int action, @Nullable ComponentHolder name,
float percent, int color, int overlay, short flags) implements MinecraftPacket {
private static final Enum2IntMap<BossBar.Color> COLORS_TO_PROTOCOL = private static final Enum2IntMap<BossBar.Color> COLORS_TO_PROTOCOL =
new Enum2IntMap.Builder<>(BossBar.Color.class) new Enum2IntMap.Builder<>(BossBar.Color.class)
@@ -62,43 +64,31 @@ public class BossBarPacket implements MinecraftPacket {
public static final int UPDATE_NAME = 3; public static final int UPDATE_NAME = 3;
public static final int UPDATE_STYLE = 4; public static final int UPDATE_STYLE = 4;
public static final int UPDATE_PROPERTIES = 5; public static final int UPDATE_PROPERTIES = 5;
private @Nullable UUID uuid;
private int action; public UUID getUuid() {
private @Nullable ComponentHolder name; return uuid;
private float percent; }
private int color;
private int overlay; public int getAction() {
private short flags; return action;
}
public static BossBarPacket createAddPacket( public static BossBarPacket createAddPacket(
final UUID id, final UUID id,
final BossBar bar, final BossBar bar,
final ComponentHolder name final ComponentHolder name
) { ) {
final BossBarPacket packet = new BossBarPacket(); return new BossBarPacket(id, ADD, name, bar.progress(),
packet.setUuid(id); COLORS_TO_PROTOCOL.get(bar.color()), OVERLAY_TO_PROTOCOL.get(bar.overlay()),
packet.setAction(BossBarPacket.ADD); serializeFlags(bar.flags()));
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;
} }
public static BossBarPacket createRemovePacket(final UUID id, final BossBar bar) { public static BossBarPacket createRemovePacket(final UUID id) {
final BossBarPacket packet = new BossBarPacket(); return new BossBarPacket(id, REMOVE, null, 0, 0, 0, (short) 0);
packet.setUuid(id);
packet.setAction(REMOVE);
return packet;
} }
public static BossBarPacket createUpdateProgressPacket(final UUID id, final BossBar bar) { public static BossBarPacket createUpdateProgressPacket(final UUID id, final BossBar bar) {
final BossBarPacket packet = new BossBarPacket(); return new BossBarPacket(id, UPDATE_PERCENT, null, bar.progress(), 0, 0, (short) 0);
packet.setUuid(id);
packet.setAction(UPDATE_PERCENT);
packet.setPercent(bar.progress());
return packet;
} }
public static BossBarPacket createUpdateNamePacket( public static BossBarPacket createUpdateNamePacket(
@@ -106,177 +96,20 @@ public class BossBarPacket implements MinecraftPacket {
final BossBar bar, final BossBar bar,
final ComponentHolder name final ComponentHolder name
) { ) {
final BossBarPacket packet = new BossBarPacket(); return new BossBarPacket(id, UPDATE_NAME, name, 0, 0, 0, (short) 0);
packet.setUuid(id);
packet.setAction(UPDATE_NAME);
packet.setName(name);
return packet;
} }
public static BossBarPacket createUpdateStylePacket(final UUID id, final BossBar bar) { public static BossBarPacket createUpdateStylePacket(final UUID id, final BossBar bar) {
final BossBarPacket packet = new BossBarPacket(); return new BossBarPacket(id, UPDATE_STYLE, null, 0,
packet.setUuid(id); COLORS_TO_PROTOCOL.get(bar.color()), OVERLAY_TO_PROTOCOL.get(bar.overlay()), (short) 0);
packet.setAction(UPDATE_STYLE);
packet.setColor(COLORS_TO_PROTOCOL.get(bar.color()));
packet.setOverlay(OVERLAY_TO_PROTOCOL.get(bar.overlay()));
return packet;
} }
public static BossBarPacket createUpdatePropertiesPacket(final UUID id, final BossBar bar) { public static BossBarPacket createUpdatePropertiesPacket(final UUID id, final BossBar bar) {
final BossBarPacket packet = new BossBarPacket(); return new BossBarPacket(id, UPDATE_PROPERTIES, null, 0, 0, 0, serializeFlags(bar.flags()));
packet.setUuid(id);
packet.setAction(UPDATE_PROPERTIES);
packet.setFlags(serializeFlags(bar.flags()));
return packet;
} }
public UUID getUuid() { private static short serializeFlags(Set<BossBar.Flag> flags) {
if (uuid == null) { short val = 0x0;
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<BossBar.Flag> flags) {
byte val = 0x0;
for (BossBar.Flag flag : flags) { for (BossBar.Flag flag : flags) {
val |= FLAG_BITS_TO_PROTOCOL.get(flag); val |= FLAG_BITS_TO_PROTOCOL.get(flag);
} }
@@ -287,4 +120,86 @@ public class BossBarPacket implements MinecraftPacket {
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<BossBarPacket> {
@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);
}
}
}
} }

View File

@@ -20,6 +20,7 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@@ -29,18 +30,21 @@ public final class BundleDelimiterPacket implements MinecraftPacket {
private BundleDelimiterPacket() { private BundleDelimiterPacket() {
} }
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<BundleDelimiterPacket> {
@Override
public BundleDelimiterPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
return INSTANCE;
}
@Override
public void encode(BundleDelimiterPacket packet, ByteBuf buf,
ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
}
}
} }

View File

@@ -20,34 +20,46 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.Objects; import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class ClientSettingsPacket implements MinecraftPacket { public final class ClientSettingsPacket implements MinecraftPacket {
private @Nullable String locale; private final @Nullable String locale;
private byte viewDistance; private final byte viewDistance;
private int chatVisibility; private final int chatVisibility;
private boolean chatColors; private final boolean chatColors;
private byte difficulty; // 1.7 Protocol private final byte difficulty; // 1.7 Protocol
private short skinParts; private final short skinParts;
private int mainHand; private final int mainHand;
private boolean textFilteringEnabled; // Added in 1.17 private final boolean textFilteringEnabled; // Added in 1.17
private boolean clientListingAllowed; // Added in 1.18, overwrites server-list "anonymous" mode private final boolean clientListingAllowed; // Added in 1.18, overwrites server-list "anonymous" mode
private int particleStatus; // Added in 1.21.2 private final int particleStatus; // Added in 1.21.2
public ClientSettingsPacket() { 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, public ClientSettingsPacket(String locale, byte viewDistance, int chatVisibility,
short skinParts, int mainHand, boolean textFilteringEnabled, boolean clientListingAllowed, boolean chatColors, short skinParts, int mainHand,
int particleStatus) { 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.locale = locale;
this.viewDistance = viewDistance; this.viewDistance = viewDistance;
this.chatVisibility = chatVisibility; this.chatVisibility = chatVisibility;
this.chatColors = chatColors; this.chatColors = chatColors;
this.difficulty = difficulty;
this.skinParts = skinParts; this.skinParts = skinParts;
this.mainHand = mainHand; this.mainHand = mainHand;
this.textFilteringEnabled = textFilteringEnabled; this.textFilteringEnabled = textFilteringEnabled;
@@ -55,150 +67,51 @@ public class ClientSettingsPacket implements MinecraftPacket {
this.particleStatus = particleStatus; this.particleStatus = particleStatus;
} }
public String getLocale() { public String locale() {
if (locale == null) { if (locale == null) {
throw new IllegalStateException("No locale specified"); throw new IllegalStateException("No locale specified");
} }
return locale; return locale;
} }
public void setLocale(String locale) { public byte viewDistance() {
this.locale = locale;
}
public byte getViewDistance() {
return viewDistance; return viewDistance;
} }
public void setViewDistance(byte viewDistance) { public int chatVisibility() {
this.viewDistance = viewDistance;
}
public int getChatVisibility() {
return chatVisibility; return chatVisibility;
} }
public void setChatVisibility(int chatVisibility) { public boolean chatColors() {
this.chatVisibility = chatVisibility;
}
public boolean isChatColors() {
return chatColors; return chatColors;
} }
public void setChatColors(boolean chatColors) { public byte difficulty() {
this.chatColors = chatColors; return difficulty;
} }
public short getSkinParts() { public short skinParts() {
return skinParts; return skinParts;
} }
public void setSkinParts(short skinParts) { public int mainHand() {
this.skinParts = skinParts;
}
public int getMainHand() {
return mainHand; return mainHand;
} }
public void setMainHand(int mainHand) { public boolean textFilteringEnabled() {
this.mainHand = mainHand;
}
public boolean isTextFilteringEnabled() {
return textFilteringEnabled; return textFilteringEnabled;
} }
public void setTextFilteringEnabled(boolean textFilteringEnabled) { public boolean clientListingAllowed() {
this.textFilteringEnabled = textFilteringEnabled;
}
public boolean isClientListingAllowed() {
return clientListingAllowed; return clientListingAllowed;
} }
public void setClientListingAllowed(boolean clientListingAllowed) { public int particleStatus() {
this.clientListingAllowed = clientListingAllowed;
}
public int getParticleStatus() {
return particleStatus; return particleStatus;
} }
public void setParticleStatus(int particleStatus) { public String getLocale() {
this.particleStatus = particleStatus; return locale();
}
@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);
}
}
}
} }
@Override @Override
@@ -227,18 +140,79 @@ public class ClientSettingsPacket implements MinecraftPacket {
&& Objects.equals(locale, that.locale); && Objects.equals(locale, that.locale);
} }
@Override public static class Codec implements PacketCodec<ClientSettingsPacket> {
public int hashCode() { @Override
return Objects.hash( public ClientSettingsPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
locale, ProtocolVersion version) {
viewDistance, String locale = ProtocolUtils.readString(buf, 16);
chatVisibility, byte viewDistance = buf.readByte();
chatColors, int chatVisibility = ProtocolUtils.readVarInt(buf);
difficulty, boolean chatColors = buf.readBoolean();
skinParts, byte difficulty = 0;
mainHand,
textFilteringEnabled, if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_7_6)) {
clientListingAllowed, difficulty = buf.readByte();
particleStatus); }
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);
}
}
}
}
} }
} }

View File

@@ -20,38 +20,34 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
public class ClientboundCookieRequestPacket implements MinecraftPacket { public record ClientboundCookieRequestPacket(Key key) implements MinecraftPacket {
private Key key;
public Key getKey() { public Key getKey() {
return key; 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);
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeKey(buf, key);
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<ClientboundCookieRequestPacket> {
@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);
}
}
} }

View File

@@ -20,82 +20,50 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.concurrent.ThreadLocalRandom;
import net.kyori.adventure.sound.Sound; import net.kyori.adventure.sound.Sound;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Random;
public class ClientboundSoundEntityPacket implements MinecraftPacket { public record ClientboundSoundEntityPacket(Sound sound, @Nullable Float fixedRange,
int emitterEntityId) 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;
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
throw new UnsupportedOperationException("Decode is not implemented");
}
@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);
buf.writeFloat(sound.volume());
buf.writeFloat(sound.pitch());
buf.writeLong(sound.seed().orElse(SEEDS_RANDOM.nextLong()));
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public Sound getSound() { public static class Codec implements PacketCodec<ClientboundSoundEntityPacket> {
return sound; @Override
} public ClientboundSoundEntityPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
throw new UnsupportedOperationException("Decode is not implemented");
}
public void setSound(Sound sound) { @Override
this.sound = sound; public void encode(ClientboundSoundEntityPacket packet, ByteBuf buf,
} ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, 0); // version-dependent, hardcoded sound ID
public @Nullable Float getFixedRange() { ProtocolUtils.writeMinimalKey(buf, packet.sound.name());
return fixedRange;
}
public void setFixedRange(@Nullable Float fixedRange) { buf.writeBoolean(packet.fixedRange != null);
this.fixedRange = fixedRange; if (packet.fixedRange != null) {
} buf.writeFloat(packet.fixedRange);
}
public int getEmitterEntityId() { ProtocolUtils.writeSoundSource(buf, protocolVersion, packet.sound.source());
return emitterEntityId;
}
public void setEmitterEntityId(int emitterEntityId) { ProtocolUtils.writeVarInt(buf, packet.emitterEntityId);
this.emitterEntityId = emitterEntityId;
}
buf.writeFloat(packet.sound.volume());
buf.writeFloat(packet.sound.pitch());
buf.writeLong(packet.sound.seed().orElse(ThreadLocalRandom.current().nextLong()));
}
}
} }

View File

@@ -20,6 +20,7 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
@@ -28,82 +29,58 @@ import net.kyori.adventure.sound.SoundStop;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class ClientboundStopSoundPacket implements MinecraftPacket { public record ClientboundStopSoundPacket(@Nullable Sound.Source source,
@Nullable Key soundName) implements MinecraftPacket {
private @Nullable Sound.Source source;
private @Nullable Key soundName;
public ClientboundStopSoundPacket() {}
public ClientboundStopSoundPacket(SoundStop soundStop) { public ClientboundStopSoundPacket(SoundStop soundStop) {
this(soundStop.source(), soundStop.sound()); this(soundStop.source(), soundStop.sound());
} }
public ClientboundStopSoundPacket(@Nullable Sound.Source source, @Nullable Key soundName) {
this.source = source;
this.soundName = soundName;
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
int flagsBitmask = buf.readByte();
if ((flagsBitmask & 1) != 0) {
source = ProtocolUtils.readSoundSource(buf, protocolVersion);
} else {
source = null;
}
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;
}
buf.writeByte(flagsBitmask);
if (source != null) {
ProtocolUtils.writeSoundSource(buf, protocolVersion, source);
}
if (soundName != null) {
ProtocolUtils.writeMinimalKey(buf, soundName);
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
@Nullable public static class Codec implements PacketCodec<ClientboundStopSoundPacket> {
public Sound.Source getSource() { @Override
return source; public ClientboundStopSoundPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
} ProtocolVersion protocolVersion) {
int flagsBitmask = buf.readByte();
public void setSource(@Nullable Sound.Source source) { Sound.Source source = null;
this.source = source; if ((flagsBitmask & 1) != 0) {
} source = ProtocolUtils.readSoundSource(buf, protocolVersion);
}
@Nullable Key soundName = null;
public Key getSoundName() { if ((flagsBitmask & 2) != 0) {
return soundName; soundName = ProtocolUtils.readKey(buf);
} }
public void setSoundName(@Nullable Key soundName) { return new ClientboundStopSoundPacket(source, soundName);
this.soundName = 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);
}
}
}
} }

View File

@@ -20,46 +20,32 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
public class ClientboundStoreCookiePacket implements MinecraftPacket { public record ClientboundStoreCookiePacket(Key key, byte[] payload) 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);
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<ClientboundStoreCookiePacket> {
@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);
}
}
} }

View File

@@ -20,26 +20,32 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; 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; import io.netty.buffer.ByteBuf;
public class DialogClearPacket implements MinecraftPacket { public final class DialogClearPacket implements MinecraftPacket {
public static final DialogClearPacket INSTANCE = new DialogClearPacket(); public static final DialogClearPacket INSTANCE = new DialogClearPacket();
private DialogClearPacket() { private DialogClearPacket() {
} }
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<DialogClearPacket> {
@Override
public DialogClearPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
return INSTANCE;
}
@Override
public void encode(DialogClearPacket packet, ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
}
}
} }

View File

@@ -20,45 +20,71 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.StateRegistry;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.BinaryTagIO; import net.kyori.adventure.nbt.BinaryTagIO;
public class DialogShowPacket implements MinecraftPacket { public final class DialogShowPacket implements MinecraftPacket {
private final StateRegistry state; private final StateRegistry state;
private int id; private final int id;
private BinaryTag nbt; private final BinaryTag nbt;
public DialogShowPacket(final StateRegistry state) { public DialogShowPacket(StateRegistry state, int id, BinaryTag nbt) {
this.state = state; this.state = state;
this.id = id;
this.nbt = nbt;
} }
@Override public StateRegistry state() {
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { return state;
this.id = this.state == StateRegistry.CONFIG ? 0 : ProtocolUtils.readVarInt(buf);
if (this.id == 0) {
this.nbt = ProtocolUtils.readBinaryTag(buf, protocolVersion, BinaryTagIO.reader());
}
} }
@Override public int id() {
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { return id;
if (this.state == StateRegistry.CONFIG) { }
ProtocolUtils.writeBinaryTag(buf, protocolVersion, this.nbt);
} else { public BinaryTag nbt() {
ProtocolUtils.writeVarInt(buf, this.id); return nbt;
if (this.id == 0) {
ProtocolUtils.writeBinaryTag(buf, protocolVersion, this.nbt);
}
}
} }
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<DialogShowPacket> {
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);
}
}
}
}
} }

View File

@@ -21,36 +21,36 @@ import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
public class DisconnectPacket implements MinecraftPacket { public final class DisconnectPacket implements MinecraftPacket {
private @Nullable ComponentHolder reason; private final ComponentHolder reason;
private final StateRegistry state; private final StateRegistry state;
public DisconnectPacket(StateRegistry state) { public DisconnectPacket(StateRegistry state, ComponentHolder reason) {
this.state = state;
}
private DisconnectPacket(StateRegistry state, ComponentHolder reason) {
this.state = state; this.state = state;
this.reason = Preconditions.checkNotNull(reason, "reason"); this.reason = Preconditions.checkNotNull(reason, "reason");
} }
public ComponentHolder getReason() { public ComponentHolder reason() {
if (reason == null) { if (reason == null) {
throw new IllegalStateException("No reason specified"); throw new IllegalStateException("No reason specified");
} }
return reason; return reason;
} }
public void setReason(@Nullable ComponentHolder reason) { public ComponentHolder getReason() {
this.reason = reason; return reason();
}
public StateRegistry state() {
return state;
} }
@Override @Override
@@ -60,25 +60,37 @@ public class DisconnectPacket implements MinecraftPacket {
+ '}'; + '}';
} }
@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);
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); 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"); Preconditions.checkNotNull(component, "component");
return new DisconnectPacket(state, new ComponentHolder(state == StateRegistry.LOGIN return new DisconnectPacket(state, new ComponentHolder(state == StateRegistry.LOGIN
? ProtocolVersion.MINECRAFT_1_20_2 : version, component)); ? ProtocolVersion.MINECRAFT_1_20_2 : version, component));
} }
}
public static class Codec implements PacketCodec<DisconnectPacket> {
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);
}
}
}

View File

@@ -17,36 +17,52 @@
package com.velocitypowered.proxy.protocol.packet; 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.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.Arrays; import java.util.Arrays;
public class EncryptionRequestPacket implements MinecraftPacket { public final class EncryptionRequestPacket implements MinecraftPacket {
private String serverId = ""; private final String serverId;
private byte[] publicKey = EMPTY_BYTE_ARRAY; private final byte[] publicKey;
private byte[] verifyToken = EMPTY_BYTE_ARRAY; private final byte[] verifyToken;
private boolean shouldAuthenticate = true; private final boolean shouldAuthenticate;
public byte[] getPublicKey() { 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 String serverId() {
return serverId;
}
public byte[] publicKey() {
return publicKey.clone(); return publicKey.clone();
} }
public void setPublicKey(byte[] publicKey) { public byte[] verifyToken() {
this.publicKey = publicKey.clone();
}
public byte[] getVerifyToken() {
return verifyToken.clone(); return verifyToken.clone();
} }
public void setVerifyToken(byte[] verifyToken) { public boolean shouldAuthenticate() {
this.verifyToken = verifyToken.clone(); return shouldAuthenticate;
}
public byte[] getPublicKey() {
return publicKey();
}
public byte[] getVerifyToken() {
return verifyToken();
} }
@Override @Override
@@ -57,40 +73,49 @@ public class EncryptionRequestPacket implements MinecraftPacket {
+ '}'; + '}';
} }
@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();
}
} else {
publicKey = ProtocolUtils.readByteArray17(buf);
verifyToken = ProtocolUtils.readByteArray17(buf);
}
}
@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);
}
} else {
ProtocolUtils.writeByteArray17(publicKey, buf, false);
ProtocolUtils.writeByteArray17(verifyToken, buf, false);
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<EncryptionRequestPacket> {
@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);
}
return new EncryptionRequestPacket(serverId, publicKey, verifyToken, 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);
}
}
}
} }

View File

@@ -17,43 +17,60 @@
package com.velocitypowered.proxy.protocol.packet; 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.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.util.except.QuietDecoderException; import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Arrays; import java.util.Arrays;
public class EncryptionResponsePacket implements MinecraftPacket { public final class EncryptionResponsePacket implements MinecraftPacket {
private static final QuietDecoderException NO_SALT = new QuietDecoderException( private static final QuietDecoderException NO_SALT = new QuietDecoderException(
"Encryption response didn't contain salt"); "Encryption response didn't contain salt");
private byte[] sharedSecret = EMPTY_BYTE_ARRAY; private final byte[] sharedSecret;
private byte[] verifyToken = EMPTY_BYTE_ARRAY; private final byte[] verifyToken;
private @Nullable Long salt; 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(); return sharedSecret.clone();
} }
public byte[] getVerifyToken() { public byte[] verifyToken() {
return verifyToken.clone(); return verifyToken.clone();
} }
public long getSalt() { public long salt() {
if (salt == null) { if (salt == null) {
throw NO_SALT; throw NO_SALT;
} }
return salt; return salt;
} }
public byte[] getSharedSecret() {
return sharedSecret();
}
public byte[] getVerifyToken() {
return verifyToken();
}
public long getSalt() {
return salt();
}
@Override @Override
public String toString() { public String toString() {
return "EncryptionResponse{" return "EncryptionResponse{"
@@ -62,73 +79,85 @@ public class EncryptionResponsePacket implements MinecraftPacket {
+ '}'; + '}';
} }
@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();
}
this.verifyToken = ProtocolUtils.readByteArray(buf,
version.noLessThan(ProtocolVersion.MINECRAFT_1_19) ? 256 : 128);
} else {
this.sharedSecret = ProtocolUtils.readByteArray17(buf);
this.verifyToken = ProtocolUtils.readByteArray17(buf);
}
}
@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);
}
}
ProtocolUtils.writeByteArray(buf, verifyToken);
} else {
ProtocolUtils.writeByteArray17(sharedSecret, buf, false);
ProtocolUtils.writeByteArray17(verifyToken, buf, false);
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
@Override public static class Codec implements PacketCodec<EncryptionResponsePacket> {
public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { @Override
// It turns out these come out to the same length, whether we're talking >=1.8 or not. public EncryptionResponsePacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
// The length prefix always winds up being 2 bytes. ProtocolVersion version) {
int base = 256 + 2 + 2; byte[] sharedSecret;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_3)) { byte[] verifyToken;
return base + 128; Long salt = null;
}
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;
}
@Override if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { sharedSecret = ProtocolUtils.readByteArray(buf, 128);
int base = decodeExpectedMaxLength(buf, direction, version);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)
// These are "optional" && version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)
base -= 128 + 8; && !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);
}
return new EncryptionResponsePacket(sharedSecret, verifyToken, salt);
}
@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);
}
}
@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;
}
@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;
} }
} }

View File

@@ -23,58 +23,71 @@ import com.velocitypowered.api.network.HandshakeIntent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class HandshakePacket implements MinecraftPacket { public final class HandshakePacket implements MinecraftPacket {
// This size was chosen to ensure Forge clients can still connect even with very long hostnames. // 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. // 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 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() { private final ProtocolVersion protocolVersion;
return protocolVersion; private final String serverAddress;
private final int port;
private final HandshakeIntent intent;
private final int nextStatus;
public HandshakePacket() {
this(ProtocolVersion.MINIMUM_VERSION, "", 0, HandshakeIntent.LOGIN);
} }
public void setProtocolVersion(ProtocolVersion protocolVersion) { public HandshakePacket(ProtocolVersion protocolVersion, String serverAddress, int port,
HandshakeIntent intent) {
this.protocolVersion = protocolVersion; this.protocolVersion = protocolVersion;
}
public String getServerAddress() {
return serverAddress;
}
public void setServerAddress(String serverAddress) {
this.serverAddress = serverAddress; this.serverAddress = serverAddress;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port; this.port = port;
}
public int getNextStatus() {
return this.nextStatus;
}
public void setIntent(HandshakeIntent intent) {
this.intent = intent; this.intent = intent;
this.nextStatus = intent.id(); this.nextStatus = intent.id();
} }
public HandshakeIntent getIntent() { public ProtocolVersion protocolVersion() {
return protocolVersion;
}
public String serverAddress() {
return serverAddress;
}
public int port() {
return port;
}
public int nextStatus() {
return this.nextStatus;
}
public HandshakeIntent intent() {
return this.intent; return this.intent;
} }
public ProtocolVersion getProtocolVersion() {
return protocolVersion();
}
public String getServerAddress() {
return serverAddress();
}
public int getPort() {
return port();
}
public HandshakeIntent getIntent() {
return intent();
}
@Override @Override
public String toString() { public String toString() {
return "Handshake{" return "Handshake{"
@@ -85,45 +98,50 @@ public class HandshakePacket implements MinecraftPacket {
+ '}'; + '}';
} }
@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);
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
@Override public static class Codec implements PacketCodec<HandshakePacket> {
public int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, @Override
ProtocolVersion version) { public HandshakePacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
return 7; 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 @Override
public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, public void encode(HandshakePacket packet, ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) { ProtocolVersion ignored) {
return 9 + (MAXIMUM_HOSTNAME_LENGTH * 3); ProtocolUtils.writeVarInt(buf, packet.protocolVersion.getProtocol());
} ProtocolUtils.writeString(buf, packet.serverAddress);
buf.writeShort(packet.port);
ProtocolUtils.writeVarInt(buf, packet.nextStatus);
}
@Override @Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) { public int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction,
// We could compute an exact size, but 4KiB ought to be enough to encode all reasonable ProtocolVersion version) {
// sizes of this packet. return 7;
return 4 * 1024; }
@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;
}
} }
} }

View File

@@ -21,42 +21,18 @@ import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
public class HeaderAndFooterPacket implements MinecraftPacket { public record HeaderAndFooterPacket(ComponentHolder header,
ComponentHolder footer) implements MinecraftPacket {
private final ComponentHolder header; public HeaderAndFooterPacket {
private final ComponentHolder footer; Preconditions.checkNotNull(header, "header");
Preconditions.checkNotNull(footer, "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);
} }
@Override @Override
@@ -74,4 +50,19 @@ public class HeaderAndFooterPacket implements MinecraftPacket {
ComponentHolder empty = new ComponentHolder(version, Component.empty()); ComponentHolder empty = new ComponentHolder(version, Component.empty());
return new HeaderAndFooterPacket(empty, empty); return new HeaderAndFooterPacket(empty, empty);
} }
}
public static class Codec implements PacketCodec<HeaderAndFooterPacket> {
@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);
}
}
}

View File

@@ -22,62 +22,83 @@ import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.registry.DimensionInfo; import com.velocitypowered.proxy.connection.registry.DimensionInfo;
import com.velocitypowered.proxy.protocol.*; import com.velocitypowered.proxy.protocol.*;
import com.velocitypowered.proxy.protocol.PacketCodec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.Pair;
import net.kyori.adventure.nbt.BinaryTagIO; import net.kyori.adventure.nbt.BinaryTagIO;
import net.kyori.adventure.nbt.CompoundBinaryTag; import net.kyori.adventure.nbt.CompoundBinaryTag;
import org.checkerframework.checker.nullness.qual.Nullable; 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 static final BinaryTagIO.Reader JOINGAME_READER = BinaryTagIO.reader(4 * 1024 * 1024);
private int entityId;
private short gamemode; private final int entityId;
private int dimension; private final short gamemode;
private long partialHashedSeed; // 1.15+ private final int dimension;
private short difficulty; private final long partialHashedSeed;
private boolean isHardcore; private final short difficulty;
private int maxPlayers; private final boolean isHardcore;
private @Nullable String levelType; private final int maxPlayers;
private int viewDistance; // 1.14+ private final @Nullable String levelType;
private boolean reducedDebugInfo; private final int viewDistance;
private boolean showRespawnScreen; private final boolean reducedDebugInfo;
private boolean doLimitedCrafting; // 1.20.2+ private final boolean showRespawnScreen;
private ImmutableSet<String> levelNames; // 1.16+ private final boolean doLimitedCrafting;
private CompoundBinaryTag registry; // 1.16+ private final ImmutableSet<String> levelNames;
private DimensionInfo dimensionInfo; // 1.16+ private final CompoundBinaryTag registry;
private CompoundBinaryTag currentDimensionData; // 1.16.2+ private final DimensionInfo dimensionInfo;
private short previousGamemode; // 1.16+ private final CompoundBinaryTag currentDimensionData;
private int simulationDistance; // 1.18+ private final short previousGamemode;
private @Nullable Pair<String, Long> lastDeathPosition; // 1.19+ private final int simulationDistance;
private int portalCooldown; // 1.20+ private final @Nullable Pair<String, Long> lastDeathPosition;
private int seaLevel; // 1.21.2+ private final int portalCooldown;
private boolean enforcesSecureChat; // 1.20.5+ 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<String> levelNames, CompoundBinaryTag registry,
DimensionInfo dimensionInfo, CompoundBinaryTag currentDimensionData, short previousGamemode,
int simulationDistance, @Nullable Pair<String, Long> 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 int getEntityId() { public int getEntityId() {
return entityId; return entityId;
} }
public void setEntityId(int entityId) {
this.entityId = entityId;
}
public short getGamemode() { public short getGamemode() {
return gamemode; return gamemode;
} }
public void setGamemode(short gamemode) {
this.gamemode = gamemode;
}
public int getDimension() { public int getDimension() {
return dimension; return dimension;
} }
public void setDimension(int dimension) {
this.dimension = dimension;
}
public long getPartialHashedSeed() { public long getPartialHashedSeed() {
return partialHashedSeed; return partialHashedSeed;
} }
@@ -86,74 +107,38 @@ public class JoinGamePacket implements MinecraftPacket {
return difficulty; return difficulty;
} }
public void setDifficulty(short difficulty) {
this.difficulty = difficulty;
}
public int getMaxPlayers() { public int getMaxPlayers() {
return maxPlayers; return maxPlayers;
} }
public void setMaxPlayers(int maxPlayers) {
this.maxPlayers = maxPlayers;
}
public @Nullable String getLevelType() { public @Nullable String getLevelType() {
return levelType; return levelType;
} }
public void setLevelType(@Nullable String levelType) {
this.levelType = levelType;
}
public int getViewDistance() { public int getViewDistance() {
return viewDistance; return viewDistance;
} }
public void setViewDistance(int viewDistance) {
this.viewDistance = viewDistance;
}
public boolean isReducedDebugInfo() { public boolean isReducedDebugInfo() {
return reducedDebugInfo; return reducedDebugInfo;
} }
public void setReducedDebugInfo(boolean reducedDebugInfo) {
this.reducedDebugInfo = reducedDebugInfo;
}
public DimensionInfo getDimensionInfo() { public DimensionInfo getDimensionInfo() {
return dimensionInfo; return dimensionInfo;
} }
public void setDimensionInfo(DimensionInfo dimensionInfo) {
this.dimensionInfo = dimensionInfo;
}
public short getPreviousGamemode() { public short getPreviousGamemode() {
return previousGamemode; return previousGamemode;
} }
public void setPreviousGamemode(short previousGamemode) {
this.previousGamemode = previousGamemode;
}
public boolean getIsHardcore() { public boolean getIsHardcore() {
return isHardcore; return isHardcore;
} }
public void setIsHardcore(boolean isHardcore) {
this.isHardcore = isHardcore;
}
public boolean getDoLimitedCrafting() { public boolean getDoLimitedCrafting() {
return doLimitedCrafting; return doLimitedCrafting;
} }
public void setDoLimitedCrafting(boolean doLimitedCrafting) {
this.doLimitedCrafting = doLimitedCrafting;
}
public CompoundBinaryTag getCurrentDimensionData() { public CompoundBinaryTag getCurrentDimensionData() {
return currentDimensionData; return currentDimensionData;
} }
@@ -162,46 +147,34 @@ public class JoinGamePacket implements MinecraftPacket {
return simulationDistance; return simulationDistance;
} }
public void setSimulationDistance(int simulationDistance) {
this.simulationDistance = simulationDistance;
}
public Pair<String, Long> getLastDeathPosition() { public Pair<String, Long> getLastDeathPosition() {
return lastDeathPosition; return lastDeathPosition;
} }
public void setLastDeathPosition(Pair<String, Long> lastDeathPosition) {
this.lastDeathPosition = lastDeathPosition;
}
public int getPortalCooldown() { public int getPortalCooldown() {
return portalCooldown; return portalCooldown;
} }
public void setPortalCooldown(int portalCooldown) {
this.portalCooldown = portalCooldown;
}
public int getSeaLevel() { public int getSeaLevel() {
return seaLevel; return seaLevel;
} }
public void setSeaLevel(int seaLevel) {
this.seaLevel = seaLevel;
}
public boolean getEnforcesSecureChat() { public boolean getEnforcesSecureChat() {
return this.enforcesSecureChat; return this.enforcesSecureChat;
} }
public void setEnforcesSecureChat(final boolean enforcesSecureChat) {
this.enforcesSecureChat = enforcesSecureChat;
}
public CompoundBinaryTag getRegistry() { public CompoundBinaryTag getRegistry() {
return registry; return registry;
} }
public boolean getShowRespawnScreen() {
return showRespawnScreen;
}
public ImmutableSet<String> getLevelNames() {
return levelNames;
}
@Override @Override
public String toString() { public String toString() {
return "JoinGame{" + "entityId=" + entityId + ", gamemode=" + gamemode + ", dimension=" + return "JoinGame{" + "entityId=" + entityId + ", gamemode=" + gamemode + ", dimension=" +
@@ -217,306 +190,349 @@ public class JoinGamePacket implements MinecraftPacket {
'}'; '}';
} }
@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);
}
}
private void decodeLegacy(ByteBuf buf, ProtocolVersion version) {
this.entityId = buf.readInt();
this.gamemode = buf.readByte();
this.isHardcore = (this.gamemode & 0x08) != 0;
this.gamemode &= ~0x08;
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();
}
}
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);
}
this.partialHashedSeed = buf.readLong();
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) {
this.maxPlayers = ProtocolUtils.readVarInt(buf);
} else {
this.maxPlayers = buf.readUnsignedByte();
}
this.viewDistance = ProtocolUtils.readVarInt(buf);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) {
this.simulationDistance = ProtocolUtils.readVarInt(buf);
}
this.reducedDebugInfo = buf.readBoolean();
this.showRespawnScreen = buf.readBoolean();
boolean isDebug = buf.readBoolean();
boolean isFlat = buf.readBoolean();
this.dimensionInfo = new DimensionInfo(dimensionIdentifier, levelName, isFlat, isDebug, version);
// optional death location
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);
}
}
@SuppressWarnings("checkstyle:VariableDeclarationUsageDistance")
private void decode1202Up(ByteBuf buf, ProtocolVersion version) {
this.entityId = buf.readInt();
this.isHardcore = buf.readBoolean();
this.levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf));
this.maxPlayers = ProtocolUtils.readVarInt(buf);
this.viewDistance = ProtocolUtils.readVarInt(buf);
this.simulationDistance = ProtocolUtils.readVarInt(buf);
this.reducedDebugInfo = buf.readBoolean();
this.showRespawnScreen = buf.readBoolean();
this.doLimitedCrafting = buf.readBoolean();
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();
this.gamemode = buf.readByte();
this.previousGamemode = buf.readByte();
boolean isDebug = buf.readBoolean();
boolean isFlat = buf.readBoolean();
this.dimensionInfo = new DimensionInfo(dimensionKey, levelName, isFlat, isDebug, version);
// optional death location
if (buf.readBoolean()) {
this.lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong());
}
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_5)) {
this.enforcesSecureChat = buf.readBoolean();
}
}
@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);
}
}
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);
}
}
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());
}
buf.writeLong(partialHashedSeed);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) {
ProtocolUtils.writeVarInt(buf, maxPlayers);
} else {
buf.writeByte(maxPlayers);
}
ProtocolUtils.writeVarInt(buf, viewDistance);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) {
ProtocolUtils.writeVarInt(buf, simulationDistance);
}
buf.writeBoolean(reducedDebugInfo);
buf.writeBoolean(showRespawnScreen);
buf.writeBoolean(dimensionInfo.isDebugType());
buf.writeBoolean(dimensionInfo.isFlat());
// 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());
} else {
buf.writeBoolean(false);
}
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) {
ProtocolUtils.writeVarInt(buf, portalCooldown);
}
}
private void encode1202Up(ByteBuf buf, ProtocolVersion version) {
buf.writeInt(entityId);
buf.writeBoolean(isHardcore);
ProtocolUtils.writeStringArray(buf, levelNames.toArray(String[]::new));
ProtocolUtils.writeVarInt(buf, maxPlayers);
ProtocolUtils.writeVarInt(buf, viewDistance);
ProtocolUtils.writeVarInt(buf, simulationDistance);
buf.writeBoolean(reducedDebugInfo);
buf.writeBoolean(showRespawnScreen);
buf.writeBoolean(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);
}
ProtocolUtils.writeVarInt(buf, portalCooldown);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
ProtocolUtils.writeVarInt(buf, seaLevel);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
buf.writeBoolean(this.enforcesSecureChat);
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<JoinGamePacket> {
@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);
}
}
@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);
}
}
private static JoinGamePacket decodeLegacy(ByteBuf buf, ProtocolVersion version) {
int entityId = buf.readInt();
short gamemode = buf.readByte();
boolean isHardcore = (gamemode & 0x08) != 0;
gamemode &= ~0x08;
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();
}
long partialHashedSeed = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) {
partialHashedSeed = buf.readLong();
}
int maxPlayers = buf.readUnsignedByte();
String levelType = ProtocolUtils.readString(buf, 16);
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();
}
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);
}
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();
ImmutableSet<String> levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf));
CompoundBinaryTag registry = ProtocolUtils.readCompoundTag(buf, version, JOINGAME_READER);
String dimensionIdentifier;
String levelName = null;
CompoundBinaryTag currentDimensionData = null;
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);
}
long partialHashedSeed = buf.readLong();
int maxPlayers;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) {
maxPlayers = ProtocolUtils.readVarInt(buf);
} else {
maxPlayers = buf.readUnsignedByte();
}
int viewDistance = ProtocolUtils.readVarInt(buf);
int simulationDistance = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) {
simulationDistance = ProtocolUtils.readVarInt(buf);
}
boolean reducedDebugInfo = buf.readBoolean();
boolean showRespawnScreen = buf.readBoolean();
boolean isDebug = buf.readBoolean();
boolean isFlat = buf.readBoolean();
DimensionInfo dimensionInfo = new DimensionInfo(dimensionIdentifier, levelName, isFlat, isDebug, version);
Pair<String, Long> 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);
}
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);
}
@SuppressWarnings("checkstyle:VariableDeclarationUsageDistance")
private static JoinGamePacket decode1202Up(ByteBuf buf, ProtocolVersion version) {
int entityId = buf.readInt();
boolean isHardcore = buf.readBoolean();
ImmutableSet<String> 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<String, Long> lastDeathPosition = null;
if (buf.readBoolean()) {
lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong());
}
int portalCooldown = ProtocolUtils.readVarInt(buf);
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);
}
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);
}
}
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);
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());
}
buf.writeLong(packet.partialHashedSeed);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) {
ProtocolUtils.writeVarInt(buf, packet.maxPlayers);
} else {
buf.writeByte(packet.maxPlayers);
}
ProtocolUtils.writeVarInt(buf, packet.viewDistance);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) {
ProtocolUtils.writeVarInt(buf, packet.simulationDistance);
}
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 static void encode1202Up(JoinGamePacket packet, ByteBuf buf, ProtocolVersion version) {
buf.writeInt(packet.entityId);
buf.writeBoolean(packet.isHardcore);
ProtocolUtils.writeStringArray(buf, packet.levelNames.toArray(String[]::new));
ProtocolUtils.writeVarInt(buf, packet.maxPlayers);
ProtocolUtils.writeVarInt(buf, packet.viewDistance);
ProtocolUtils.writeVarInt(buf, packet.simulationDistance);
buf.writeBoolean(packet.reducedDebugInfo);
buf.writeBoolean(packet.showRespawnScreen);
buf.writeBoolean(packet.doLimitedCrafting);
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);
buf.writeByte(packet.gamemode);
buf.writeByte(packet.previousGamemode);
buf.writeBoolean(packet.dimensionInfo.isDebugType());
buf.writeBoolean(packet.dimensionInfo.isFlat());
// 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);
}
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);
}
}
}
} }

View File

@@ -20,21 +20,16 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class KeepAlivePacket implements MinecraftPacket { public record KeepAlivePacket(long randomId) implements MinecraftPacket {
private long randomId;
public long getRandomId() { public long getRandomId() {
return randomId; return randomId;
} }
public void setRandomId(long randomId) {
this.randomId = randomId;
}
@Override @Override
public String toString() { public String toString() {
return "KeepAlive{" return "KeepAlive{"
@@ -42,30 +37,36 @@ public class KeepAlivePacket implements MinecraftPacket {
+ '}'; + '}';
} }
@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();
}
}
@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 @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<KeepAlivePacket> {
@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(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);
}
}
}
} }

View File

@@ -20,23 +20,33 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class LegacyHandshakePacket implements MinecraftPacket { public final class LegacyHandshakePacket implements MinecraftPacket {
@Override public static final LegacyHandshakePacket INSTANCE = new LegacyHandshakePacket();
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
throw new UnsupportedOperationException();
}
@Override private LegacyHandshakePacket() {
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
throw new UnsupportedOperationException();
} }
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<LegacyHandshakePacket> {
@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();
}
}
} }

View File

@@ -20,13 +20,14 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.legacyping.LegacyMinecraftPingVersion; import com.velocitypowered.proxy.protocol.packet.legacyping.LegacyMinecraftPingVersion;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class LegacyPingPacket implements MinecraftPacket { public final class LegacyPingPacket implements MinecraftPacket {
private final LegacyMinecraftPingVersion version; private final LegacyMinecraftPingVersion version;
private final @Nullable InetSocketAddress vhost; private final @Nullable InetSocketAddress vhost;
@@ -41,26 +42,38 @@ public class LegacyPingPacket implements MinecraftPacket {
this.vhost = vhost; this.vhost = vhost;
} }
public LegacyMinecraftPingVersion getVersion() { public LegacyMinecraftPingVersion version() {
return version; return version;
} }
public @Nullable InetSocketAddress getVhost() { public @Nullable InetSocketAddress vhost() {
return vhost; return vhost;
} }
@Override public LegacyMinecraftPingVersion getVersion() {
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { return version();
throw new UnsupportedOperationException();
} }
@Override public @Nullable InetSocketAddress getVhost() {
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { return vhost();
throw new UnsupportedOperationException();
} }
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<LegacyPingPacket> {
@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();
}
}
} }

View File

@@ -24,6 +24,7 @@ import com.velocitypowered.api.proxy.player.TabListEntry;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.ArrayList; import java.util.ArrayList;
@@ -33,22 +34,20 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.checkerframework.checker.nullness.qual.Nullable; 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 ADD_PLAYER = 0;
public static final int UPDATE_GAMEMODE = 1; public static final int UPDATE_GAMEMODE = 1;
public static final int UPDATE_LATENCY = 2; public static final int UPDATE_LATENCY = 2;
public static final int UPDATE_DISPLAY_NAME = 3; public static final int UPDATE_DISPLAY_NAME = 3;
public static final int REMOVE_PLAYER = 4; public static final int REMOVE_PLAYER = 4;
private int action;
private final List<Item> items = new ArrayList<>(); private final int action;
private final List<Item> items;
public LegacyPlayerListItemPacket(int action, List<Item> items) { public LegacyPlayerListItemPacket(int action, List<Item> items) {
this.action = action; this.action = action;
this.items.addAll(items); this.items = ImmutableList.copyOf(items);
}
public LegacyPlayerListItemPacket() {
} }
public int getAction() { public int getAction() {
@@ -59,131 +58,137 @@ public class LegacyPlayerListItemPacket implements MinecraftPacket {
return items; return items;
} }
@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));
}
}
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);
}
}
} 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);
}
}
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);
}
}
} 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());
}
buf.writeBoolean(action != REMOVE_PLAYER);
buf.writeShort(item.getLatency());
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
private void writeDisplayName(ByteBuf buf, @Nullable Component displayName, public static class Codec implements PacketCodec<LegacyPlayerListItemPacket> {
ProtocolVersion version) { @Override
buf.writeBoolean(displayName != null); public LegacyPlayerListItemPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
if (displayName != null) { ProtocolVersion version) {
ProtocolUtils.writeString(buf, ProtocolUtils.getJsonChatSerializer(version) if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
.serialize(displayName)); int action = ProtocolUtils.readVarInt(buf);
int length = ProtocolUtils.readVarInt(buf);
List<Item> 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);
}
}
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));
}
}
@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 " + packet.action);
}
}
} else {
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());
}
}
private static @Nullable Component readOptionalComponent(ByteBuf buf, ProtocolVersion version) {
if (buf.readBoolean()) {
return ProtocolUtils.getJsonChatSerializer(version)
.deserialize(ProtocolUtils.readString(buf));
}
return null;
}
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));
}
} }
} }

View File

@@ -20,29 +20,38 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class LoginAcknowledgedPacket implements MinecraftPacket { public final class LoginAcknowledgedPacket implements MinecraftPacket {
@Override public static final LoginAcknowledgedPacket INSTANCE = new LoginAcknowledgedPacket();
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
}
@Override private LoginAcknowledgedPacket() {
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
}
@Override
public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
return 0;
} }
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<LoginAcknowledgedPacket> {
@Override
public LoginAcknowledgedPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
return 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;
}
}
} }

View File

@@ -20,23 +20,19 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.DefaultByteBufHolder;
import io.netty.buffer.Unpooled; 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 final int id;
private @Nullable String channel; private final String channel;
public LoginPluginMessagePacket() { public LoginPluginMessagePacket(int id, String channel, ByteBuf data) {
super(null);
}
public LoginPluginMessagePacket(int id, @Nullable String channel, ByteBuf data) {
super(data); super(data);
this.id = id; this.id = id;
this.channel = channel; this.channel = channel;
@@ -47,9 +43,6 @@ public class LoginPluginMessagePacket extends DeferredByteBufHolder implements M
} }
public String getChannel() { public String getChannel() {
if (channel == null) {
throw new IllegalStateException("Channel is not specified!");
}
return channel; return channel;
} }
@@ -62,34 +55,40 @@ public class LoginPluginMessagePacket extends DeferredByteBufHolder implements M
+ '}'; + '}';
} }
@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);
}
}
@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!");
}
ProtocolUtils.writeString(buf, channel);
buf.writeBytes(content());
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
@Override public static class Codec implements PacketCodec<LoginPluginMessagePacket> {
public int encodeSizeHint(Direction direction, ProtocolVersion version) { @Override
return content().readableBytes(); 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);
}
@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(LoginPluginMessagePacket packet, Direction direction,
ProtocolVersion version) {
return ProtocolUtils.varIntBytes(packet.id)
+ ProtocolUtils.stringSizeHint(packet.channel)
+ packet.content().readableBytes();
}
} }
} }

View File

@@ -20,21 +20,18 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; 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.ByteBuf;
import io.netty.buffer.DefaultByteBufHolder;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 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 final int id;
private boolean success; private final boolean success;
public LoginPluginResponsePacket() {
super(Unpooled.EMPTY_BUFFER);
}
public LoginPluginResponsePacket(int id, boolean success, @MonotonicNonNull ByteBuf buf) { public LoginPluginResponsePacket(int id, boolean success, @MonotonicNonNull ByteBuf buf) {
super(buf); super(buf);
@@ -42,20 +39,20 @@ public class LoginPluginResponsePacket extends DeferredByteBufHolder implements
this.success = success; this.success = success;
} }
public int getId() { public int id() {
return id; return id;
} }
public void setId(int id) { public boolean success() {
this.id = id;
}
public boolean isSuccess() {
return success; return success;
} }
public void setSuccess(boolean success) { public int getId() {
this.success = success; return id();
}
public boolean isSuccess() {
return success();
} }
@Override @Override
@@ -67,31 +64,36 @@ public class LoginPluginResponsePacket extends DeferredByteBufHolder implements
+ '}'; + '}';
} }
@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 @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
@Override public static class Codec implements PacketCodec<LoginPluginResponsePacket> {
public int encodeSizeHint(Direction direction, ProtocolVersion version) { @Override
return content().readableBytes(); 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();
}
} }
} }

View File

@@ -20,30 +20,28 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class PingIdentifyPacket implements MinecraftPacket { public record PingIdentifyPacket(int id) implements MinecraftPacket {
private int id;
@Override
public String toString() {
return "Ping{" + "id=" + id + '}';
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
id = buf.readInt();
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
buf.writeInt(id);
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<PingIdentifyPacket> {
@Override
public PingIdentifyPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
return new PingIdentifyPacket(buf.readInt());
}
@Override
public void encode(PingIdentifyPacket packet, ByteBuf buf,
ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
buf.writeInt(packet.id);
}
}
} }

View File

@@ -22,38 +22,24 @@ import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.transfor
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; 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.ByteBuf;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import io.netty.buffer.DefaultByteBufHolder;
import org.checkerframework.checker.nullness.qual.Nullable;
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() { public PluginMessagePacket(String channel, ByteBuf backing) {
super(null);
}
public PluginMessagePacket(String channel,
@MonotonicNonNull ByteBuf backing) {
super(backing); super(backing);
this.channel = channel; this.channel = channel;
} }
public String getChannel() { public String getChannel() {
if (channel == null) {
throw new IllegalStateException("Channel is not specified.");
}
return channel; return channel;
} }
public void setChannel(String channel) {
this.channel = channel;
}
@Override @Override
public String toString() { public String toString() {
return "PluginMessage{" return "PluginMessage{"
@@ -62,44 +48,6 @@ public class PluginMessagePacket extends DeferredByteBufHolder implements Minecr
+ '}'; + '}';
} }
@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 @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
@@ -145,8 +93,41 @@ public class PluginMessagePacket extends DeferredByteBufHolder implements Minecr
return (PluginMessagePacket) super.touch(hint); return (PluginMessagePacket) super.touch(hint);
} }
@Override public static class Codec implements PacketCodec<PluginMessagePacket> {
public int encodeSizeHint(Direction direction, ProtocolVersion version) { @Override
return content().readableBytes(); 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
}
}
} }
} }

View File

@@ -21,54 +21,42 @@ import com.google.common.collect.Lists;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.UUID; import java.util.UUID;
public class RemovePlayerInfoPacket implements MinecraftPacket { public record RemovePlayerInfoPacket(Collection<UUID> profilesToRemove) implements MinecraftPacket {
private Collection<UUID> profilesToRemove;
public RemovePlayerInfoPacket() {
this.profilesToRemove = new ArrayList<>();
}
public RemovePlayerInfoPacket(Collection<UUID> profilesToRemove) {
this.profilesToRemove = profilesToRemove;
}
public Collection<UUID> getProfilesToRemove() { public Collection<UUID> getProfilesToRemove() {
return profilesToRemove; return profilesToRemove;
} }
public void setProfilesToRemove(Collection<UUID> profilesToRemove) {
this.profilesToRemove = profilesToRemove;
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
int length = ProtocolUtils.readVarInt(buf);
Collection<UUID> profilesToRemove = Lists.newArrayListWithCapacity(length);
for (int idx = 0; idx < length; idx++) {
profilesToRemove.add(ProtocolUtils.readUuid(buf));
}
this.profilesToRemove = profilesToRemove;
}
@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);
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<RemovePlayerInfoPacket> {
@Override
public RemovePlayerInfoPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
int length = ProtocolUtils.readVarInt(buf);
Collection<UUID> profilesToRemove = Lists.newArrayListWithCapacity(length);
for (int idx = 0; idx < length; idx++) {
profilesToRemove.add(ProtocolUtils.readUuid(buf));
}
return new RemovePlayerInfoPacket(profilesToRemove);
}
@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);
}
}
}
} }

View File

@@ -20,44 +20,42 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.UUID; 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 @Nullable UUID getId() {
public RemoveResourcePackPacket() {
}
public RemoveResourcePackPacket(UUID id) {
this.id = id;
}
public UUID getId() {
return id; return id;
} }
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
if (buf.readBoolean()) {
this.id = ProtocolUtils.readUuid(buf);
}
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
buf.writeBoolean(id != null);
if (id != null) {
ProtocolUtils.writeUuid(buf, id);
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
}
public static class Codec implements PacketCodec<RemoveResourcePackPacket> {
@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 void encode(RemoveResourcePackPacket packet, ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
buf.writeBoolean(packet.id != null);
if (packet.id != null) {
ProtocolUtils.writeUuid(buf, packet.id);
}
}
}
}

View File

@@ -23,6 +23,7 @@ import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo; import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
@@ -30,100 +31,47 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class ResourcePackRequestPacket implements MinecraftPacket { public final class ResourcePackRequestPacket implements MinecraftPacket {
private @MonotonicNonNull UUID id; // 1.20.3+ private final @Nullable UUID id; // 1.20.3+
private @MonotonicNonNull String url; private final String url;
private @MonotonicNonNull String hash; private final String hash;
private boolean isRequired; // 1.17+ private final boolean isRequired; // 1.17+
private @Nullable ComponentHolder prompt; // 1.17+ private final @Nullable ComponentHolder prompt; // 1.17+
private static final Pattern PLAUSIBLE_SHA1_HASH = Pattern.compile("^[a-z0-9]{40}$"); // 1.20.2+ private static final Pattern PLAUSIBLE_SHA1_HASH = Pattern.compile("^[a-z0-9]{40}$"); // 1.20.2+
public ResourcePackRequestPacket(@Nullable UUID id, String url, String hash,
boolean isRequired, @Nullable ComponentHolder prompt) {
this.id = id;
this.url = url;
this.hash = hash;
this.isRequired = isRequired;
this.prompt = prompt;
}
public @Nullable UUID getId() { public @Nullable UUID getId() {
return id; return id;
} }
public void setId(UUID id) { public String getUrl() {
this.id = id;
}
public @Nullable String getUrl() {
return url; return url;
} }
public void setUrl(String url) {
this.url = url;
}
public boolean isRequired() { public boolean isRequired() {
return isRequired; return isRequired;
} }
public @Nullable String getHash() { public String getHash() {
return hash; return hash;
} }
public void setHash(String hash) {
this.hash = hash;
}
public void setRequired(boolean required) {
isRequired = required;
}
public @Nullable ComponentHolder getPrompt() { public @Nullable ComponentHolder getPrompt() {
return prompt; 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() { public VelocityResourcePackInfo toServerPromptedPack() {
final ResourcePackInfo.Builder builder = final ResourcePackInfo.Builder builder =
new VelocityResourcePackInfo.BuilderImpl(Preconditions.checkNotNull(url)) new VelocityResourcePackInfo.BuilderImpl(Preconditions.checkNotNull(url))
@@ -153,4 +101,51 @@ public class ResourcePackRequestPacket implements MinecraftPacket {
", prompt=" + prompt + ", prompt=" + prompt +
'}'; '}';
} }
}
public static class Codec implements PacketCodec<ResourcePackRequestPacket> {
@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);
}
}
}
}
}

View File

@@ -21,63 +21,26 @@ import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent.Status
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import java.util.UUID; import java.util.UUID;
public class ResourcePackResponsePacket implements MinecraftPacket { public record ResourcePackResponsePacket(UUID id, String hash,
Status status) implements MinecraftPacket {
private UUID id; public UUID getId() {
private String hash = ""; return id;
private @MonotonicNonNull Status status;
public ResourcePackResponsePacket() {
}
public ResourcePackResponsePacket(UUID id, String hash, @MonotonicNonNull Status status) {
this.id = id;
this.hash = hash;
this.status = status;
}
public Status getStatus() {
if (status == null) {
throw new IllegalStateException("Packet not yet deserialized");
}
return status;
} }
public String getHash() { public String getHash() {
return hash; return hash;
} }
public UUID getId() { public Status getStatus() {
return id; return status;
}
@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);
}
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);
}
if (protocolVersion.noGreaterThan(ProtocolVersion.MINECRAFT_1_9_4)) {
ProtocolUtils.writeString(buf, hash);
}
ProtocolUtils.writeVarInt(buf, status.ordinal());
} }
@Override @Override
@@ -85,12 +48,32 @@ public class ResourcePackResponsePacket implements MinecraftPacket {
return handler.handle(this); return handler.handle(this);
} }
@Override public static class Codec implements PacketCodec<ResourcePackResponsePacket> {
public String toString() { @Override
return "ResourcePackResponsePacket{" + public ResourcePackResponsePacket decode(ByteBuf buf, Direction direction,
"id=" + id + ProtocolVersion protocolVersion) {
", hash='" + hash + '\'' + UUID id = null;
", status=" + status + 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);
}
@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());
}
} }
} }

View File

@@ -21,6 +21,7 @@ import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.registry.DimensionInfo; import com.velocitypowered.proxy.connection.registry.DimensionInfo;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.Pair;
@@ -28,23 +29,20 @@ import net.kyori.adventure.nbt.BinaryTagIO;
import net.kyori.adventure.nbt.CompoundBinaryTag; import net.kyori.adventure.nbt.CompoundBinaryTag;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class RespawnPacket implements MinecraftPacket { public final class RespawnPacket implements MinecraftPacket {
private int dimension; private final int dimension;
private long partialHashedSeed; private final long partialHashedSeed;
private short difficulty; private final short difficulty;
private short gamemode; private final short gamemode;
private String levelType = ""; private final String levelType;
private byte dataToKeep; // 1.16+ private final byte dataToKeep;
private DimensionInfo dimensionInfo; // 1.16-1.16.1 private final DimensionInfo dimensionInfo;
private short previousGamemode; // 1.16+ private final short previousGamemode;
private CompoundBinaryTag currentDimensionData; // 1.16.2+ private final CompoundBinaryTag currentDimensionData;
private @Nullable Pair<String, Long> lastDeathPosition; // 1.19+ private final @Nullable Pair<String, Long> lastDeathPosition;
private int portalCooldown; // 1.20+ private final int portalCooldown;
private int seaLevel; // 1.21.2+ private final int seaLevel;
public RespawnPacket() {
}
public RespawnPacket(int dimension, long partialHashedSeed, short difficulty, short gamemode, public RespawnPacket(int dimension, long partialHashedSeed, short difficulty, short gamemode,
String levelType, byte dataToKeep, DimensionInfo dimensionInfo, String levelType, byte dataToKeep, DimensionInfo dimensionInfo,
@@ -73,84 +71,60 @@ public class RespawnPacket implements MinecraftPacket {
joinGame.getPortalCooldown(), joinGame.getSeaLevel()); joinGame.getPortalCooldown(), joinGame.getSeaLevel());
} }
public int getDimension() { public static RespawnPacket fromJoinGame(JoinGamePacket joinGame, int newDimension) {
return dimension; 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) { public int getDimension() {
this.dimension = dimension; return dimension;
} }
public long getPartialHashedSeed() { public long getPartialHashedSeed() {
return partialHashedSeed; return partialHashedSeed;
} }
public void setPartialHashedSeed(long partialHashedSeed) {
this.partialHashedSeed = partialHashedSeed;
}
public short getDifficulty() { public short getDifficulty() {
return difficulty; return difficulty;
} }
public void setDifficulty(short difficulty) {
this.difficulty = difficulty;
}
public short getGamemode() { public short getGamemode() {
return gamemode; return gamemode;
} }
public void setGamemode(short gamemode) {
this.gamemode = gamemode;
}
public String getLevelType() { public String getLevelType() {
return levelType; return levelType;
} }
public void setLevelType(String levelType) {
this.levelType = levelType;
}
public byte getDataToKeep() { public byte getDataToKeep() {
return dataToKeep; return dataToKeep;
} }
public void setDataToKeep(byte dataToKeep) {
this.dataToKeep = dataToKeep;
}
public short getPreviousGamemode() { public short getPreviousGamemode() {
return previousGamemode; return previousGamemode;
} }
public void setPreviousGamemode(short previousGamemode) {
this.previousGamemode = previousGamemode;
}
public Pair<String, Long> getLastDeathPosition() { public Pair<String, Long> getLastDeathPosition() {
return lastDeathPosition; return lastDeathPosition;
} }
public void setLastDeathPosition(Pair<String, Long> lastDeathPosition) {
this.lastDeathPosition = lastDeathPosition;
}
public int getPortalCooldown() { public int getPortalCooldown() {
return portalCooldown; return portalCooldown;
} }
public void setPortalCooldown(int portalCooldown) {
this.portalCooldown = portalCooldown;
}
public int getSeaLevel() { public int getSeaLevel() {
return seaLevel; return seaLevel;
} }
public void setSeaLevel(int seaLevel) { public DimensionInfo getDimensionInfo() {
this.seaLevel = seaLevel; return dimensionInfo;
}
public CompoundBinaryTag getCurrentDimensionData() {
return currentDimensionData;
} }
@Override @Override
@@ -171,124 +145,157 @@ public class RespawnPacket implements MinecraftPacket {
+ '}'; + '}';
} }
@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 {
dimensionKey = ProtocolUtils.readString(buf);
}
levelName = ProtocolUtils.readString(buf);
}
} 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();
}
} 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());
} else {
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
ProtocolUtils.writeVarInt(buf, dimension);
} else {
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier());
}
ProtocolUtils.writeString(buf, dimensionInfo.getLevelName());
}
} 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);
}
} 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());
} else {
buf.writeBoolean(false);
}
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) {
ProtocolUtils.writeVarInt(buf, portalCooldown);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
ProtocolUtils.writeVarInt(buf, seaLevel);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
buf.writeByte(dataToKeep);
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<RespawnPacket> {
@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);
}
} else {
dimension = buf.readInt();
}
short difficulty = 0;
if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) {
difficulty = buf.readUnsignedByte();
}
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 {
levelType = ProtocolUtils.readString(buf, 16);
}
Pair<String, Long> 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 {
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());
}
} else {
buf.writeInt(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.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 {
ProtocolUtils.writeString(buf, packet.levelType);
}
// 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);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
ProtocolUtils.writeVarInt(buf, packet.seaLevel);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
buf.writeByte(packet.dataToKeep);
}
}
}
} }

View File

@@ -21,6 +21,7 @@ import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.util.Favicon; import com.velocitypowered.api.util.Favicon;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
@@ -29,14 +30,11 @@ import org.jetbrains.annotations.Nullable;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Base64; import java.util.Base64;
public class ServerDataPacket implements MinecraftPacket { public final class ServerDataPacket implements MinecraftPacket {
private @Nullable ComponentHolder description; private final @Nullable ComponentHolder description;
private @Nullable Favicon favicon; private final @Nullable Favicon favicon;
private boolean secureChatEnforced; // Added in 1.19.1 - Removed in 1.20.5 private final boolean secureChatEnforced; // Added in 1.19.1 - Removed in 1.20.5
public ServerDataPacket() {
}
public ServerDataPacket(@Nullable ComponentHolder description, @Nullable Favicon favicon, public ServerDataPacket(@Nullable ComponentHolder description, @Nullable Favicon favicon,
boolean secureChatEnforced) { boolean secureChatEnforced) {
@@ -45,63 +43,6 @@ public class ServerDataPacket implements MinecraftPacket {
this.secureChatEnforced = secureChatEnforced; 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 @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
@@ -119,12 +60,68 @@ public class ServerDataPacket implements MinecraftPacket {
return secureChatEnforced; return secureChatEnforced;
} }
public void setSecureChatEnforced(boolean secureChatEnforced) { public static class Codec implements PacketCodec<ServerDataPacket> {
this.secureChatEnforced = secureChatEnforced; @Override
} public ServerDataPacket decode(ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
ComponentHolder description = null;
Favicon favicon = null;
boolean secureChatEnforced = false;
@Override if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4) || buf.readBoolean()) {
public int encodeSizeHint(Direction direction, ProtocolVersion version) { description = ComponentHolder.read(buf, protocolVersion);
return 8 * 1024; }
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);
}
}
} }
} }

View File

@@ -17,165 +17,172 @@
package com.velocitypowered.proxy.protocol.packet; package com.velocitypowered.proxy.protocol.packet;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey; import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.util.except.QuietDecoderException; import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.UUID; import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class ServerLoginPacket implements MinecraftPacket { public final class ServerLoginPacket implements MinecraftPacket {
private static final QuietDecoderException EMPTY_USERNAME = new QuietDecoderException( private static final QuietDecoderException EMPTY_USERNAME = new QuietDecoderException(
"Empty username!"); "Empty username!");
private @Nullable String username; private final String username;
private @Nullable IdentifiedKey playerKey; // Introduced in 1.19.3 private final @Nullable IdentifiedKey playerKey; // Introduced in 1.19.3
private @Nullable UUID holderUuid; // Used for key revision 2 private final @Nullable UUID holderUuid; // Used for key revision 2
public ServerLoginPacket() {
}
public ServerLoginPacket(String username, @Nullable IdentifiedKey playerKey) { public ServerLoginPacket(String username, @Nullable IdentifiedKey playerKey) {
this.username = Preconditions.checkNotNull(username, "username"); this(username, playerKey, null);
this.playerKey = playerKey;
} }
public ServerLoginPacket(String username, @Nullable UUID holderUuid) { public ServerLoginPacket(String username, @Nullable UUID holderUuid) {
this.username = Preconditions.checkNotNull(username, "username"); this(username, null, holderUuid);
this.holderUuid = holderUuid;
this.playerKey = null;
} }
public String getUsername() { private ServerLoginPacket(String username, @Nullable IdentifiedKey playerKey,
if (username == null) { @Nullable UUID holderUuid) {
throw new IllegalStateException("No username found!"); this.username = username;
} this.playerKey = playerKey;
this.holderUuid = holderUuid;
}
public String username() {
return username; return username;
} }
public @Nullable IdentifiedKey getPlayerKey() { public String getUsername() {
return username();
}
public @Nullable IdentifiedKey playerKey() {
return this.playerKey; return this.playerKey;
} }
public void setPlayerKey(IdentifiedKey playerKey) { public @Nullable UUID holderUuid() {
this.playerKey = playerKey;
}
public @Nullable UUID getHolderUuid() {
return holderUuid; return holderUuid;
} }
@Override @Override
public String toString() { public String toString() {
return "ServerLogin{" return "ServerLogin{"
+ "username='" + username + '\'' + "username='" + username + '\''
+ "playerKey='" + playerKey + '\'' + "playerKey='" + playerKey + '\''
+ "holderUUID='" + holderUuid + '\'' + "holderUUID='" + holderUuid + '\''
+ '}'; + '}';
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion version) {
username = ProtocolUtils.readString(buf, 16);
if (username.isEmpty()) {
throw EMPTY_USERNAME;
}
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()) {
holderUuid = ProtocolUtils.readUuid(buf);
}
}
} else {
playerKey = null;
}
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (username == null) {
throw new IllegalStateException("No username found!");
}
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);
}
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
ProtocolUtils.writeUuid(buf, this.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);
}
}
}
}
@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;
}
}
return base;
} }
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<ServerLoginPacket> {
@Override
public ServerLoginPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
String 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 {
playerKey = null;
}
}
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;
}
return new ServerLoginPacket(username, playerKey, holderUuid);
}
@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, packet.holderUuid);
return;
}
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, 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;
}
}
} }

View File

@@ -22,52 +22,40 @@ import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.UuidUtils; import com.velocitypowered.api.util.UuidUtils;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.util.VelocityProperties; import com.velocitypowered.proxy.util.VelocityProperties;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.List; import java.util.List;
import java.util.UUID; 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 final UUID uuid;
private @Nullable String username; private final String username;
private @Nullable List<GameProfile.Property> properties; private final List<GameProfile.Property> properties;
private static final boolean strictErrorHandling = VelocityProperties private static final boolean strictErrorHandling = VelocityProperties
.readBoolean("velocity.strictErrorHandling", true); .readBoolean("velocity.strictErrorHandling", true);
public ServerLoginSuccessPacket(UUID uuid, String username, List<GameProfile.Property> properties) {
this.uuid = uuid;
this.username = username;
this.properties = properties;
}
public UUID getUuid() { public UUID getUuid() {
if (uuid == null) {
throw new IllegalStateException("No UUID specified!");
}
return uuid; return uuid;
} }
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public String getUsername() { public String getUsername() {
if (username == null) {
throw new IllegalStateException("No username specified!");
}
return username; return username;
} }
public void setUsername(String username) {
this.username = username;
}
public List<GameProfile.Property> getProperties() { public List<GameProfile.Property> getProperties() {
return properties; return properties;
} }
public void setProperties(List<GameProfile.Property> properties) {
this.properties = properties;
}
@Override @Override
public String toString() { public String toString() {
return "ServerLoginSuccess{" return "ServerLoginSuccess{"
@@ -77,67 +65,70 @@ public class ServerLoginSuccessPacket implements MinecraftPacket {
+ '}'; + '}';
} }
@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();
}
}
@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!");
}
ProtocolUtils.writeString(buf, username);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) {
if (properties == null) {
ProtocolUtils.writeVarInt(buf, 0);
} else {
ProtocolUtils.writeProperties(buf, properties);
}
}
if (version == ProtocolVersion.MINECRAFT_1_20_5 || version == ProtocolVersion.MINECRAFT_1_21) {
buf.writeBoolean(strictErrorHandling);
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
@Override public static class Codec implements PacketCodec<ServerLoginSuccessPacket> {
public int encodeSizeHint(Direction direction, ProtocolVersion version) { @Override
// We could compute an exact size, but 4KiB ought to be enough to encode all reasonable public ServerLoginSuccessPacket decode(ByteBuf buf, Direction direction,
// sizes of this packet. ProtocolVersion version) {
return 4 * 1024; 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<GameProfile.Property> 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);
}
@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.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);
}
}
@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;
}
} }
} }

View File

@@ -20,16 +20,15 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class ServerboundCookieResponsePacket implements MinecraftPacket { public record ServerboundCookieResponsePacket(Key key,
byte @Nullable [] payload) implements MinecraftPacket {
private Key key;
private byte @Nullable [] payload;
public Key getKey() { public Key getKey() {
return key; return key;
@@ -39,34 +38,32 @@ public class ServerboundCookieResponsePacket implements MinecraftPacket {
return payload; return payload;
} }
public ServerboundCookieResponsePacket() {
}
public ServerboundCookieResponsePacket(final Key key, final byte @Nullable [] payload) {
this.key = key;
this.payload = payload;
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
this.key = ProtocolUtils.readKey(buf);
if (buf.readBoolean()) {
this.payload = ProtocolUtils.readByteArray(buf, 5120);
}
}
@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);
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<ServerboundCookieResponsePacket> {
@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 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);
}
}
}
} }

View File

@@ -20,25 +20,17 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
import io.netty.buffer.ByteBuf; 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() { public ServerboundCustomClickActionPacket(ByteBuf backing) {
super(null); super(backing);
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
replace(buf.readRetainedSlice(buf.readableBytes()));
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
buf.writeBytes(content());
} }
@Override @Override
@@ -47,7 +39,61 @@ public class ServerboundCustomClickActionPacket extends DeferredByteBufHolder im
} }
@Override @Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) { public ServerboundCustomClickActionPacket copy() {
return content().readableBytes(); return (ServerboundCustomClickActionPacket) super.copy();
}
@Override
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 ServerboundCustomClickActionPacket touch(Object hint) {
return (ServerboundCustomClickActionPacket) super.touch(hint);
}
public static class Codec implements PacketCodec<ServerboundCustomClickActionPacket> {
@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();
}
} }
} }

View File

@@ -20,28 +20,16 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class SetCompressionPacket implements MinecraftPacket { public record SetCompressionPacket(int threshold) implements MinecraftPacket {
private int threshold;
public SetCompressionPacket() {
}
public SetCompressionPacket(int threshold) {
this.threshold = threshold;
}
public int getThreshold() { public int getThreshold() {
return threshold; return threshold;
} }
public void setThreshold(int threshold) {
this.threshold = threshold;
}
@Override @Override
public String toString() { public String toString() {
return "SetCompression{" return "SetCompression{"
@@ -49,18 +37,22 @@ public class SetCompressionPacket implements MinecraftPacket {
+ '}'; + '}';
} }
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
this.threshold = ProtocolUtils.readVarInt(buf);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
ProtocolUtils.writeVarInt(buf, threshold);
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<SetCompressionPacket> {
@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);
}
}
} }

View File

@@ -20,36 +20,41 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class StatusPingPacket implements MinecraftPacket { public record StatusPingPacket(long randomId) 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);
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
@Override public static class Codec implements PacketCodec<StatusPingPacket> {
public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { @Override
return 8; public StatusPingPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
} ProtocolVersion protocolVersion) {
return new StatusPingPacket(buf.readLong());
}
@Override @Override
public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { public void encode(StatusPingPacket packet, ByteBuf buf, ProtocolUtils.Direction direction,
return 8; 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;
}
} }
} }

View File

@@ -20,11 +20,11 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; 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 com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class StatusRequestPacket implements MinecraftPacket { public final class StatusRequestPacket implements MinecraftPacket {
public static final StatusRequestPacket INSTANCE = new StatusRequestPacket(); public static final StatusRequestPacket INSTANCE = new StatusRequestPacket();
@@ -32,16 +32,6 @@ public class StatusRequestPacket implements MinecraftPacket {
} }
@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 @Override
public String toString() { public String toString() {
return "StatusRequest"; return "StatusRequest";
@@ -52,8 +42,22 @@ public class StatusRequestPacket implements MinecraftPacket {
return handler.handle(this); return handler.handle(this);
} }
@Override public static class Codec implements PacketCodec<StatusRequestPacket> {
public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { @Override
return 0; public StatusRequestPacket decode(ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
return 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;
}
} }
} }

View File

@@ -20,29 +20,30 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class StatusResponsePacket implements MinecraftPacket { public final class StatusResponsePacket implements MinecraftPacket {
private @Nullable CharSequence status; private final @Nullable CharSequence status;
public StatusResponsePacket() {
}
public StatusResponsePacket(CharSequence status) { public StatusResponsePacket(CharSequence status) {
this.status = status; this.status = status;
} }
public String getStatus() { public String status() {
if (status == null) { if (status == null) {
throw new IllegalStateException("Status is not specified"); throw new IllegalStateException("Status is not specified");
} }
return status.toString(); return status.toString();
} }
public String getStatus() {
return status();
}
@Override @Override
public String toString() { public String toString() {
return "StatusResponse{" return "StatusResponse{"
@@ -50,26 +51,31 @@ public class StatusResponsePacket implements MinecraftPacket {
+ '}'; + '}';
} }
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
status = ProtocolUtils.readString(buf, Short.MAX_VALUE);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (status == null) {
throw new IllegalStateException("Status is not specified");
}
ProtocolUtils.writeString(buf, status);
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
@Override public static class Codec implements PacketCodec<StatusResponsePacket> {
public int encodeSizeHint(Direction direction, ProtocolVersion version) { @Override
return ProtocolUtils.stringSizeHint(this.status); public StatusResponsePacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
CharSequence status = ProtocolUtils.readString(buf, Short.MAX_VALUE);
return new StatusResponsePacket(status);
}
@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(StatusResponsePacket packet, ProtocolUtils.Direction direction, ProtocolVersion version) {
return ProtocolUtils.stringSizeHint(packet.status);
}
} }
} }

View File

@@ -21,122 +21,125 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; 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;
import com.google.common.base.MoreObjects;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable; 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 static final int VANILLA_MAX_TAB_COMPLETE_LEN = 2048;
private @Nullable String command; private final @Nullable String command;
private int transactionId; private final int transactionId;
private boolean assumeCommand; private final boolean assumeCommand;
private boolean hasPosition; private final boolean hasPosition;
private long position; 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) { if (command == null) {
throw new IllegalStateException("Command is not specified"); throw new IllegalStateException("Command is not specified");
} }
return command; return command;
} }
public void setCommand(String command) { public String getCommand() {
this.command = command; return command();
} }
public boolean isAssumeCommand() { public boolean assumeCommand() {
return assumeCommand; return assumeCommand;
} }
public void setAssumeCommand(boolean assumeCommand) { public boolean isAssumeCommand() {
this.assumeCommand = assumeCommand; return assumeCommand();
} }
public boolean hasPosition() { public boolean hasPosition() {
return hasPosition; return hasPosition;
} }
public void setHasPosition(boolean hasPosition) { public long position() {
this.hasPosition = hasPosition;
}
public long getPosition() {
return position; return position;
} }
public void setPosition(long position) { public int transactionId() {
this.position = position;
}
public int getTransactionId() {
return 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();
}
@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();
}
}
}
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (command == null) {
throw new IllegalStateException("Command is not specified");
}
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);
}
if (version.noLessThan(MINECRAFT_1_8)) {
buf.writeBoolean(hasPosition);
if (hasPosition) {
buf.writeLong(position);
}
}
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<TabCompleteRequestPacket> {
@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();
}
}
}
return new TabCompleteRequestPacket(command, transactionId, assumeCommand, hasPosition,
position);
}
@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_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);
}
}
}
}
}
} }

View File

@@ -23,6 +23,7 @@ import com.google.common.base.MoreObjects;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@@ -30,39 +31,54 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class TabCompleteResponsePacket implements MinecraftPacket { public final class TabCompleteResponsePacket implements MinecraftPacket {
private int transactionId; private final int transactionId;
private int start; private final int start;
private int length; private final int length;
private final List<Offer> offers = new ArrayList<>(); private final List<Offer> offers;
public int getTransactionId() { public TabCompleteResponsePacket(int transactionId, int start, int length, List<Offer> offers) {
this.transactionId = transactionId;
this.start = start;
this.length = length;
this.offers = offers;
}
public int transactionId() {
return transactionId; return transactionId;
} }
public void setTransactionId(int transactionId) { public int start() {
this.transactionId = transactionId;
}
public int getStart() {
return start; return start;
} }
public void setStart(int start) { public int length() {
this.start = start;
}
public int getLength() {
return length; return length;
} }
public void setLength(int length) { public List<Offer> offers() {
this.length = length; return offers;
}
public int getTransactionId() {
return transactionId();
}
public int getStart() {
return start();
}
public int getLength() {
return length();
} }
public List<Offer> getOffers() { public List<Offer> getOffers() {
return offers; return offers();
}
public TabCompleteResponsePacket withOffers(List<Offer> offers) {
return new TabCompleteResponsePacket(transactionId, start, length, offers);
} }
@Override @Override
@@ -75,53 +91,61 @@ public class TabCompleteResponsePacket implements MinecraftPacket {
+ '}'; + '}';
} }
@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));
}
}
}
@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);
}
}
} else {
ProtocolUtils.writeVarInt(buf, offers.size());
for (Offer offer : offers) {
ProtocolUtils.writeString(buf, offer.text);
}
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<TabCompleteResponsePacket> {
@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<Offer> 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));
}
return new TabCompleteResponsePacket(transactionId, start, length, offers);
} else {
int offersAvailable = ProtocolUtils.readVarInt(buf);
List<Offer> 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 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<Offer> { public static class Offer implements Comparable<Offer> {
private final String text; private final String text;

View File

@@ -20,22 +20,13 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class TransferPacket implements MinecraftPacket { public record TransferPacket(String host, int port) implements MinecraftPacket {
private String host;
private int port;
public TransferPacket() {
}
public TransferPacket(final String host, final int port) {
this.host = host;
this.port = port;
}
@Nullable @Nullable
public InetSocketAddress address() { public InetSocketAddress address() {
@@ -45,20 +36,31 @@ public class TransferPacket implements MinecraftPacket {
return new InetSocketAddress(host, port); return new InetSocketAddress(host, port);
} }
@Override public String getHost() {
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { return host;
this.host = ProtocolUtils.readString(buf);
this.port = ProtocolUtils.readVarInt(buf);
} }
@Override public int getPort() {
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { return port;
ProtocolUtils.writeString(buf, host);
ProtocolUtils.writeVarInt(buf, port);
} }
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<TransferPacket> {
@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);
}
}
} }

View File

@@ -21,6 +21,7 @@ import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import com.velocitypowered.proxy.protocol.packet.chat.RemoteChatSession; import com.velocitypowered.proxy.protocol.packet.chat.RemoteChatSession;
@@ -28,29 +29,18 @@ import io.netty.buffer.ByteBuf;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet; import java.util.BitSet;
import java.util.Collection;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.jetbrains.annotations.Nullable; 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 static final Action[] ALL_ACTIONS = Action.class.getEnumConstants();
private final EnumSet<Action> actions; private final EnumSet<Action> actions;
private final List<Entry> entries; private final List<Entry> 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<Action> actions, List<Entry> entries) { public UpsertPlayerInfoPacket(EnumSet<Action> actions, List<Entry> entries) {
this.actions = actions; this.actions = actions;
this.entries = entries; this.entries = entries;
@@ -68,71 +58,60 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket {
return this.actions.contains(action); return this.actions.contains(action);
} }
public void addAction(Action action) {
this.actions.add(action);
}
public void addAllActions(Collection<? extends Action> actions) {
this.actions.addAll(actions);
}
public void addEntry(Entry entry) {
this.entries.add(entry);
}
public void addAllEntries(Collection<? extends Entry> entries) {
this.entries.addAll(entries);
}
@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]);
}
}
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);
}
addEntry(entry);
}
}
@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]));
}
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);
for (Action action : this.actions) {
action.write.write(protocolVersion, buf, entry);
}
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<UpsertPlayerInfoPacket> {
@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<Action> 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);
List<Entry> 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);
}
return new UpsertPlayerInfoPacket(actions, entries);
}
@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)));
ProtocolUtils.writeVarInt(buf, packet.entries.size());
for (Entry entry : packet.entries) {
ProtocolUtils.writeUuid(buf, entry.profileId);
for (Action action : packet.actions) {
action.write.write(protocolVersion, buf, entry);
}
}
}
}
public enum Action { public enum Action {
ADD_PLAYER((ignored, buf, info) -> { // read ADD_PLAYER((ignored, buf, info) -> { // read
info.profile = new GameProfile( info.profile = new GameProfile(
@@ -315,4 +294,4 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket {
'}'; '}';
} }
} }
} }

View File

@@ -20,42 +20,35 @@ package com.velocitypowered.proxy.protocol.packet.chat;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class ChatAcknowledgementPacket implements MinecraftPacket { public record ChatAcknowledgementPacket(int offset) implements MinecraftPacket {
int offset;
public ChatAcknowledgementPacket(int offset) { @Override
this.offset = offset; public boolean handle(MinecraftSessionHandler handler) {
} return handler.handle(this);
}
public ChatAcknowledgementPacket() { @Override
public String toString() {
return "ChatAcknowledgement{" +
"offset=" + offset +
'}';
}
public static class Codec implements PacketCodec<ChatAcknowledgementPacket> {
@Override
public ChatAcknowledgementPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
return new ChatAcknowledgementPacket(ProtocolUtils.readVarInt(buf));
} }
@Override @Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { public void encode(ChatAcknowledgementPacket packet, ByteBuf buf,
offset = ProtocolUtils.readVarInt(buf); ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
} ProtocolUtils.writeVarInt(buf, packet.offset);
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, offset);
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
@Override
public String toString() {
return "ChatAcknowledgement{" +
"offset=" + offset +
'}';
}
public int offset() {
return offset;
} }
}
} }

View File

@@ -20,15 +20,17 @@ package com.velocitypowered.proxy.protocol.packet.chat;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class PlayerChatCompletionPacket implements MinecraftPacket { public final class PlayerChatCompletionPacket implements MinecraftPacket {
private String[] completions; private final String[] completions;
private Action action; private final Action action;
public PlayerChatCompletionPacket() { public PlayerChatCompletionPacket() {
this(new String[0], Action.ADD);
} }
public PlayerChatCompletionPacket(String[] completions, Action action) { public PlayerChatCompletionPacket(String[] completions, Action action) {
@@ -36,36 +38,14 @@ public class PlayerChatCompletionPacket implements MinecraftPacket {
this.action = action; this.action = action;
} }
public String[] getCompletions() { public String[] completions() {
return completions; return completions;
} }
public Action getAction() { public Action action() {
return 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 @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
@@ -76,4 +56,21 @@ public class PlayerChatCompletionPacket implements MinecraftPacket {
REMOVE, REMOVE,
SET SET
} }
public static class Codec implements PacketCodec<PlayerChatCompletionPacket> {
@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);
}
}
} }

View File

@@ -20,12 +20,17 @@ package com.velocitypowered.proxy.protocol.packet.chat;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; 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() { public SystemChatPacket() {
this(null, ChatType.SYSTEM);
} }
public SystemChatPacket(ComponentHolder component, ChatType type) { public SystemChatPacket(ComponentHolder component, ChatType type) {
@@ -33,48 +38,59 @@ public class SystemChatPacket implements MinecraftPacket {
this.type = type; this.type = type;
} }
private ComponentHolder component; public ChatType type() {
private ChatType type;
public ChatType getType() {
return type; return type;
} }
public ComponentHolder getComponent() { public ComponentHolder component() {
return component; return component;
} }
@Override public ComponentHolder getComponent() {
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { return component();
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)];
}
} }
@Override public ChatType getType() {
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { return type();
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());
}
} }
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<SystemChatPacket> {
@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());
}
}
}
} }

View File

@@ -17,7 +17,6 @@
package com.velocitypowered.proxy.protocol.packet.chat.keyed; package com.velocitypowered.proxy.protocol.packet.chat.keyed;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.packet.chat.ChatType; import com.velocitypowered.proxy.protocol.packet.chat.ChatType;
@@ -42,12 +41,13 @@ public class KeyedChatBuilder extends ChatBuilderV2 {
@Override @Override
public MinecraftPacket toServer() { public MinecraftPacket toServer() {
if (message.startsWith("/")) { 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 { } else {
// This will produce an error on the server, but needs to be here. // This will produce an error on the server, but needs to be here.
KeyedPlayerChatPacket v1Chat = new KeyedPlayerChatPacket(message); return new KeyedPlayerChatPacket(message, false, false, timestamp, null, null,
v1Chat.setExpiry(this.timestamp); new com.velocitypowered.proxy.crypto.SignaturePair[0], null);
return v1Chat;
} }
} }
} }

View File

@@ -23,41 +23,43 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.crypto.EncryptionUtils; import com.velocitypowered.proxy.crypto.EncryptionUtils;
import com.velocitypowered.proxy.crypto.SignaturePair; import com.velocitypowered.proxy.crypto.SignaturePair;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.util.except.QuietDecoderException; import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.time.Instant; import java.time.Instant;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class KeyedPlayerChatPacket implements MinecraftPacket { public final 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 static final int MAXIMUM_PREVIOUS_MESSAGE_COUNT = 5; public static final int MAXIMUM_PREVIOUS_MESSAGE_COUNT = 5;
public static final QuietDecoderException INVALID_PREVIOUS_MESSAGES = public static final QuietDecoderException INVALID_PREVIOUS_MESSAGES =
new QuietDecoderException("Invalid previous messages"); new QuietDecoderException("Invalid previous messages");
public KeyedPlayerChatPacket() { 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) { 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.message = message;
this.unsigned = true; this.signedPreview = signedPreview;
} this.unsigned = unsigned;
public void setExpiry(@Nullable Instant expiry) {
this.expiry = expiry; this.expiry = expiry;
this.signature = signature;
this.salt = salt;
this.previousMessages = previousMessages;
this.lastMessage = lastMessage;
} }
public Instant getExpiry() { public @Nullable Instant getExpiry() {
return expiry; return expiry;
} }
@@ -73,82 +75,96 @@ public class KeyedPlayerChatPacket implements MinecraftPacket {
return signedPreview; return signedPreview;
} }
@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;
}
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));
}
}
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
ProtocolUtils.writeString(buf, message);
buf.writeLong(unsigned ? Instant.now().toEpochMilli() : expiry.toEpochMilli());
buf.writeLong(unsigned ? 0L : Longs.fromByteArray(salt));
ProtocolUtils.writeByteArray(buf, unsigned ? EncryptionUtils.EMPTY : signature);
buf.writeBoolean(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 (lastMessage != null) {
buf.writeBoolean(true);
ProtocolUtils.writeUuid(buf, lastMessage.getSigner());
ProtocolUtils.writeByteArray(buf, lastMessage.getSignature());
} else {
buf.writeBoolean(false);
}
}
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<KeyedPlayerChatPacket> {
@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;
}
boolean signedPreview = buf.readBoolean();
if (signedPreview && unsigned) {
throw EncryptionUtils.PREVIEW_SIGNATURE_MISSING;
}
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(KeyedPlayerChatPacket packet, ByteBuf buf,
ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeString(buf, packet.message);
buf.writeLong(packet.unsigned ? Instant.now().toEpochMilli() : packet.expiry.toEpochMilli());
buf.writeLong(packet.unsigned ? 0L : Longs.fromByteArray(packet.salt));
ProtocolUtils.writeByteArray(buf, packet.unsigned ? EncryptionUtils.EMPTY : packet.signature);
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);
}
}
}
}
} }

View File

@@ -26,30 +26,43 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.crypto.EncryptionUtils; import com.velocitypowered.proxy.crypto.EncryptionUtils;
import com.velocitypowered.proxy.crypto.SignaturePair; import com.velocitypowered.proxy.crypto.SignaturePair;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.util.except.QuietDecoderException; import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.time.Instant; import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable; 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_NUM_ARGUMENTS = 8;
private static final int MAX_LENGTH_ARGUMENTS = 16; private static final int MAX_LENGTH_ARGUMENTS = 16;
private static final QuietDecoderException LIMITS_VIOLATION = private static final QuietDecoderException LIMITS_VIOLATION =
new QuietDecoderException("Command arguments incorrect size"); new QuietDecoderException("Command arguments incorrect size");
private boolean unsigned = false; private final boolean unsigned;
private String command; private final String command;
private Instant timestamp; private final Instant timestamp;
private long salt; private final long salt;
private boolean signedPreview; // purely for pass through for 1.19 -> 1.19.2 - this will never be implemented private final boolean signedPreview;
private SignaturePair[] previousMessages = new SignaturePair[0]; private final SignaturePair[] previousMessages;
private @Nullable SignaturePair lastMessage; private final @Nullable SignaturePair lastMessage;
private Map<String, byte[]> arguments = ImmutableMap.of(); private final Map<String, byte[]> arguments;
public KeyedPlayerCommandPacket(boolean unsigned, String command, Instant timestamp, long salt,
boolean signedPreview, SignaturePair[] previousMessages, @Nullable SignaturePair lastMessage,
Map<String, byte[]> 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() { public Instant getTimestamp() {
return timestamp; return timestamp;
@@ -63,116 +76,6 @@ public class KeyedPlayerCommandPacket implements MinecraftPacket {
return command; 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<String> arguments, Instant timestamp) {
this.unsigned = true;
ImmutableMap.Builder<String, byte[]> 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 void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
command = ProtocolUtils.readString(buf, 256);
timestamp = Instant.ofEpochMilli(buf.readLong());
salt = buf.readLong();
int mapSize = ProtocolUtils.readVarInt(buf);
if (mapSize > MAX_NUM_ARGUMENTS) {
throw LIMITS_VIOLATION;
}
// Mapped as Argument : signature
ImmutableMap.Builder<String, byte[]> 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();
this.signedPreview = buf.readBoolean();
if (unsigned && signedPreview) {
throw EncryptionUtils.PREVIEW_SIGNATURE_MISSING;
}
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));
}
}
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());
buf.writeLong(unsigned ? 0L : salt);
int size = arguments.size();
if (size > MAX_NUM_ARGUMENTS) {
throw LIMITS_VIOLATION;
}
ProtocolUtils.writeVarInt(buf, size);
for (Map.Entry<String, byte[]> 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());
}
buf.writeBoolean(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 (lastMessage != null) {
buf.writeBoolean(true);
ProtocolUtils.writeUuid(buf, lastMessage.getSigner());
ProtocolUtils.writeByteArray(buf, lastMessage.getSignature());
} else {
buf.writeBoolean(false);
}
}
}
@Override @Override
public String toString() { public String toString() {
return "PlayerCommand{" return "PlayerCommand{"
@@ -190,4 +93,100 @@ public class KeyedPlayerCommandPacket implements MinecraftPacket {
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<KeyedPlayerCommandPacket> {
@Override
public KeyedPlayerCommandPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
String command = ProtocolUtils.readString(buf, 256);
Instant timestamp = Instant.ofEpochMilli(buf.readLong());
long salt = buf.readLong();
int mapSize = ProtocolUtils.readVarInt(buf);
if (mapSize > MAX_NUM_ARGUMENTS) {
throw LIMITS_VIOLATION;
}
boolean unsigned = false;
ImmutableMap.Builder<String, byte[]> 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));
}
Map<String, byte[]> arguments = entries.build();
boolean signedPreview = buf.readBoolean();
if (unsigned && signedPreview) {
throw EncryptionUtils.PREVIEW_SIGNATURE_MISSING;
}
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));
}
}
if (salt == 0L && previousMessages.length == 0) {
unsigned = true;
}
return new KeyedPlayerCommandPacket(unsigned, command, timestamp, salt, signedPreview,
previousMessages, lastMessage, arguments);
}
@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);
int size = packet.arguments.size();
if (size > MAX_NUM_ARGUMENTS) {
throw LIMITS_VIOLATION;
}
ProtocolUtils.writeVarInt(buf, size);
for (Map.Entry<String, byte[]> 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());
}
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);
}
}
}
}
} }

View File

@@ -44,8 +44,6 @@ public class LegacyChatBuilder extends ChatBuilderV2 {
@Override @Override
public MinecraftPacket toServer() { public MinecraftPacket toServer() {
LegacyChatPacket chat = new LegacyChatPacket(); return new LegacyChatPacket(message, (byte) 0, null);
chat.setMessage(message);
return chat;
} }
} }

View File

@@ -20,12 +20,13 @@ package com.velocitypowered.proxy.protocol.packet.chat.legacy;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.UUID; import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable; 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 CHAT_TYPE = (byte) 0;
public static final byte SYSTEM_TYPE = (byte) 1; public static final byte SYSTEM_TYPE = (byte) 1;
@@ -34,91 +35,44 @@ public class LegacyChatPacket implements MinecraftPacket {
public static final int MAX_SERVERBOUND_MESSAGE_LENGTH = 256; public static final int MAX_SERVERBOUND_MESSAGE_LENGTH = 256;
public static final UUID EMPTY_SENDER = new UUID(0, 0); 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() { public String getMessage() {
if (message == null) {
throw new IllegalStateException("Message is not specified");
}
return message; 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
+ '}';
}
@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);
}
}
}
@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 @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<LegacyChatPacket> {
@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(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);
}
}
}
}
} }

View File

@@ -44,27 +44,14 @@ public class SessionChatBuilder extends ChatBuilderV2 {
LastSeenMessages lastSeenMessages = this.lastSeenMessages != null ? this.lastSeenMessages : new LastSeenMessages(); LastSeenMessages lastSeenMessages = this.lastSeenMessages != null ? this.lastSeenMessages : new LastSeenMessages();
if (message.startsWith("/")) { if (message.startsWith("/")) {
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
UnsignedPlayerCommandPacket command = new UnsignedPlayerCommandPacket(); return new UnsignedPlayerCommandPacket(message.substring(1));
command.command = message.substring(1);
return command;
} else { } else {
SessionPlayerCommandPacket command = new SessionPlayerCommandPacket(); return new SessionPlayerCommandPacket(message.substring(1), timestamp, 0L,
command.command = message.substring(1); new SessionPlayerCommandPacket.ArgumentSignatures(), lastSeenMessages);
command.salt = 0L;
command.timeStamp = timestamp;
command.argumentSignatures = new SessionPlayerCommandPacket.ArgumentSignatures();
command.lastSeenMessages = lastSeenMessages;
return command;
} }
} else { } else {
SessionPlayerChatPacket chat = new SessionPlayerChatPacket(); return new SessionPlayerChatPacket(message, timestamp, 0L, false, new byte[0],
chat.message = message; lastSeenMessages);
chat.signed = false;
chat.signature = new byte[0];
chat.timestamp = timestamp;
chat.salt = 0L;
chat.lastSeenMessages = lastSeenMessages;
return chat;
} }
} }
} }

View File

@@ -20,6 +20,7 @@ package com.velocitypowered.proxy.protocol.packet.chat.session;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages; import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@@ -27,14 +28,21 @@ import java.time.Instant;
public class SessionPlayerChatPacket implements MinecraftPacket { public class SessionPlayerChatPacket implements MinecraftPacket {
protected String message; protected final String message;
protected Instant timestamp; protected final Instant timestamp;
protected long salt; protected final long salt;
protected boolean signed; protected final boolean signed;
protected byte[] signature; protected final byte[] signature;
protected LastSeenMessages lastSeenMessages; protected final LastSeenMessages lastSeenMessages;
public SessionPlayerChatPacket() { 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() { public String getMessage() {
@@ -61,34 +69,6 @@ public class SessionPlayerChatPacket implements MinecraftPacket {
return lastSeenMessages; 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 @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
@@ -101,13 +81,38 @@ public class SessionPlayerChatPacket implements MinecraftPacket {
} }
public SessionPlayerChatPacket withLastSeenMessages(LastSeenMessages lastSeenMessages) { public SessionPlayerChatPacket withLastSeenMessages(LastSeenMessages lastSeenMessages) {
SessionPlayerChatPacket packet = new SessionPlayerChatPacket(); return new SessionPlayerChatPacket(message, timestamp, salt, signed, signature, lastSeenMessages);
packet.message = message; }
packet.timestamp = timestamp;
packet.salt = salt; public static class Codec implements PacketCodec<SessionPlayerChatPacket> {
packet.signed = signed; @Override
packet.signature = signature; public SessionPlayerChatPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
packet.lastSeenMessages = lastSeenMessages; ProtocolVersion protocolVersion) {
return packet; 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);
}
} }
} }

View File

@@ -22,6 +22,7 @@ import com.velocitypowered.api.event.command.CommandExecuteEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages; import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages;
import com.velocitypowered.proxy.util.except.QuietDecoderException; import com.velocitypowered.proxy.util.except.QuietDecoderException;
@@ -33,29 +34,19 @@ import java.util.List;
public class SessionPlayerCommandPacket implements MinecraftPacket { public class SessionPlayerCommandPacket implements MinecraftPacket {
protected String command; protected final String command;
protected Instant timeStamp; protected final Instant timeStamp;
protected long salt; protected final long salt;
protected ArgumentSignatures argumentSignatures; protected final ArgumentSignatures argumentSignatures;
protected LastSeenMessages lastSeenMessages; protected final LastSeenMessages lastSeenMessages;
@Override public SessionPlayerCommandPacket(String command, Instant timeStamp, long salt,
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { ArgumentSignatures argumentSignatures, LastSeenMessages lastSeenMessages) {
int cap = protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5) ? 256 : ProtocolUtils.DEFAULT_MAX_STRING_SIZE; this.command = command;
this.command = ProtocolUtils.readString(buf, cap); this.timeStamp = timeStamp;
this.timeStamp = Instant.ofEpochMilli(buf.readLong()); this.salt = salt;
this.salt = buf.readLong(); this.argumentSignatures = argumentSignatures;
this.argumentSignatures = new ArgumentSignatures(buf); this.lastSeenMessages = lastSeenMessages;
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);
} }
public String getCommand() { public String getCommand() {
@@ -92,17 +83,33 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
public SessionPlayerCommandPacket withLastSeenMessages(@Nullable LastSeenMessages lastSeenMessages) { public SessionPlayerCommandPacket withLastSeenMessages(@Nullable LastSeenMessages lastSeenMessages) {
if (lastSeenMessages == null) { if (lastSeenMessages == null) {
UnsignedPlayerCommandPacket packet = new UnsignedPlayerCommandPacket(); return new UnsignedPlayerCommandPacket(command);
packet.command = command; }
return packet; return new SessionPlayerCommandPacket(command, timeStamp, salt, argumentSignatures, lastSeenMessages);
}
public static class Codec implements PacketCodec<SessionPlayerCommandPacket> {
@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 { public static class ArgumentSignatures {

View File

@@ -19,21 +19,18 @@ package com.velocitypowered.proxy.protocol.packet.chat.session;
import com.velocitypowered.api.event.command.CommandExecuteEvent; import com.velocitypowered.api.event.command.CommandExecuteEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages; import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket { import java.time.Instant;
@Override public final class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket {
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
this.command = ProtocolUtils.readString(buf, ProtocolUtils.DEFAULT_MAX_STRING_SIZE);
}
@Override public UnsignedPlayerCommandPacket(String command) {
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { super(command, Instant.EPOCH, 0L, new ArgumentSignatures(), null);
ProtocolUtils.writeString(buf, this.command);
} }
@Override @Override
@@ -41,6 +38,7 @@ public class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket {
return this; return this;
} }
@Override
public boolean isSigned() { public boolean isSigned() {
return false; return false;
} }
@@ -56,4 +54,19 @@ public class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket {
"command='" + command + '\'' + "command='" + command + '\'' +
'}'; '}';
} }
public static class Codec implements PacketCodec<UnsignedPlayerCommandPacket> {
@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);
}
}
} }

View File

@@ -20,44 +20,38 @@ package com.velocitypowered.proxy.protocol.packet.config;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
public class ActiveFeaturesPacket implements MinecraftPacket { public record ActiveFeaturesPacket(Key[] activeFeatures) implements MinecraftPacket {
private Key[] activeFeatures;
public ActiveFeaturesPacket(Key[] activeFeatures) {
this.activeFeatures = activeFeatures;
}
public ActiveFeaturesPacket() { public ActiveFeaturesPacket() {
this.activeFeatures = new Key[0]; this(new Key[0]);
}
public void setActiveFeatures(Key[] activeFeatures) {
this.activeFeatures = activeFeatures;
} }
public Key[] getActiveFeatures() { public Key[] getActiveFeatures() {
return activeFeatures; 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 @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<ActiveFeaturesPacket> {
@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);
}
}
} }

View File

@@ -20,40 +20,16 @@ package com.velocitypowered.proxy.protocol.packet.config;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class ClientboundCustomReportDetailsPacket implements MinecraftPacket { public record ClientboundCustomReportDetailsPacket(Map<String, String> details) implements MinecraftPacket {
private Map<String, String> details;
public ClientboundCustomReportDetailsPacket() { public ClientboundCustomReportDetailsPacket() {
} this(Map.of());
public ClientboundCustomReportDetailsPacket(Map<String, String> details) {
this.details = details;
}
@Override
public void 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));
}
}
@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 @Override
@@ -61,7 +37,28 @@ public class ClientboundCustomReportDetailsPacket implements MinecraftPacket {
return handler.handle(this); return handler.handle(this);
} }
public Map<String, String> getDetails() { public static class Codec implements PacketCodec<ClientboundCustomReportDetailsPacket> {
return details; @Override
public ClientboundCustomReportDetailsPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
int detailsCount = ProtocolUtils.readVarInt(buf);
Map<String, String> 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(ClientboundCustomReportDetailsPacket packet, ByteBuf buf,
ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, packet.details.size());
packet.details.forEach((key, detail) -> {
ProtocolUtils.writeString(buf, key);
ProtocolUtils.writeString(buf, detail);
});
}
} }
} }

View File

@@ -21,40 +21,17 @@ import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.util.ServerLink; import com.velocitypowered.api.util.ServerLink;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class ClientboundServerLinksPacket implements MinecraftPacket { public record ClientboundServerLinksPacket(List<ServerLink> serverLinks) implements MinecraftPacket {
private List<ServerLink> serverLinks;
public ClientboundServerLinksPacket() { public ClientboundServerLinksPacket() {
} this(List.of());
public ClientboundServerLinksPacket(List<ServerLink> 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);
}
} }
@Override @Override
@@ -62,10 +39,6 @@ public class ClientboundServerLinksPacket implements MinecraftPacket {
return handler.handle(this); return handler.handle(this);
} }
public List<ServerLink> getServerLinks() {
return serverLinks;
}
public record ServerLink(int id, ComponentHolder displayName, String url) { public record ServerLink(int id, ComponentHolder displayName, String url) {
private static ServerLink read(ByteBuf buf, ProtocolVersion version) { private static ServerLink read(ByteBuf buf, ProtocolVersion version) {
@@ -87,4 +60,28 @@ public class ClientboundServerLinksPacket implements MinecraftPacket {
ProtocolUtils.writeString(buf, url); ProtocolUtils.writeString(buf, url);
} }
} }
public static class Codec implements PacketCodec<ClientboundServerLinksPacket> {
@Override
public ClientboundServerLinksPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
int linksCount = ProtocolUtils.readVarInt(buf);
List<ServerLink> 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);
}
}
}
} }

View File

@@ -20,26 +20,32 @@ package com.velocitypowered.proxy.protocol.packet.config;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class CodeOfConductAcceptPacket implements MinecraftPacket { public final class CodeOfConductAcceptPacket implements MinecraftPacket {
public static final CodeOfConductAcceptPacket INSTANCE = new CodeOfConductAcceptPacket(); public static final CodeOfConductAcceptPacket INSTANCE = new CodeOfConductAcceptPacket();
private CodeOfConductAcceptPacket() { private CodeOfConductAcceptPacket() {
} }
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<CodeOfConductAcceptPacket> {
@Override
public CodeOfConductAcceptPacket decode(ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
return INSTANCE;
}
@Override
public void encode(CodeOfConductAcceptPacket packet, ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
}
}
} }

View File

@@ -20,24 +20,15 @@ package com.velocitypowered.proxy.protocol.packet.config;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
import io.netty.buffer.ByteBuf; 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() { public CodeOfConductPacket(ByteBuf buf) {
super(null); super(buf);
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
this.replace(buf.readRetainedSlice(buf.readableBytes()));
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
buf.writeBytes(this.content());
} }
@Override @Override
@@ -46,7 +37,61 @@ public class CodeOfConductPacket extends DeferredByteBufHolder implements Minecr
} }
@Override @Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) { public CodeOfConductPacket copy() {
return content().readableBytes(); return (CodeOfConductPacket) super.copy();
}
@Override
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 CodeOfConductPacket touch(Object hint) {
return (CodeOfConductPacket) super.touch(hint);
}
public static class Codec implements PacketCodec<CodeOfConductPacket> {
@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();
}
} }
} }

View File

@@ -20,33 +20,37 @@ package com.velocitypowered.proxy.protocol.packet.config;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class FinishedUpdatePacket implements MinecraftPacket { public final class FinishedUpdatePacket implements MinecraftPacket {
public static final FinishedUpdatePacket INSTANCE = new FinishedUpdatePacket(); public static final FinishedUpdatePacket INSTANCE = new FinishedUpdatePacket();
private 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 @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<FinishedUpdatePacket> {
@Override
public FinishedUpdatePacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
return 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;
}
}
} }

View File

@@ -20,43 +20,15 @@ package com.velocitypowered.proxy.protocol.packet.config;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.util.except.QuietDecoderException; import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf; 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); public KnownPacksPacket() {
private static final QuietDecoderException TOO_MANY_PACKS = this(new KnownPack[0]);
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);
}
} }
@Override @Override
@@ -75,4 +47,37 @@ public class KnownPacksPacket implements MinecraftPacket {
ProtocolUtils.writeString(buf, version); ProtocolUtils.writeString(buf, version);
} }
} }
public static class Codec implements PacketCodec<KnownPacksPacket> {
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);
}
}
}
} }

View File

@@ -20,28 +20,16 @@ package com.velocitypowered.proxy.protocol.packet.config;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
import io.netty.buffer.ByteBuf; 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() { public RegistrySyncPacket(ByteBuf backing) {
super(null); super(backing);
}
// 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());
} }
@Override @Override
@@ -49,8 +37,22 @@ public class RegistrySyncPacket extends DeferredByteBufHolder implements Minecra
return handler.handle(this); return handler.handle(this);
} }
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) { public int encodeSizeHint(Direction direction, ProtocolVersion version) {
return content().readableBytes(); return content().readableBytes();
} }
public static class Codec implements PacketCodec<RegistrySyncPacket> {
@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());
}
}
} }

View File

@@ -20,33 +20,37 @@ package com.velocitypowered.proxy.protocol.packet.config;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class StartUpdatePacket implements MinecraftPacket { public final class StartUpdatePacket implements MinecraftPacket {
public static final StartUpdatePacket INSTANCE = new StartUpdatePacket(); public static final StartUpdatePacket INSTANCE = new StartUpdatePacket();
private 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 @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<StartUpdatePacket> {
@Override
public StartUpdatePacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
return 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;
}
}
} }

View File

@@ -21,59 +21,23 @@ import com.google.common.collect.ImmutableMap;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.Map; import java.util.Map;
public class TagsUpdatePacket implements MinecraftPacket { public final class TagsUpdatePacket implements MinecraftPacket {
private Map<String, Map<String, int[]>> tags; private final Map<String, Map<String, int[]>> tags;
public TagsUpdatePacket(Map<String, Map<String, int[]>> tags) { public TagsUpdatePacket(Map<String, Map<String, int[]>> tags) {
this.tags = tags; this.tags = ImmutableMap.copyOf(tags);
} }
public TagsUpdatePacket() { public Map<String, Map<String, int[]>> getTags() {
this.tags = Map.of(); return tags;
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
ImmutableMap.Builder<String, Map<String, int[]>> builder = ImmutableMap.builder();
int size = ProtocolUtils.readVarInt(buf);
for (int i = 0; i < size; i++) {
String key = ProtocolUtils.readString(buf);
int innerSize = ProtocolUtils.readVarInt(buf);
ImmutableMap.Builder<String, int[]> 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());
}
tags = builder.build();
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, tags.size());
for (Map.Entry<String, Map<String, int[]>> entry : tags.entrySet()) {
ProtocolUtils.writeString(buf, entry.getKey());
// Oh, joy
ProtocolUtils.writeVarInt(buf, entry.getValue().size());
for (Map.Entry<String, int[]> innerEntry : entry.getValue().entrySet()) {
// Yea, object oriented programming be damned
ProtocolUtils.writeString(buf, innerEntry.getKey());
ProtocolUtils.writeVarIntArray(buf, innerEntry.getValue());
}
}
} }
@Override @Override
@@ -81,21 +45,61 @@ public class TagsUpdatePacket implements MinecraftPacket {
return handler.handle(this); return handler.handle(this);
} }
@Override public static class Codec implements PacketCodec<TagsUpdatePacket> {
public int encodeSizeHint(Direction direction, ProtocolVersion version) { @Override
int size = ProtocolUtils.varIntBytes(tags.size()); public TagsUpdatePacket decode(ByteBuf buf, Direction direction,
for (Map.Entry<String, Map<String, int[]>> entry : tags.entrySet()) { ProtocolVersion protocolVersion) {
size += ProtocolUtils.stringSizeHint(entry.getKey()); ImmutableMap.Builder<String, Map<String, int[]>> builder = ImmutableMap.builder();
size += ProtocolUtils.varIntBytes(entry.getValue().size()); int size = ProtocolUtils.readVarInt(buf);
for (Map.Entry<String, int[]> innerEntry : entry.getValue().entrySet()) { for (int i = 0; i < size; i++) {
size += ProtocolUtils.stringSizeHint(innerEntry.getKey()); String key = ProtocolUtils.readString(buf);
size += ProtocolUtils.varIntBytes(innerEntry.getValue().length);
for (int innerEntryValue : innerEntry.getValue()) { int innerSize = ProtocolUtils.readVarInt(buf);
size += ProtocolUtils.varIntBytes(innerEntryValue); ImmutableMap.Builder<String, int[]> 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 void encode(TagsUpdatePacket packet, ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, packet.tags.size());
for (Map.Entry<String, Map<String, int[]>> entry : packet.tags.entrySet()) {
ProtocolUtils.writeString(buf, entry.getKey());
// Oh, joy
ProtocolUtils.writeVarInt(buf, entry.getValue().size());
for (Map.Entry<String, int[]> 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<String, Map<String, int[]>> entry : tags.entrySet()) {
size += ProtocolUtils.stringSizeHint(entry.getKey());
size += ProtocolUtils.varIntBytes(entry.getValue().size());
for (Map.Entry<String, int[]> 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;
}
} }
} }

View File

@@ -19,9 +19,7 @@ package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf;
public abstract class GenericTitlePacket implements MinecraftPacket { public abstract class GenericTitlePacket implements MinecraftPacket {
@@ -45,10 +43,9 @@ public abstract class GenericTitlePacket implements MinecraftPacket {
} }
} }
private final ActionType action;
private ActionType action; protected GenericTitlePacket(ActionType action) {
protected void setAction(ActionType action) {
this.action = action; this.action = action;
} }
@@ -60,76 +57,77 @@ public abstract class GenericTitlePacket implements MinecraftPacket {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); 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() { public int getFadeIn() {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); 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() { public int getStay() {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); 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() { public int getFadeOut() {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType"); 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. * Creates a version and type dependent TitlePacket for HIDE/RESET actions.
* *
* @param type Action the packet should invoke * @param type Action the packet should invoke (HIDE or RESET)
* @param version Protocol version of the target player * @param version Protocol version of the target player
* @return GenericTitlePacket instance that follows the invoker type/version * @return GenericTitlePacket instance that follows the invoker type/version
*/ */
public static GenericTitlePacket constructTitlePacket(ActionType type, ProtocolVersion version) { public static GenericTitlePacket createClearTitlePacket(ActionType type, ProtocolVersion version) {
GenericTitlePacket packet = null; 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 for component-based actions.
*
* @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 createComponentTitlePacket(ActionType type,
ComponentHolder component, ProtocolVersion version) {
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) {
switch (type) { switch (type) {
case SET_ACTION_BAR: case SET_ACTION_BAR:
packet = new TitleActionbarPacket(); return new TitleActionbarPacket(component);
break;
case SET_SUBTITLE: case SET_SUBTITLE:
packet = new TitleSubtitlePacket(); return new TitleSubtitlePacket(component);
break;
case SET_TIMES:
packet = new TitleTimesPacket();
break;
case SET_TITLE: case SET_TITLE:
packet = new TitleTextPacket(); return new TitleTextPacket(component);
break;
case HIDE:
case RESET:
packet = new TitleClearPacket();
break;
default: default:
throw new IllegalArgumentException("Invalid ActionType"); throw new IllegalArgumentException("Invalid ActionType for component title: " + type);
} }
} else { } 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);
}
}
} }

View File

@@ -19,52 +19,26 @@ package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class LegacyTitlePacket extends GenericTitlePacket { public final class LegacyTitlePacket extends GenericTitlePacket {
private @Nullable ComponentHolder component; private final @Nullable ComponentHolder component;
private int fadeIn; private final int fadeIn;
private int stay; private final int stay;
private int fadeOut; private final int fadeOut;
@Override public LegacyTitlePacket(ActionType action, @Nullable ComponentHolder component,
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { int fadeIn, int stay, int fadeOut) {
if (version.lessThan(ProtocolVersion.MINECRAFT_1_11) super(action);
&& getAction() == ActionType.SET_ACTION_BAR) { this.component = component;
throw new IllegalStateException("Action bars are only supported on 1.11 and newer"); this.fadeIn = fadeIn;
} this.stay = stay;
ProtocolUtils.writeVarInt(buf, getAction().getAction(version)); this.fadeOut = fadeOut;
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);
} }
@Override @Override
@@ -72,44 +46,24 @@ public class LegacyTitlePacket extends GenericTitlePacket {
return component; return component;
} }
@Override
public void setComponent(@Nullable ComponentHolder component) {
this.component = component;
}
@Override @Override
public int getFadeIn() { public int getFadeIn() {
return fadeIn; return fadeIn;
} }
@Override
public void setFadeIn(int fadeIn) {
this.fadeIn = fadeIn;
}
@Override @Override
public int getStay() { public int getStay() {
return stay; return stay;
} }
@Override
public void setStay(int stay) {
this.stay = stay;
}
@Override @Override
public int getFadeOut() { public int getFadeOut() {
return fadeOut; return fadeOut;
} }
@Override
public void setFadeOut(int fadeOut) {
this.fadeOut = fadeOut;
}
@Override @Override
public String toString() { public String toString() {
return "GenericTitlePacket{" return "LegacyTitlePacket{"
+ "action=" + getAction() + "action=" + getAction()
+ ", component='" + component + '\'' + ", component='" + component + '\''
+ ", fadeIn=" + fadeIn + ", fadeIn=" + fadeIn
@@ -122,4 +76,43 @@ public class LegacyTitlePacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<LegacyTitlePacket> {
@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());
}
}
}
} }

View File

@@ -19,21 +19,18 @@ package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf; 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() { public TitleActionbarPacket(ComponentHolder component) {
setAction(ActionType.SET_TITLE); super(ActionType.SET_ACTION_BAR);
} this.component = component;
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
component.write(buf);
} }
@Override @Override
@@ -41,15 +38,10 @@ public class TitleActionbarPacket extends GenericTitlePacket {
return component; return component;
} }
@Override
public void setComponent(ComponentHolder component) {
this.component = component;
}
@Override @Override
public String toString() { public String toString() {
return "TitleActionbarPacket{" return "TitleActionbarPacket{"
+ ", component='" + component + '\'' + "component='" + component + '\''
+ '}'; + '}';
} }
@@ -57,4 +49,18 @@ public class TitleActionbarPacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<TitleActionbarPacket> {
@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);
}
}
} }

View File

@@ -19,32 +19,27 @@ package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class TitleClearPacket extends GenericTitlePacket { public final class TitleClearPacket extends GenericTitlePacket {
public TitleClearPacket() { private final boolean reset;
setAction(ActionType.HIDE);
public TitleClearPacket(boolean reset) {
super(reset ? ActionType.RESET : ActionType.HIDE);
this.reset = reset;
} }
@Override public boolean isReset() {
public void setAction(ActionType action) { return reset;
if (action != ActionType.HIDE && action != ActionType.RESET) {
throw new IllegalArgumentException("TitleClearPacket only accepts CLEAR and RESET actions");
}
super.setAction(action);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
buf.writeBoolean(getAction() == ActionType.RESET);
} }
@Override @Override
public String toString() { public String toString() {
return "TitleClearPacket{" return "TitleClearPacket{"
+ ", resetTimes=" + (getAction() == ActionType.RESET) + "resetTimes=" + reset
+ '}'; + '}';
} }
@@ -52,4 +47,18 @@ public class TitleClearPacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<TitleClearPacket> {
@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);
}
}
} }

View File

@@ -19,21 +19,18 @@ package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf; 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() { public TitleSubtitlePacket(ComponentHolder component) {
setAction(ActionType.SET_SUBTITLE); super(ActionType.SET_SUBTITLE);
} this.component = component;
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
component.write(buf);
} }
@Override @Override
@@ -41,15 +38,10 @@ public class TitleSubtitlePacket extends GenericTitlePacket {
return component; return component;
} }
@Override
public void setComponent(ComponentHolder component) {
this.component = component;
}
@Override @Override
public String toString() { public String toString() {
return "TitleSubtitlePacket{" return "TitleSubtitlePacket{"
+ ", component='" + component + '\'' + "component='" + component + '\''
+ '}'; + '}';
} }
@@ -57,4 +49,18 @@ public class TitleSubtitlePacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<TitleSubtitlePacket> {
@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);
}
}
} }

View File

@@ -19,21 +19,18 @@ package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf; 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() { public TitleTextPacket(ComponentHolder component) {
setAction(ActionType.SET_TITLE); super(ActionType.SET_TITLE);
} this.component = component;
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
component.write(buf);
} }
@Override @Override
@@ -41,15 +38,10 @@ public class TitleTextPacket extends GenericTitlePacket {
return component; return component;
} }
@Override
public void setComponent(ComponentHolder component) {
this.component = component;
}
@Override @Override
public String toString() { public String toString() {
return "TitleTextPacket{" return "TitleTextPacket{"
+ ", component='" + component + '\'' + "component='" + component + '\''
+ '}'; + '}';
} }
@@ -57,4 +49,18 @@ public class TitleTextPacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<TitleTextPacket> {
@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);
}
}
} }

View File

@@ -19,24 +19,21 @@ package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class TitleTimesPacket extends GenericTitlePacket { public final class TitleTimesPacket extends GenericTitlePacket {
private int fadeIn; private final int fadeIn;
private int stay; private final int stay;
private int fadeOut; private final int fadeOut;
public TitleTimesPacket() { public TitleTimesPacket(int fadeIn, int stay, int fadeOut) {
setAction(ActionType.SET_TIMES); super(ActionType.SET_TIMES);
} this.fadeIn = fadeIn;
this.stay = stay;
@Override this.fadeOut = fadeOut;
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
buf.writeInt(fadeIn);
buf.writeInt(stay);
buf.writeInt(fadeOut);
} }
@Override @Override
@@ -44,35 +41,20 @@ public class TitleTimesPacket extends GenericTitlePacket {
return fadeIn; return fadeIn;
} }
@Override
public void setFadeIn(int fadeIn) {
this.fadeIn = fadeIn;
}
@Override @Override
public int getStay() { public int getStay() {
return stay; return stay;
} }
@Override
public void setStay(int stay) {
this.stay = stay;
}
@Override @Override
public int getFadeOut() { public int getFadeOut() {
return fadeOut; return fadeOut;
} }
@Override
public void setFadeOut(int fadeOut) {
this.fadeOut = fadeOut;
}
@Override @Override
public String toString() { public String toString() {
return "TitleTimesPacket{" return "TitleTimesPacket{"
+ ", fadeIn=" + fadeIn + "fadeIn=" + fadeIn
+ ", stay=" + stay + ", stay=" + stay
+ ", fadeOut=" + fadeOut + ", fadeOut=" + fadeOut
+ '}'; + '}';
@@ -82,4 +64,20 @@ public class TitleTimesPacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);
} }
public static class Codec implements PacketCodec<TitleTimesPacket> {
@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);
}
}
} }

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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 + "]";
}
}

View File

@@ -56,12 +56,10 @@ public class PingSessionHandler implements MinecraftSessionHandler {
@Override @Override
public void activated() { public void activated() {
HandshakePacket handshake = new HandshakePacket(); String serverAddress = this.virtualHostString == null || this.virtualHostString.isEmpty()
handshake.setIntent(HandshakeIntent.STATUS); ? server.getServerInfo().getAddress().getHostString() : this.virtualHostString;
handshake.setServerAddress(this.virtualHostString == null || this.virtualHostString.isEmpty() HandshakePacket handshake = new HandshakePacket(version, serverAddress,
? server.getServerInfo().getAddress().getHostString() : this.virtualHostString); server.getServerInfo().getAddress().getPort(), HandshakeIntent.STATUS);
handshake.setPort(server.getServerInfo().getAddress().getPort());
handshake.setProtocolVersion(version);
connection.delayedWrite(handshake); connection.delayedWrite(handshake);
connection.setActiveSessionHandler(StateRegistry.STATUS); connection.setActiveSessionHandler(StateRegistry.STATUS);

View File

@@ -45,7 +45,7 @@ class PacketRegistryTest {
private StateRegistry.PacketRegistry setupRegistry() { private StateRegistry.PacketRegistry setupRegistry() {
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY);
registry.register(HandshakePacket.class, HandshakePacket::new, registry.register(HandshakePacket.class, new HandshakePacket.Codec(),
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_8, null, false), new StateRegistry.PacketMapping(0x01, MINECRAFT_1_8, null, false),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, null, false), new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, null, false),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_15, MINECRAFT_1_16, false)); new StateRegistry.PacketMapping(0x00, MINECRAFT_1_15, MINECRAFT_1_16, false));
@@ -55,29 +55,30 @@ class PacketRegistryTest {
@Test @Test
void packetRegistryWorks() { void packetRegistryWorks() {
StateRegistry.PacketRegistry registry = setupRegistry(); StateRegistry.PacketRegistry registry = setupRegistry();
MinecraftPacket packet = registry.getProtocolRegistry(MINECRAFT_1_12).createPacket(0); PacketCodec<?> packet = registry.getProtocolRegistry(MINECRAFT_1_12).getCodec(0);
assertNotNull(packet, "Packet was not found in registry"); 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.getProtocolRegistry(MINECRAFT_1_12).getPacketId(new HandshakePacket()),
"Registry did not return the correct packet ID"); "Registry did not return the correct packet ID");
} }
@Test @Test
void packetRegistryLinkingWorks() { void packetRegistryLinkingWorks() {
StateRegistry.PacketRegistry registry = setupRegistry(); StateRegistry.PacketRegistry registry = setupRegistry();
MinecraftPacket packet = registry.getProtocolRegistry(MINECRAFT_1_12_1).createPacket(0); PacketCodec<?> packet = registry.getProtocolRegistry(MINECRAFT_1_12_1).getCodec(0);
assertNotNull(packet, "Packet was not found in registry"); 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_1).getPacketId(packet), HandshakePacket handshakePacket = new HandshakePacket();
assertEquals(0, registry.getProtocolRegistry(MINECRAFT_1_12_1).getPacketId(handshakePacket),
"Registry did not return the correct packet ID"); "Registry did not return the correct packet ID");
assertEquals(0, registry.getProtocolRegistry(MINECRAFT_1_14_2).getPacketId(packet), assertEquals(0, registry.getProtocolRegistry(MINECRAFT_1_14_2).getPacketId(handshakePacket),
"Registry did not return the correct packet ID"); "Registry did not return the correct packet ID");
assertEquals(1, registry.getProtocolRegistry(MINECRAFT_1_11).getPacketId(packet), assertEquals(1, registry.getProtocolRegistry(MINECRAFT_1_11).getPacketId(handshakePacket),
"Registry did not return the correct packet ID"); "Registry did not return the correct packet ID");
assertNull(registry.getProtocolRegistry(MINECRAFT_1_14_2).createPacket(0x01), assertNull(registry.getProtocolRegistry(MINECRAFT_1_14_2).getCodec(0x01),
"Registry should return a null"); "Registry should return a null");
assertNull(registry.getProtocolRegistry(MINECRAFT_1_16_2).createPacket(0), assertNull(registry.getProtocolRegistry(MINECRAFT_1_16_2).getCodec(0),
"Registry should return null"); "Registry should return null");
} }
@@ -86,7 +87,7 @@ class PacketRegistryTest {
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY);
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.register(HandshakePacket.class, HandshakePacket::new)); () -> registry.register(HandshakePacket.class, new HandshakePacket.Codec()));
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.getProtocolRegistry(ProtocolVersion.UNKNOWN) () -> registry.getProtocolRegistry(ProtocolVersion.UNKNOWN)
.getPacketId(new HandshakePacket())); .getPacketId(new HandshakePacket()));
@@ -97,18 +98,18 @@ class PacketRegistryTest {
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY);
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.register(HandshakePacket.class, HandshakePacket::new, () -> registry.register(HandshakePacket.class, new HandshakePacket.Codec(),
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false), new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false))); new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false)));
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.register(HandshakePacket.class, HandshakePacket::new, () -> registry.register(HandshakePacket.class, new HandshakePacket.Codec(),
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false), new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false),
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false))); new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false)));
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.register(HandshakePacket.class, HandshakePacket::new, () -> registry.register(HandshakePacket.class, new HandshakePacket.Codec(),
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, MINECRAFT_1_8, false))); new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, MINECRAFT_1_8, false)));
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.register(HandshakePacket.class, HandshakePacket::new, () -> registry.register(HandshakePacket.class, new HandshakePacket.Codec(),
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_8, MINECRAFT_1_14, false), new StateRegistry.PacketMapping(0x01, MINECRAFT_1_8, MINECRAFT_1_14, false),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_16, null, false))); new StateRegistry.PacketMapping(0x00, MINECRAFT_1_16, null, false)));
} }
@@ -117,13 +118,13 @@ class PacketRegistryTest {
void failOnDuplicate() { void failOnDuplicate() {
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY);
registry.register(HandshakePacket.class, HandshakePacket::new, registry.register(HandshakePacket.class, new HandshakePacket.Codec(),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false)); new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false));
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.register(HandshakePacket.class, HandshakePacket::new, () -> registry.register(HandshakePacket.class, new HandshakePacket.Codec(),
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12, null, false))); new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12, null, false)));
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.register(StatusPingPacket.class, StatusPingPacket::new, () -> registry.register(StatusPingPacket.class, new StatusPingPacket.Codec(),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_13, null, false))); new StateRegistry.PacketMapping(0x00, MINECRAFT_1_13, null, false)));
} }
@@ -131,7 +132,7 @@ class PacketRegistryTest {
void shouldNotFailWhenRegisterLatestProtocolVersion() { void shouldNotFailWhenRegisterLatestProtocolVersion() {
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY);
assertDoesNotThrow(() -> registry.register(HandshakePacket.class, HandshakePacket::new, assertDoesNotThrow(() -> registry.register(HandshakePacket.class, new HandshakePacket.Codec(),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false), new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false),
new StateRegistry.PacketMapping(0x01, getLast(ProtocolVersion.SUPPORTED_VERSIONS), new StateRegistry.PacketMapping(0x01, getLast(ProtocolVersion.SUPPORTED_VERSIONS),
null, false))); null, false)));
@@ -141,19 +142,19 @@ class PacketRegistryTest {
void registrySuppliesCorrectPacketsByProtocol() { void registrySuppliesCorrectPacketsByProtocol() {
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY); ProtocolUtils.Direction.CLIENTBOUND, StateRegistry.PLAY);
registry.register(HandshakePacket.class, HandshakePacket::new, registry.register(HandshakePacket.class, new HandshakePacket.Codec(),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, null, false), new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, null, false),
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12_1, null, false), new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12_1, null, false),
new StateRegistry.PacketMapping(0x02, MINECRAFT_1_13, null, false)); new StateRegistry.PacketMapping(0x02, MINECRAFT_1_13, null, false));
assertEquals(HandshakePacket.class, assertEquals(HandshakePacket.Codec.class,
registry.getProtocolRegistry(MINECRAFT_1_12).createPacket(0x00).getClass()); registry.getProtocolRegistry(MINECRAFT_1_12).getCodec(0x00).getClass());
assertEquals(HandshakePacket.class, assertEquals(HandshakePacket.Codec.class,
registry.getProtocolRegistry(MINECRAFT_1_12_1).createPacket(0x01).getClass()); registry.getProtocolRegistry(MINECRAFT_1_12_1).getCodec(0x01).getClass());
assertEquals(HandshakePacket.class, assertEquals(HandshakePacket.Codec.class,
registry.getProtocolRegistry(MINECRAFT_1_12_2).createPacket(0x01).getClass()); registry.getProtocolRegistry(MINECRAFT_1_12_2).getCodec(0x01).getClass());
assertEquals(HandshakePacket.class, assertEquals(HandshakePacket.Codec.class,
registry.getProtocolRegistry(MINECRAFT_1_13).createPacket(0x02).getClass()); registry.getProtocolRegistry(MINECRAFT_1_13).getCodec(0x02).getClass());
assertEquals(HandshakePacket.class, assertEquals(HandshakePacket.Codec.class,
registry.getProtocolRegistry(MINECRAFT_1_14_2).createPacket(0x02).getClass()); registry.getProtocolRegistry(MINECRAFT_1_14_2).getCodec(0x02).getClass());
} }
} }