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) {
if (this.viewers.remove(viewer)) {
viewer.getBossBarManager().remove(this, BossBarPacket.createRemovePacket(this.id, this.bar));
viewer.getBossBarManager().remove(this, BossBarPacket.createRemovePacket(this.id));
return true;
}
return false;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,33 +17,23 @@
package com.velocitypowered.proxy.protocol;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import io.netty.buffer.ByteBuf;
/**
* Represents a Minecraft packet.
* Represents a Minecraft packet. Packets are immutable data holders that use separate
* {@link PacketCodec} implementations for encoding and decoding.
*
* @see PacketCodec
* @see PacketEncoder
* @see PacketDecoder
*/
public interface MinecraftPacket {
void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion);
void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion);
/**
* Handles this packet using the visitor pattern.
*
* @param handler the session handler
* @return true if the packet was handled
*/
boolean handle(MinecraftSessionHandler handler);
default int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
return -1;
}
default int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
return 0;
}
default int encodeSizeHint(ProtocolUtils.Direction direction,
ProtocolVersion version) {
return -1;
}
}

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

View File

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

View File

@@ -20,6 +20,7 @@ package com.velocitypowered.proxy.protocol.netty;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.StateRegistry;
import io.netty.buffer.ByteBuf;
@@ -48,16 +49,31 @@ public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> {
}
@Override
@SuppressWarnings("unchecked")
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);
ProtocolUtils.writeVarInt(out, packetId);
msg.encode(out, direction, registry.version);
codec.encode(msg, out, direction, registry.version);
}
@Override
@SuppressWarnings("unchecked")
protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, MinecraftPacket msg,
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) {
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 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.StateRegistry;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
@@ -122,22 +122,21 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
}
final int payloadLength = length - ProtocolUtils.varIntBytes(packetId);
MinecraftPacket packet = registry.createPacket(packetId);
PacketCodec<?> codec = registry.getCodec(packetId);
// We handle every packet in this phase, if you said something we don't know, something is really wrong
if (packet == null) {
if (codec == null) {
throw UNKNOWN_PACKET;
}
// We 'technically' have the incoming bytes of a payload here, and so, these can actually parse
// the packet if needed, so, we'll take advantage of the existing methods
int expectedMinLen = packet.decodeExpectedMinLength(in, direction, registry.version);
int expectedMaxLen = packet.decodeExpectedMaxLength(in, direction, registry.version);
int expectedMinLen = codec.decodeExpectedMinLength(in, direction, registry.version);
int expectedMaxLen = codec.decodeExpectedMaxLength(in, direction, registry.version);
if (expectedMaxLen != -1 && payloadLength > expectedMaxLen) {
throw handleOverflow(packet, expectedMaxLen, in.readableBytes());
throw handleOverflow(expectedMaxLen, in.readableBytes());
}
if (payloadLength < expectedMinLen) {
throw handleUnderflow(packet, expectedMaxLen, in.readableBytes());
throw handleUnderflow(expectedMaxLen, in.readableBytes());
}
in.readerIndex(index);
@@ -224,18 +223,18 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
return result | (tmp & 0x7F) << 14;
}
private Exception handleOverflow(MinecraftPacket packet, int expected, int actual) {
private Exception handleOverflow(int expected, int actual) {
if (MinecraftDecoder.DEBUG) {
return new CorruptedFrameException("Packet sent for " + packet.getClass() + " was too "
return new CorruptedFrameException("Packet sent for handshake was too "
+ "big (expected " + expected + " bytes, got " + actual + " bytes)");
} else {
return FRAME_DECODER_FAILED;
}
}
private Exception handleUnderflow(MinecraftPacket packet, int expected, int actual) {
private Exception handleUnderflow(int expected, int actual) {
if (MinecraftDecoder.DEBUG) {
return new CorruptedFrameException("Packet sent for " + packet.getClass() + " was too "
return new CorruptedFrameException("Packet sent for handshake was too "
+ "small (expected " + expected + " bytes, got " + actual + " bytes)");
} else {
return FRAME_DECODER_FAILED;

View File

@@ -37,6 +37,7 @@ import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentPropertyRegistry;
@@ -54,7 +55,7 @@ import java.util.function.Predicate;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
public class AvailableCommandsPacket implements MinecraftPacket {
public final class AvailableCommandsPacket implements MinecraftPacket {
private static final Command<CommandSource> PLACEHOLDER_COMMAND = source -> 0;
private static final Predicate<CommandSource> PLACEHOLDER_REQUIREMENT = source -> true;
@@ -69,22 +70,25 @@ public class AvailableCommandsPacket implements MinecraftPacket {
private static final byte FLAG_HAS_SUGGESTIONS = 0x10;
private static final byte FLAG_IS_RESTRICTED = 0x20;
private @MonotonicNonNull RootCommandNode<CommandSource> rootNode;
private final RootCommandNode<CommandSource> rootNode;
/**
* Returns the root node.
*
* @return the root node
*/
public RootCommandNode<CommandSource> getRootNode() {
if (rootNode == null) {
throw new IllegalStateException("Packet not yet deserialized");
public AvailableCommandsPacket(RootCommandNode<CommandSource> rootNode) {
this.rootNode = rootNode;
}
public RootCommandNode<CommandSource> getRootNode() {
return rootNode;
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<AvailableCommandsPacket> {
@Override
public AvailableCommandsPacket decode(ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
int commands = ProtocolUtils.readVarInt(buf);
WireNode[] wireNodes = new WireNode[commands];
for (int i = 0; i < commands; i++) {
@@ -112,13 +116,15 @@ public class AvailableCommandsPacket implements MinecraftPacket {
}
int rootIdx = ProtocolUtils.readVarInt(buf);
rootNode = (RootCommandNode<CommandSource>) wireNodes[rootIdx].built;
RootCommandNode<CommandSource> rootNode = (RootCommandNode<CommandSource>) wireNodes[rootIdx].built;
return new AvailableCommandsPacket(rootNode);
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
public void encode(AvailableCommandsPacket packet, ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
// Assign all the children an index.
Deque<CommandNode<CommandSource>> childrenQueue = new ArrayDeque<>(ImmutableList.of(rootNode));
Deque<CommandNode<CommandSource>> childrenQueue = new ArrayDeque<>(ImmutableList.of(packet.rootNode));
Object2IntMap<CommandNode<CommandSource>> idMappings = new Object2IntLinkedOpenCustomHashMap<>(
IdentityHashStrategy.instance());
while (!childrenQueue.isEmpty()) {
@@ -137,7 +143,7 @@ public class AvailableCommandsPacket implements MinecraftPacket {
for (CommandNode<CommandSource> child : idMappings.keySet()) {
serializeNode(child, buf, idMappings, protocolVersion);
}
ProtocolUtils.writeVarInt(buf, idMappings.getInt(rootNode));
ProtocolUtils.writeVarInt(buf, idMappings.getInt(packet.rootNode));
}
private static void serializeNode(CommandNode<CommandSource> node, ByteBuf buf,
@@ -192,11 +198,6 @@ public class AvailableCommandsPacket implements MinecraftPacket {
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
private static WireNode deserializeNode(ByteBuf buf, int idx, ProtocolVersion version) {
byte flags = buf.readByte();
int[] children = ProtocolUtils.readIntegerArray(buf);
@@ -226,6 +227,15 @@ public class AvailableCommandsPacket implements MinecraftPacket {
}
}
@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;
}
}
private static class WireNode {
private final int idx;
@@ -362,12 +372,4 @@ public class AvailableCommandsPacket implements MinecraftPacket {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import com.velocitypowered.proxy.util.collect.Enum2IntMap;
@@ -29,7 +30,8 @@ import java.util.UUID;
import net.kyori.adventure.bossbar.BossBar;
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 =
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_STYLE = 4;
public static final int UPDATE_PROPERTIES = 5;
private @Nullable UUID uuid;
private int action;
private @Nullable ComponentHolder name;
private float percent;
private int color;
private int overlay;
private short flags;
public UUID getUuid() {
return uuid;
}
public int getAction() {
return action;
}
public static BossBarPacket createAddPacket(
final UUID id,
final BossBar bar,
final ComponentHolder name
) {
final BossBarPacket packet = new BossBarPacket();
packet.setUuid(id);
packet.setAction(BossBarPacket.ADD);
packet.setName(name);
packet.setColor(COLORS_TO_PROTOCOL.get(bar.color()));
packet.setOverlay(OVERLAY_TO_PROTOCOL.get(bar.overlay()));
packet.setPercent(bar.progress());
packet.setFlags(serializeFlags(bar.flags()));
return packet;
return new BossBarPacket(id, ADD, name, bar.progress(),
COLORS_TO_PROTOCOL.get(bar.color()), OVERLAY_TO_PROTOCOL.get(bar.overlay()),
serializeFlags(bar.flags()));
}
public static BossBarPacket createRemovePacket(final UUID id, final BossBar bar) {
final BossBarPacket packet = new BossBarPacket();
packet.setUuid(id);
packet.setAction(REMOVE);
return packet;
public static BossBarPacket createRemovePacket(final UUID id) {
return new BossBarPacket(id, REMOVE, null, 0, 0, 0, (short) 0);
}
public static BossBarPacket createUpdateProgressPacket(final UUID id, final BossBar bar) {
final BossBarPacket packet = new BossBarPacket();
packet.setUuid(id);
packet.setAction(UPDATE_PERCENT);
packet.setPercent(bar.progress());
return packet;
return new BossBarPacket(id, UPDATE_PERCENT, null, bar.progress(), 0, 0, (short) 0);
}
public static BossBarPacket createUpdateNamePacket(
@@ -106,177 +96,20 @@ public class BossBarPacket implements MinecraftPacket {
final BossBar bar,
final ComponentHolder name
) {
final BossBarPacket packet = new BossBarPacket();
packet.setUuid(id);
packet.setAction(UPDATE_NAME);
packet.setName(name);
return packet;
return new BossBarPacket(id, UPDATE_NAME, name, 0, 0, 0, (short) 0);
}
public static BossBarPacket createUpdateStylePacket(final UUID id, final BossBar bar) {
final BossBarPacket packet = new BossBarPacket();
packet.setUuid(id);
packet.setAction(UPDATE_STYLE);
packet.setColor(COLORS_TO_PROTOCOL.get(bar.color()));
packet.setOverlay(OVERLAY_TO_PROTOCOL.get(bar.overlay()));
return packet;
return new BossBarPacket(id, UPDATE_STYLE, null, 0,
COLORS_TO_PROTOCOL.get(bar.color()), OVERLAY_TO_PROTOCOL.get(bar.overlay()), (short) 0);
}
public static BossBarPacket createUpdatePropertiesPacket(final UUID id, final BossBar bar) {
final BossBarPacket packet = new BossBarPacket();
packet.setUuid(id);
packet.setAction(UPDATE_PROPERTIES);
packet.setFlags(serializeFlags(bar.flags()));
return packet;
return new BossBarPacket(id, UPDATE_PROPERTIES, null, 0, 0, 0, serializeFlags(bar.flags()));
}
public UUID getUuid() {
if (uuid == null) {
throw new IllegalStateException("No boss bar UUID specified");
}
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public int getAction() {
return action;
}
public void setAction(int action) {
this.action = action;
}
public @Nullable ComponentHolder getName() {
return name;
}
public void setName(ComponentHolder name) {
this.name = name;
}
public float getPercent() {
return percent;
}
public void setPercent(float percent) {
this.percent = percent;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public int getOverlay() {
return overlay;
}
public void setOverlay(int overlay) {
this.overlay = overlay;
}
public short getFlags() {
return flags;
}
public void setFlags(short flags) {
this.flags = flags;
}
@Override
public String toString() {
return "BossBar{"
+ "uuid=" + uuid
+ ", action=" + action
+ ", name='" + name + '\''
+ ", percent=" + percent
+ ", color=" + color
+ ", overlay=" + overlay
+ ", flags=" + flags
+ '}';
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
this.uuid = ProtocolUtils.readUuid(buf);
this.action = ProtocolUtils.readVarInt(buf);
switch (action) {
case ADD:
this.name = ComponentHolder.read(buf, version);
this.percent = buf.readFloat();
this.color = ProtocolUtils.readVarInt(buf);
this.overlay = ProtocolUtils.readVarInt(buf);
this.flags = buf.readUnsignedByte();
break;
case REMOVE:
break;
case UPDATE_PERCENT:
this.percent = buf.readFloat();
break;
case UPDATE_NAME:
this.name = ComponentHolder.read(buf, version);
break;
case UPDATE_STYLE:
this.color = ProtocolUtils.readVarInt(buf);
this.overlay = ProtocolUtils.readVarInt(buf);
break;
case UPDATE_PROPERTIES:
this.flags = buf.readUnsignedByte();
break;
default:
throw new UnsupportedOperationException("Unknown action " + action);
}
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (uuid == null) {
throw new IllegalStateException("No boss bar UUID specified");
}
ProtocolUtils.writeUuid(buf, uuid);
ProtocolUtils.writeVarInt(buf, action);
switch (action) {
case ADD:
if (name == null) {
throw new IllegalStateException("No name specified!");
}
name.write(buf);
buf.writeFloat(percent);
ProtocolUtils.writeVarInt(buf, color);
ProtocolUtils.writeVarInt(buf, overlay);
buf.writeByte(flags);
break;
case REMOVE:
break;
case UPDATE_PERCENT:
buf.writeFloat(percent);
break;
case UPDATE_NAME:
if (name == null) {
throw new IllegalStateException("No name specified!");
}
name.write(buf);
break;
case UPDATE_STYLE:
ProtocolUtils.writeVarInt(buf, color);
ProtocolUtils.writeVarInt(buf, overlay);
break;
case UPDATE_PROPERTIES:
buf.writeByte(flags);
break;
default:
throw new UnsupportedOperationException("Unknown action " + action);
}
}
private static byte serializeFlags(Set<BossBar.Flag> flags) {
byte val = 0x0;
private static short serializeFlags(Set<BossBar.Flag> flags) {
short val = 0x0;
for (BossBar.Flag flag : flags) {
val |= FLAG_BITS_TO_PROTOCOL.get(flag);
}
@@ -287,4 +120,86 @@ public class BossBarPacket implements MinecraftPacket {
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
@@ -29,18 +30,21 @@ public final class BundleDelimiterPacket implements MinecraftPacket {
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
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ClientSettingsPacket implements MinecraftPacket {
private @Nullable String locale;
private byte viewDistance;
private int chatVisibility;
private boolean chatColors;
private byte difficulty; // 1.7 Protocol
private short skinParts;
private int mainHand;
private boolean textFilteringEnabled; // Added in 1.17
private boolean clientListingAllowed; // Added in 1.18, overwrites server-list "anonymous" mode
private int particleStatus; // Added in 1.21.2
public final class ClientSettingsPacket implements MinecraftPacket {
private final @Nullable String locale;
private final byte viewDistance;
private final int chatVisibility;
private final boolean chatColors;
private final byte difficulty; // 1.7 Protocol
private final short skinParts;
private final int mainHand;
private final boolean textFilteringEnabled; // Added in 1.17
private final boolean clientListingAllowed; // Added in 1.18, overwrites server-list "anonymous" mode
private final int particleStatus; // Added in 1.21.2
public ClientSettingsPacket() {
this(null, (byte) 0, 0, false, (byte) 0, (short) 0, 0, false, false, 0);
}
public ClientSettingsPacket(String locale, byte viewDistance, int chatVisibility, boolean chatColors,
short skinParts, int mainHand, boolean textFilteringEnabled, boolean clientListingAllowed,
public ClientSettingsPacket(String locale, byte viewDistance, int chatVisibility,
boolean chatColors, short skinParts, int mainHand,
boolean textFilteringEnabled, boolean clientListingAllowed,
int particleStatus) {
this(locale, viewDistance, chatVisibility, chatColors, (byte) 0, skinParts, mainHand,
textFilteringEnabled, clientListingAllowed, particleStatus);
}
public ClientSettingsPacket(String locale, byte viewDistance, int chatVisibility,
boolean chatColors, byte difficulty, short skinParts, int mainHand,
boolean textFilteringEnabled, boolean clientListingAllowed,
int particleStatus) {
this.locale = locale;
this.viewDistance = viewDistance;
this.chatVisibility = chatVisibility;
this.chatColors = chatColors;
this.difficulty = difficulty;
this.skinParts = skinParts;
this.mainHand = mainHand;
this.textFilteringEnabled = textFilteringEnabled;
@@ -55,150 +67,51 @@ public class ClientSettingsPacket implements MinecraftPacket {
this.particleStatus = particleStatus;
}
public String getLocale() {
public String locale() {
if (locale == null) {
throw new IllegalStateException("No locale specified");
}
return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
public byte getViewDistance() {
public byte viewDistance() {
return viewDistance;
}
public void setViewDistance(byte viewDistance) {
this.viewDistance = viewDistance;
}
public int getChatVisibility() {
public int chatVisibility() {
return chatVisibility;
}
public void setChatVisibility(int chatVisibility) {
this.chatVisibility = chatVisibility;
}
public boolean isChatColors() {
public boolean chatColors() {
return chatColors;
}
public void setChatColors(boolean chatColors) {
this.chatColors = chatColors;
public byte difficulty() {
return difficulty;
}
public short getSkinParts() {
public short skinParts() {
return skinParts;
}
public void setSkinParts(short skinParts) {
this.skinParts = skinParts;
}
public int getMainHand() {
public int mainHand() {
return mainHand;
}
public void setMainHand(int mainHand) {
this.mainHand = mainHand;
}
public boolean isTextFilteringEnabled() {
public boolean textFilteringEnabled() {
return textFilteringEnabled;
}
public void setTextFilteringEnabled(boolean textFilteringEnabled) {
this.textFilteringEnabled = textFilteringEnabled;
}
public boolean isClientListingAllowed() {
public boolean clientListingAllowed() {
return clientListingAllowed;
}
public void setClientListingAllowed(boolean clientListingAllowed) {
this.clientListingAllowed = clientListingAllowed;
}
public int getParticleStatus() {
public int particleStatus() {
return particleStatus;
}
public void setParticleStatus(int particleStatus) {
this.particleStatus = particleStatus;
}
@Override
public String toString() {
return "ClientSettings{" + "locale='" + locale + '\'' + ", viewDistance=" + viewDistance +
", chatVisibility=" + chatVisibility + ", chatColors=" + chatColors + ", skinParts=" +
skinParts + ", mainHand=" + mainHand + ", chatFilteringEnabled=" + textFilteringEnabled +
", clientListingAllowed=" + clientListingAllowed + ", particleStatus=" + particleStatus + '}';
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
this.locale = ProtocolUtils.readString(buf, 16);
this.viewDistance = buf.readByte();
this.chatVisibility = ProtocolUtils.readVarInt(buf);
this.chatColors = buf.readBoolean();
if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_7_6)) {
this.difficulty = buf.readByte();
}
this.skinParts = buf.readUnsignedByte();
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9)) {
this.mainHand = ProtocolUtils.readVarInt(buf);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) {
this.textFilteringEnabled = buf.readBoolean();
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) {
this.clientListingAllowed = buf.readBoolean();
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
this.particleStatus = ProtocolUtils.readVarInt(buf);
}
}
}
}
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (locale == null) {
throw new IllegalStateException("No locale specified");
}
ProtocolUtils.writeString(buf, locale);
buf.writeByte(viewDistance);
ProtocolUtils.writeVarInt(buf, chatVisibility);
buf.writeBoolean(chatColors);
if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_7_6)) {
buf.writeByte(difficulty);
}
buf.writeByte(skinParts);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9)) {
ProtocolUtils.writeVarInt(buf, mainHand);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) {
buf.writeBoolean(textFilteringEnabled);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) {
buf.writeBoolean(clientListingAllowed);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
ProtocolUtils.writeVarInt(buf, particleStatus);
}
}
}
public String getLocale() {
return locale();
}
@Override
@@ -227,18 +140,79 @@ public class ClientSettingsPacket implements MinecraftPacket {
&& Objects.equals(locale, that.locale);
}
public static class Codec implements PacketCodec<ClientSettingsPacket> {
@Override
public int hashCode() {
return Objects.hash(
locale,
viewDistance,
chatVisibility,
chatColors,
difficulty,
skinParts,
mainHand,
textFilteringEnabled,
clientListingAllowed,
public ClientSettingsPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
String locale = ProtocolUtils.readString(buf, 16);
byte viewDistance = buf.readByte();
int chatVisibility = ProtocolUtils.readVarInt(buf);
boolean chatColors = buf.readBoolean();
byte difficulty = 0;
if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_7_6)) {
difficulty = buf.readByte();
}
short skinParts = buf.readUnsignedByte();
int mainHand = 0;
boolean textFilteringEnabled = false;
boolean clientListingAllowed = false;
int particleStatus = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9)) {
mainHand = ProtocolUtils.readVarInt(buf);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) {
textFilteringEnabled = buf.readBoolean();
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) {
clientListingAllowed = buf.readBoolean();
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
particleStatus = ProtocolUtils.readVarInt(buf);
}
}
}
}
return new ClientSettingsPacket(locale, viewDistance, chatVisibility, chatColors,
difficulty, skinParts, mainHand, textFilteringEnabled, clientListingAllowed,
particleStatus);
}
@Override
public void encode(ClientSettingsPacket packet, ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
if (packet.locale == null) {
throw new IllegalStateException("No locale specified");
}
ProtocolUtils.writeString(buf, packet.locale);
buf.writeByte(packet.viewDistance);
ProtocolUtils.writeVarInt(buf, packet.chatVisibility);
buf.writeBoolean(packet.chatColors);
if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_7_6)) {
buf.writeByte(packet.difficulty);
}
buf.writeByte(packet.skinParts);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9)) {
ProtocolUtils.writeVarInt(buf, packet.mainHand);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) {
buf.writeBoolean(packet.textFilteringEnabled);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) {
buf.writeBoolean(packet.clientListingAllowed);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
ProtocolUtils.writeVarInt(buf, packet.particleStatus);
}
}
}
}
}
}

View File

@@ -20,38 +20,34 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf;
import net.kyori.adventure.key.Key;
public class ClientboundCookieRequestPacket implements MinecraftPacket {
private Key key;
public record ClientboundCookieRequestPacket(Key key) implements MinecraftPacket {
public Key getKey() {
return key;
}
public ClientboundCookieRequestPacket() {
}
public ClientboundCookieRequestPacket(final Key key) {
this.key = key;
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
this.key = ProtocolUtils.readKey(buf);
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeKey(buf, key);
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import java.util.concurrent.ThreadLocalRandom;
import net.kyori.adventure.sound.Sound;
import org.jetbrains.annotations.Nullable;
import java.util.Random;
public class ClientboundSoundEntityPacket implements MinecraftPacket {
private static final Random SEEDS_RANDOM = new Random();
private Sound sound;
private @Nullable Float fixedRange;
private int emitterEntityId;
public ClientboundSoundEntityPacket() {}
public ClientboundSoundEntityPacket(Sound sound, @Nullable Float fixedRange, int emitterEntityId) {
this.sound = sound;
this.fixedRange = fixedRange;
this.emitterEntityId = emitterEntityId;
}
@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()));
}
public record ClientboundSoundEntityPacket(Sound sound, @Nullable Float fixedRange,
int emitterEntityId) implements MinecraftPacket {
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public Sound getSound() {
return sound;
public static class Codec implements PacketCodec<ClientboundSoundEntityPacket> {
@Override
public ClientboundSoundEntityPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
throw new UnsupportedOperationException("Decode is not implemented");
}
public void setSound(Sound sound) {
this.sound = sound;
@Override
public void encode(ClientboundSoundEntityPacket packet, ByteBuf buf,
ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, 0); // version-dependent, hardcoded sound ID
ProtocolUtils.writeMinimalKey(buf, packet.sound.name());
buf.writeBoolean(packet.fixedRange != null);
if (packet.fixedRange != null) {
buf.writeFloat(packet.fixedRange);
}
public @Nullable Float getFixedRange() {
return fixedRange;
}
ProtocolUtils.writeSoundSource(buf, protocolVersion, packet.sound.source());
public void setFixedRange(@Nullable Float fixedRange) {
this.fixedRange = fixedRange;
}
ProtocolUtils.writeVarInt(buf, packet.emitterEntityId);
public int getEmitterEntityId() {
return emitterEntityId;
}
buf.writeFloat(packet.sound.volume());
public void setEmitterEntityId(int emitterEntityId) {
this.emitterEntityId = emitterEntityId;
}
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import net.kyori.adventure.key.Key;
@@ -28,82 +29,58 @@ import net.kyori.adventure.sound.SoundStop;
import javax.annotation.Nullable;
public class ClientboundStopSoundPacket implements MinecraftPacket {
private @Nullable Sound.Source source;
private @Nullable Key soundName;
public ClientboundStopSoundPacket() {}
public record ClientboundStopSoundPacket(@Nullable Sound.Source source,
@Nullable Key soundName) implements MinecraftPacket {
public ClientboundStopSoundPacket(SoundStop soundStop) {
this(soundStop.source(), soundStop.sound());
}
public ClientboundStopSoundPacket(@Nullable Sound.Source source, @Nullable Key soundName) {
this.source = source;
this.soundName = soundName;
}
@Override
public 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
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
@Nullable
public Sound.Source getSource() {
return source;
public static class Codec implements PacketCodec<ClientboundStopSoundPacket> {
@Override
public ClientboundStopSoundPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
int flagsBitmask = buf.readByte();
Sound.Source source = null;
if ((flagsBitmask & 1) != 0) {
source = ProtocolUtils.readSoundSource(buf, protocolVersion);
}
public void setSource(@Nullable Sound.Source source) {
this.source = source;
Key soundName = null;
if ((flagsBitmask & 2) != 0) {
soundName = ProtocolUtils.readKey(buf);
}
@Nullable
public Key getSoundName() {
return soundName;
return new ClientboundStopSoundPacket(source, soundName);
}
public void setSoundName(@Nullable Key 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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf;
import net.kyori.adventure.key.Key;
public class ClientboundStoreCookiePacket implements MinecraftPacket {
private Key key;
private byte[] payload;
public Key getKey() {
return key;
}
public byte[] getPayload() {
return payload;
}
public ClientboundStoreCookiePacket() {
}
public ClientboundStoreCookiePacket(final Key key, final byte[] payload) {
this.key = key;
this.payload = payload;
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
this.key = ProtocolUtils.readKey(buf);
this.payload = ProtocolUtils.readByteArray(buf, 5120);
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeKey(buf, key);
ProtocolUtils.writeByteArray(buf, payload);
}
public record ClientboundStoreCookiePacket(Key key, byte[] payload) implements MinecraftPacket {
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class DialogClearPacket implements MinecraftPacket {
public final class DialogClearPacket implements MinecraftPacket {
public static final DialogClearPacket INSTANCE = new DialogClearPacket();
private DialogClearPacket() {
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.StateRegistry;
import io.netty.buffer.ByteBuf;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.BinaryTagIO;
public class DialogShowPacket implements MinecraftPacket {
public final class DialogShowPacket implements MinecraftPacket {
private final StateRegistry state;
private int id;
private BinaryTag nbt;
private final int id;
private final BinaryTag nbt;
public DialogShowPacket(final StateRegistry state) {
public DialogShowPacket(StateRegistry state, int id, BinaryTag nbt) {
this.state = state;
this.id = id;
this.nbt = nbt;
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
this.id = this.state == StateRegistry.CONFIG ? 0 : ProtocolUtils.readVarInt(buf);
if (this.id == 0) {
this.nbt = ProtocolUtils.readBinaryTag(buf, protocolVersion, BinaryTagIO.reader());
}
public StateRegistry state() {
return state;
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
if (this.state == StateRegistry.CONFIG) {
ProtocolUtils.writeBinaryTag(buf, protocolVersion, this.nbt);
} else {
ProtocolUtils.writeVarInt(buf, this.id);
if (this.id == 0) {
ProtocolUtils.writeBinaryTag(buf, protocolVersion, this.nbt);
}
public int id() {
return id;
}
public BinaryTag nbt() {
return nbt;
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
public class DisconnectPacket implements MinecraftPacket {
public final class DisconnectPacket implements MinecraftPacket {
private @Nullable ComponentHolder reason;
private final ComponentHolder reason;
private final StateRegistry state;
public DisconnectPacket(StateRegistry state) {
this.state = state;
}
private DisconnectPacket(StateRegistry state, ComponentHolder reason) {
public DisconnectPacket(StateRegistry state, ComponentHolder reason) {
this.state = state;
this.reason = Preconditions.checkNotNull(reason, "reason");
}
public ComponentHolder getReason() {
public ComponentHolder reason() {
if (reason == null) {
throw new IllegalStateException("No reason specified");
}
return reason;
}
public void setReason(@Nullable ComponentHolder reason) {
this.reason = reason;
public ComponentHolder getReason() {
return reason();
}
public StateRegistry state() {
return state;
}
@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
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static DisconnectPacket create(Component component, ProtocolVersion version, StateRegistry state) {
public static DisconnectPacket create(Component component, ProtocolVersion version,
StateRegistry state) {
Preconditions.checkNotNull(component, "component");
return new DisconnectPacket(state, new ComponentHolder(state == StateRegistry.LOGIN
? ProtocolVersion.MINECRAFT_1_20_2 : version, component));
}
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;
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import java.util.Arrays;
public class EncryptionRequestPacket implements MinecraftPacket {
public final class EncryptionRequestPacket implements MinecraftPacket {
private String serverId = "";
private byte[] publicKey = EMPTY_BYTE_ARRAY;
private byte[] verifyToken = EMPTY_BYTE_ARRAY;
private boolean shouldAuthenticate = true;
private final String serverId;
private final byte[] publicKey;
private final byte[] verifyToken;
private final boolean shouldAuthenticate;
public byte[] getPublicKey() {
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();
}
public void setPublicKey(byte[] publicKey) {
this.publicKey = publicKey.clone();
}
public byte[] getVerifyToken() {
public byte[] verifyToken() {
return verifyToken.clone();
}
public void setVerifyToken(byte[] verifyToken) {
this.verifyToken = verifyToken.clone();
public boolean shouldAuthenticate() {
return shouldAuthenticate;
}
public byte[] getPublicKey() {
return publicKey();
}
public byte[] getVerifyToken() {
return verifyToken();
}
@Override
@@ -58,8 +74,18 @@ public class EncryptionRequestPacket implements MinecraftPacket {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
this.serverId = ProtocolUtils.readString(buf, 20);
public boolean handle(MinecraftSessionHandler handler) {
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);
@@ -71,26 +97,25 @@ public class EncryptionRequestPacket implements MinecraftPacket {
publicKey = ProtocolUtils.readByteArray17(buf);
verifyToken = ProtocolUtils.readByteArray17(buf);
}
return new EncryptionRequestPacket(serverId, publicKey, verifyToken, shouldAuthenticate);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
ProtocolUtils.writeString(buf, this.serverId);
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, publicKey);
ProtocolUtils.writeByteArray(buf, verifyToken);
ProtocolUtils.writeByteArray(buf, packet.publicKey);
ProtocolUtils.writeByteArray(buf, packet.verifyToken);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
buf.writeBoolean(shouldAuthenticate);
buf.writeBoolean(packet.shouldAuthenticate);
}
} else {
ProtocolUtils.writeByteArray17(publicKey, buf, false);
ProtocolUtils.writeByteArray17(verifyToken, buf, false);
ProtocolUtils.writeByteArray17(packet.publicKey, buf, false);
ProtocolUtils.writeByteArray17(packet.verifyToken, buf, false);
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

View File

@@ -17,43 +17,60 @@
package com.velocitypowered.proxy.protocol.packet;
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Arrays;
public class EncryptionResponsePacket implements MinecraftPacket {
public final class EncryptionResponsePacket implements MinecraftPacket {
private static final QuietDecoderException NO_SALT = new QuietDecoderException(
"Encryption response didn't contain salt");
private byte[] sharedSecret = EMPTY_BYTE_ARRAY;
private byte[] verifyToken = EMPTY_BYTE_ARRAY;
private @Nullable Long salt;
private final byte[] sharedSecret;
private final byte[] verifyToken;
private final @Nullable Long salt;
public byte[] getSharedSecret() {
public EncryptionResponsePacket(byte[] sharedSecret, byte[] verifyToken, @Nullable Long salt) {
this.sharedSecret = sharedSecret.clone();
this.verifyToken = verifyToken.clone();
this.salt = salt;
}
public byte[] sharedSecret() {
return sharedSecret.clone();
}
public byte[] getVerifyToken() {
public byte[] verifyToken() {
return verifyToken.clone();
}
public long getSalt() {
public long salt() {
if (salt == null) {
throw NO_SALT;
}
return salt;
}
public byte[] getSharedSecret() {
return sharedSecret();
}
public byte[] getVerifyToken() {
return verifyToken();
}
public long getSalt() {
return salt();
}
@Override
public String toString() {
return "EncryptionResponse{"
@@ -63,9 +80,20 @@ public class EncryptionResponsePacket implements MinecraftPacket {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<EncryptionResponsePacket> {
@Override
public EncryptionResponsePacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
byte[] sharedSecret;
byte[] verifyToken;
Long salt = null;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
this.sharedSecret = ProtocolUtils.readByteArray(buf, 128);
sharedSecret = ProtocolUtils.readByteArray(buf, 128);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)
&& version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)
@@ -73,41 +101,40 @@ public class EncryptionResponsePacket implements MinecraftPacket {
salt = buf.readLong();
}
this.verifyToken = ProtocolUtils.readByteArray(buf,
verifyToken = ProtocolUtils.readByteArray(buf,
version.noLessThan(ProtocolVersion.MINECRAFT_1_19) ? 256 : 128);
} else {
this.sharedSecret = ProtocolUtils.readByteArray17(buf);
this.verifyToken = ProtocolUtils.readByteArray17(buf);
sharedSecret = ProtocolUtils.readByteArray17(buf);
verifyToken = ProtocolUtils.readByteArray17(buf);
}
return new EncryptionResponsePacket(sharedSecret, verifyToken, salt);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
public void encode(EncryptionResponsePacket packet, ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
ProtocolUtils.writeByteArray(buf, sharedSecret);
ProtocolUtils.writeByteArray(buf, packet.sharedSecret);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)
&& version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
if (salt != null) {
if (packet.salt != null) {
buf.writeBoolean(false);
buf.writeLong(salt);
buf.writeLong(packet.salt);
} else {
buf.writeBoolean(true);
}
}
ProtocolUtils.writeByteArray(buf, verifyToken);
ProtocolUtils.writeByteArray(buf, packet.verifyToken);
} else {
ProtocolUtils.writeByteArray17(sharedSecret, buf, false);
ProtocolUtils.writeByteArray17(verifyToken, buf, false);
ProtocolUtils.writeByteArray17(packet.sharedSecret, buf, false);
ProtocolUtils.writeByteArray17(packet.verifyToken, buf, false);
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
@Override
public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) {
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;
@@ -123,7 +150,8 @@ public class EncryptionResponsePacket implements MinecraftPacket {
}
@Override
public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) {
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"
@@ -131,4 +159,5 @@ public class EncryptionResponsePacket implements MinecraftPacket {
}
return base;
}
}
}

View File

@@ -23,58 +23,71 @@ import com.velocitypowered.api.network.HandshakeIntent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf;
public class HandshakePacket implements MinecraftPacket {
public final class HandshakePacket implements MinecraftPacket {
// This size was chosen to ensure Forge clients can still connect even with very long hostnames.
// While DNS technically allows any character to be used, in practice ASCII is used.
private static final int MAXIMUM_HOSTNAME_LENGTH = 255 + HANDSHAKE_HOSTNAME_TOKEN.length() + 1;
private ProtocolVersion protocolVersion;
private String serverAddress = "";
private int port;
private HandshakeIntent intent;
private int nextStatus;
public ProtocolVersion getProtocolVersion() {
return protocolVersion;
private final ProtocolVersion 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;
}
public String getServerAddress() {
return serverAddress;
}
public void setServerAddress(String serverAddress) {
this.serverAddress = serverAddress;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getNextStatus() {
return this.nextStatus;
}
public void setIntent(HandshakeIntent intent) {
this.intent = intent;
this.nextStatus = intent.id();
}
public 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;
}
public ProtocolVersion getProtocolVersion() {
return protocolVersion();
}
public String getServerAddress() {
return serverAddress();
}
public int getPort() {
return port();
}
public HandshakeIntent getIntent() {
return intent();
}
@Override
public String toString() {
return "Handshake{"
@@ -85,29 +98,33 @@ 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
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<HandshakePacket> {
@Override
public HandshakePacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion ignored) {
int realProtocolVersion = ProtocolUtils.readVarInt(buf);
ProtocolVersion protocolVersion = ProtocolVersion.getProtocolVersion(realProtocolVersion);
String serverAddress = ProtocolUtils.readString(buf, MAXIMUM_HOSTNAME_LENGTH);
int port = buf.readUnsignedShort();
int nextStatus = ProtocolUtils.readVarInt(buf);
HandshakeIntent intent = HandshakeIntent.getById(nextStatus);
return new HandshakePacket(protocolVersion, serverAddress, port, intent);
}
@Override
public void encode(HandshakePacket packet, ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion ignored) {
ProtocolUtils.writeVarInt(buf, packet.protocolVersion.getProtocol());
ProtocolUtils.writeString(buf, packet.serverAddress);
buf.writeShort(packet.port);
ProtocolUtils.writeVarInt(buf, packet.nextStatus);
}
@Override
public int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
@@ -121,9 +138,10 @@ public class HandshakePacket implements MinecraftPacket {
}
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf;
import net.kyori.adventure.text.Component;
public class HeaderAndFooterPacket implements MinecraftPacket {
public record HeaderAndFooterPacket(ComponentHolder header,
ComponentHolder footer) implements MinecraftPacket {
private final ComponentHolder header;
private final ComponentHolder footer;
public HeaderAndFooterPacket() {
throw new UnsupportedOperationException("Decode is not implemented");
}
public HeaderAndFooterPacket(ComponentHolder header, ComponentHolder footer) {
this.header = Preconditions.checkNotNull(header, "header");
this.footer = Preconditions.checkNotNull(footer, "footer");
}
public ComponentHolder getHeader() {
return header;
}
public ComponentHolder getFooter() {
return footer;
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
throw new UnsupportedOperationException("Decode is not implemented");
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
header.write(buf);
footer.write(buf);
public HeaderAndFooterPacket {
Preconditions.checkNotNull(header, "header");
Preconditions.checkNotNull(footer, "footer");
}
@Override
@@ -74,4 +50,19 @@ public class HeaderAndFooterPacket implements MinecraftPacket {
ComponentHolder empty = new ComponentHolder(version, Component.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.registry.DimensionInfo;
import com.velocitypowered.proxy.protocol.*;
import com.velocitypowered.proxy.protocol.PacketCodec;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.Pair;
import net.kyori.adventure.nbt.BinaryTagIO;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import org.checkerframework.checker.nullness.qual.Nullable;
public class JoinGamePacket implements MinecraftPacket {
public final class JoinGamePacket implements MinecraftPacket {
private static final BinaryTagIO.Reader JOINGAME_READER = BinaryTagIO.reader(4 * 1024 * 1024);
private int entityId;
private short gamemode;
private int dimension;
private long partialHashedSeed; // 1.15+
private short difficulty;
private boolean isHardcore;
private int maxPlayers;
private @Nullable String levelType;
private int viewDistance; // 1.14+
private boolean reducedDebugInfo;
private boolean showRespawnScreen;
private boolean doLimitedCrafting; // 1.20.2+
private ImmutableSet<String> levelNames; // 1.16+
private CompoundBinaryTag registry; // 1.16+
private DimensionInfo dimensionInfo; // 1.16+
private CompoundBinaryTag currentDimensionData; // 1.16.2+
private short previousGamemode; // 1.16+
private int simulationDistance; // 1.18+
private @Nullable Pair<String, Long> lastDeathPosition; // 1.19+
private int portalCooldown; // 1.20+
private int seaLevel; // 1.21.2+
private boolean enforcesSecureChat; // 1.20.5+
private final int entityId;
private final short gamemode;
private final int dimension;
private final long partialHashedSeed;
private final short difficulty;
private final boolean isHardcore;
private final int maxPlayers;
private final @Nullable String levelType;
private final int viewDistance;
private final boolean reducedDebugInfo;
private final boolean showRespawnScreen;
private final boolean doLimitedCrafting;
private final ImmutableSet<String> levelNames;
private final CompoundBinaryTag registry;
private final DimensionInfo dimensionInfo;
private final CompoundBinaryTag currentDimensionData;
private final short previousGamemode;
private final int simulationDistance;
private final @Nullable Pair<String, Long> lastDeathPosition;
private final int portalCooldown;
private final int seaLevel;
private final boolean enforcesSecureChat;
public JoinGamePacket(int entityId, short gamemode, int dimension, long partialHashedSeed,
short difficulty, boolean isHardcore, int maxPlayers, @Nullable String levelType,
int viewDistance, boolean reducedDebugInfo, boolean showRespawnScreen,
boolean doLimitedCrafting, ImmutableSet<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() {
return entityId;
}
public void setEntityId(int entityId) {
this.entityId = entityId;
}
public short getGamemode() {
return gamemode;
}
public void setGamemode(short gamemode) {
this.gamemode = gamemode;
}
public int getDimension() {
return dimension;
}
public void setDimension(int dimension) {
this.dimension = dimension;
}
public long getPartialHashedSeed() {
return partialHashedSeed;
}
@@ -86,74 +107,38 @@ public class JoinGamePacket implements MinecraftPacket {
return difficulty;
}
public void setDifficulty(short difficulty) {
this.difficulty = difficulty;
}
public int getMaxPlayers() {
return maxPlayers;
}
public void setMaxPlayers(int maxPlayers) {
this.maxPlayers = maxPlayers;
}
public @Nullable String getLevelType() {
return levelType;
}
public void setLevelType(@Nullable String levelType) {
this.levelType = levelType;
}
public int getViewDistance() {
return viewDistance;
}
public void setViewDistance(int viewDistance) {
this.viewDistance = viewDistance;
}
public boolean isReducedDebugInfo() {
return reducedDebugInfo;
}
public void setReducedDebugInfo(boolean reducedDebugInfo) {
this.reducedDebugInfo = reducedDebugInfo;
}
public DimensionInfo getDimensionInfo() {
return dimensionInfo;
}
public void setDimensionInfo(DimensionInfo dimensionInfo) {
this.dimensionInfo = dimensionInfo;
}
public short getPreviousGamemode() {
return previousGamemode;
}
public void setPreviousGamemode(short previousGamemode) {
this.previousGamemode = previousGamemode;
}
public boolean getIsHardcore() {
return isHardcore;
}
public void setIsHardcore(boolean isHardcore) {
this.isHardcore = isHardcore;
}
public boolean getDoLimitedCrafting() {
return doLimitedCrafting;
}
public void setDoLimitedCrafting(boolean doLimitedCrafting) {
this.doLimitedCrafting = doLimitedCrafting;
}
public CompoundBinaryTag getCurrentDimensionData() {
return currentDimensionData;
}
@@ -162,46 +147,34 @@ public class JoinGamePacket implements MinecraftPacket {
return simulationDistance;
}
public void setSimulationDistance(int simulationDistance) {
this.simulationDistance = simulationDistance;
}
public Pair<String, Long> getLastDeathPosition() {
return lastDeathPosition;
}
public void setLastDeathPosition(Pair<String, Long> lastDeathPosition) {
this.lastDeathPosition = lastDeathPosition;
}
public int getPortalCooldown() {
return portalCooldown;
}
public void setPortalCooldown(int portalCooldown) {
this.portalCooldown = portalCooldown;
}
public int getSeaLevel() {
return seaLevel;
}
public void setSeaLevel(int seaLevel) {
this.seaLevel = seaLevel;
}
public boolean getEnforcesSecureChat() {
return this.enforcesSecureChat;
}
public void setEnforcesSecureChat(final boolean enforcesSecureChat) {
this.enforcesSecureChat = enforcesSecureChat;
}
public CompoundBinaryTag getRegistry() {
return registry;
}
public boolean getShowRespawnScreen() {
return showRespawnScreen;
}
public ImmutableSet<String> getLevelNames() {
return levelNames;
}
@Override
public String toString() {
return "JoinGame{" + "entityId=" + entityId + ", gamemode=" + gamemode + ", dimension=" +
@@ -218,119 +191,173 @@ public class JoinGamePacket implements MinecraftPacket {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
public boolean handle(MinecraftSessionHandler handler) {
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
this.decode1202Up(buf, version);
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.
this.decode116Up(buf, version);
return decode116Up(buf, version);
} else {
this.decodeLegacy(buf, version);
return 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;
@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)) {
this.dimension = buf.readInt();
dimension = buf.readInt();
} else {
this.dimension = buf.readByte();
dimension = buf.readByte();
}
short difficulty = 0;
if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) {
this.difficulty = buf.readUnsignedByte();
difficulty = buf.readUnsignedByte();
}
long partialHashedSeed = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) {
this.partialHashedSeed = buf.readLong();
partialHashedSeed = buf.readLong();
}
this.maxPlayers = buf.readUnsignedByte();
this.levelType = ProtocolUtils.readString(buf, 16);
int maxPlayers = buf.readUnsignedByte();
String levelType = ProtocolUtils.readString(buf, 16);
int viewDistance = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_14)) {
this.viewDistance = ProtocolUtils.readVarInt(buf);
viewDistance = ProtocolUtils.readVarInt(buf);
}
boolean reducedDebugInfo = false;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
this.reducedDebugInfo = buf.readBoolean();
reducedDebugInfo = buf.readBoolean();
}
boolean showRespawnScreen = true;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) {
this.showRespawnScreen = buf.readBoolean();
}
showRespawnScreen = buf.readBoolean();
}
private void decode116Up(ByteBuf buf, ProtocolVersion version) {
this.entityId = buf.readInt();
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)) {
this.isHardcore = buf.readBoolean();
this.gamemode = buf.readByte();
isHardcore = buf.readBoolean();
gamemode = buf.readByte();
} else {
this.gamemode = buf.readByte();
this.isHardcore = (this.gamemode & 0x08) != 0;
this.gamemode &= ~0x08;
gamemode = buf.readByte();
isHardcore = (gamemode & 0x08) != 0;
gamemode &= ~0x08;
}
this.previousGamemode = buf.readByte();
short previousGamemode = buf.readByte();
ImmutableSet<String> levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf));
CompoundBinaryTag registry = ProtocolUtils.readCompoundTag(buf, version, JOINGAME_READER);
this.levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf));
this.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)) {
this.currentDimensionData = ProtocolUtils.readCompoundTag(buf, version, JOINGAME_READER);
currentDimensionData = ProtocolUtils.readCompoundTag(buf, version, JOINGAME_READER);
dimensionIdentifier = ProtocolUtils.readString(buf);
} else {
dimensionIdentifier = ProtocolUtils.readString(buf);
levelName = ProtocolUtils.readString(buf);
}
this.partialHashedSeed = buf.readLong();
long partialHashedSeed = buf.readLong();
int maxPlayers;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) {
this.maxPlayers = ProtocolUtils.readVarInt(buf);
maxPlayers = ProtocolUtils.readVarInt(buf);
} else {
this.maxPlayers = buf.readUnsignedByte();
maxPlayers = buf.readUnsignedByte();
}
this.viewDistance = ProtocolUtils.readVarInt(buf);
int viewDistance = ProtocolUtils.readVarInt(buf);
int simulationDistance = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) {
this.simulationDistance = ProtocolUtils.readVarInt(buf);
simulationDistance = ProtocolUtils.readVarInt(buf);
}
this.reducedDebugInfo = buf.readBoolean();
this.showRespawnScreen = buf.readBoolean();
boolean reducedDebugInfo = buf.readBoolean();
boolean showRespawnScreen = buf.readBoolean();
boolean isDebug = buf.readBoolean();
boolean isFlat = buf.readBoolean();
this.dimensionInfo = new DimensionInfo(dimensionIdentifier, levelName, isFlat, isDebug, version);
DimensionInfo dimensionInfo = new DimensionInfo(dimensionIdentifier, levelName, isFlat, isDebug, version);
// optional death location
Pair<String, Long> lastDeathPosition = null;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19) && buf.readBoolean()) {
this.lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong());
lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong());
}
int portalCooldown = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) {
this.portalCooldown = ProtocolUtils.readVarInt(buf);
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 void decode1202Up(ByteBuf buf, ProtocolVersion version) {
this.entityId = buf.readInt();
this.isHardcore = buf.readBoolean();
private static JoinGamePacket decode1202Up(ByteBuf buf, ProtocolVersion version) {
int entityId = buf.readInt();
boolean isHardcore = buf.readBoolean();
this.levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf));
ImmutableSet<String> levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf));
this.maxPlayers = ProtocolUtils.readVarInt(buf);
int maxPlayers = ProtocolUtils.readVarInt(buf);
this.viewDistance = ProtocolUtils.readVarInt(buf);
this.simulationDistance = ProtocolUtils.readVarInt(buf);
int viewDistance = ProtocolUtils.readVarInt(buf);
int simulationDistance = ProtocolUtils.readVarInt(buf);
this.reducedDebugInfo = buf.readBoolean();
this.showRespawnScreen = buf.readBoolean();
this.doLimitedCrafting = buf.readBoolean();
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);
@@ -338,185 +365,174 @@ public class JoinGamePacket implements MinecraftPacket {
dimensionKey = ProtocolUtils.readString(buf);
}
String levelName = ProtocolUtils.readString(buf);
this.partialHashedSeed = buf.readLong();
long partialHashedSeed = buf.readLong();
this.gamemode = buf.readByte();
this.previousGamemode = buf.readByte();
short gamemode = buf.readByte();
short previousGamemode = buf.readByte();
boolean isDebug = buf.readBoolean();
boolean isFlat = buf.readBoolean();
this.dimensionInfo = new DimensionInfo(dimensionKey, levelName, isFlat, isDebug, version);
DimensionInfo dimensionInfo = new DimensionInfo(dimensionKey, levelName, isFlat, isDebug, version);
// optional death location
Pair<String, Long> lastDeathPosition = null;
if (buf.readBoolean()) {
this.lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong());
lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong());
}
this.portalCooldown = ProtocolUtils.readVarInt(buf);
int portalCooldown = ProtocolUtils.readVarInt(buf);
int seaLevel = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
this.seaLevel = ProtocolUtils.readVarInt(buf);
seaLevel = ProtocolUtils.readVarInt(buf);
}
boolean enforcesSecureChat = false;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
this.enforcesSecureChat = buf.readBoolean();
}
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);
}
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 void encodeLegacy(ByteBuf buf, ProtocolVersion version) {
buf.writeInt(entityId);
private static void encodeLegacy(JoinGamePacket packet, ByteBuf buf, ProtocolVersion version) {
buf.writeInt(packet.entityId);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) {
buf.writeBoolean(isHardcore);
buf.writeByte(gamemode);
buf.writeBoolean(packet.isHardcore);
buf.writeByte(packet.gamemode);
} else {
buf.writeByte(isHardcore ? gamemode | 0x8 : gamemode);
buf.writeByte(packet.isHardcore ? packet.gamemode | 0x8 : packet.gamemode);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_9_1)) {
buf.writeInt(dimension);
buf.writeInt(packet.dimension);
} else {
buf.writeByte(dimension);
buf.writeByte(packet.dimension);
}
if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) {
buf.writeByte(difficulty);
buf.writeByte(packet.difficulty);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) {
buf.writeLong(partialHashedSeed);
buf.writeLong(packet.partialHashedSeed);
}
buf.writeByte(maxPlayers);
if (levelType == null) {
buf.writeByte(packet.maxPlayers);
if (packet.levelType == null) {
throw new IllegalStateException("No level type specified.");
}
ProtocolUtils.writeString(buf, levelType);
ProtocolUtils.writeString(buf, packet.levelType);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_14)) {
ProtocolUtils.writeVarInt(buf, viewDistance);
ProtocolUtils.writeVarInt(buf, packet.viewDistance);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
buf.writeBoolean(reducedDebugInfo);
buf.writeBoolean(packet.reducedDebugInfo);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) {
buf.writeBoolean(showRespawnScreen);
buf.writeBoolean(packet.showRespawnScreen);
}
}
private void encode116Up(ByteBuf buf, ProtocolVersion version) {
buf.writeInt(entityId);
private static void encode116Up(JoinGamePacket packet, ByteBuf buf, ProtocolVersion version) {
buf.writeInt(packet.entityId);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) {
buf.writeBoolean(isHardcore);
buf.writeByte(gamemode);
buf.writeBoolean(packet.isHardcore);
buf.writeByte(packet.gamemode);
} else {
buf.writeByte(isHardcore ? gamemode | 0x8 : gamemode);
buf.writeByte(packet.isHardcore ? packet.gamemode | 0x8 : packet.gamemode);
}
buf.writeByte(previousGamemode);
buf.writeByte(packet.previousGamemode);
ProtocolUtils.writeStringArray(buf, levelNames.toArray(String[]::new));
ProtocolUtils.writeBinaryTag(buf, version, this.registry);
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, currentDimensionData);
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier());
ProtocolUtils.writeBinaryTag(buf, version, packet.currentDimensionData);
ProtocolUtils.writeString(buf, packet.dimensionInfo.getRegistryIdentifier());
} else {
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier());
ProtocolUtils.writeString(buf, dimensionInfo.getLevelName());
ProtocolUtils.writeString(buf, packet.dimensionInfo.getRegistryIdentifier());
ProtocolUtils.writeString(buf, packet.dimensionInfo.getLevelName());
}
buf.writeLong(partialHashedSeed);
buf.writeLong(packet.partialHashedSeed);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16_2)) {
ProtocolUtils.writeVarInt(buf, maxPlayers);
ProtocolUtils.writeVarInt(buf, packet.maxPlayers);
} else {
buf.writeByte(maxPlayers);
buf.writeByte(packet.maxPlayers);
}
ProtocolUtils.writeVarInt(buf, viewDistance);
ProtocolUtils.writeVarInt(buf, packet.viewDistance);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) {
ProtocolUtils.writeVarInt(buf, simulationDistance);
ProtocolUtils.writeVarInt(buf, packet.simulationDistance);
}
buf.writeBoolean(reducedDebugInfo);
buf.writeBoolean(showRespawnScreen);
buf.writeBoolean(packet.reducedDebugInfo);
buf.writeBoolean(packet.showRespawnScreen);
buf.writeBoolean(dimensionInfo.isDebugType());
buf.writeBoolean(dimensionInfo.isFlat());
buf.writeBoolean(packet.dimensionInfo.isDebugType());
buf.writeBoolean(packet.dimensionInfo.isFlat());
// optional death location
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) {
if (lastDeathPosition != null) {
if (packet.lastDeathPosition != null) {
buf.writeBoolean(true);
ProtocolUtils.writeString(buf, lastDeathPosition.key());
buf.writeLong(lastDeathPosition.value());
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, portalCooldown);
ProtocolUtils.writeVarInt(buf, packet.portalCooldown);
}
}
private void encode1202Up(ByteBuf buf, ProtocolVersion version) {
buf.writeInt(entityId);
buf.writeBoolean(isHardcore);
private static void encode1202Up(JoinGamePacket packet, ByteBuf buf, ProtocolVersion version) {
buf.writeInt(packet.entityId);
buf.writeBoolean(packet.isHardcore);
ProtocolUtils.writeStringArray(buf, levelNames.toArray(String[]::new));
ProtocolUtils.writeStringArray(buf, packet.levelNames.toArray(String[]::new));
ProtocolUtils.writeVarInt(buf, maxPlayers);
ProtocolUtils.writeVarInt(buf, packet.maxPlayers);
ProtocolUtils.writeVarInt(buf, viewDistance);
ProtocolUtils.writeVarInt(buf, simulationDistance);
ProtocolUtils.writeVarInt(buf, packet.viewDistance);
ProtocolUtils.writeVarInt(buf, packet.simulationDistance);
buf.writeBoolean(reducedDebugInfo);
buf.writeBoolean(showRespawnScreen);
buf.writeBoolean(doLimitedCrafting);
buf.writeBoolean(packet.reducedDebugInfo);
buf.writeBoolean(packet.showRespawnScreen);
buf.writeBoolean(packet.doLimitedCrafting);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
ProtocolUtils.writeVarInt(buf, dimension);
ProtocolUtils.writeVarInt(buf, packet.dimension);
} else {
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier());
ProtocolUtils.writeString(buf, packet.dimensionInfo.getRegistryIdentifier());
}
ProtocolUtils.writeString(buf, dimensionInfo.getLevelName());
buf.writeLong(partialHashedSeed);
ProtocolUtils.writeString(buf, packet.dimensionInfo.getLevelName());
buf.writeLong(packet.partialHashedSeed);
buf.writeByte(gamemode);
buf.writeByte(previousGamemode);
buf.writeByte(packet.gamemode);
buf.writeByte(packet.previousGamemode);
buf.writeBoolean(dimensionInfo.isDebugType());
buf.writeBoolean(dimensionInfo.isFlat());
buf.writeBoolean(packet.dimensionInfo.isDebugType());
buf.writeBoolean(packet.dimensionInfo.isFlat());
// optional death location
if (lastDeathPosition != null) {
if (packet.lastDeathPosition != null) {
buf.writeBoolean(true);
ProtocolUtils.writeString(buf, lastDeathPosition.key());
buf.writeLong(lastDeathPosition.value());
ProtocolUtils.writeString(buf, packet.lastDeathPosition.key());
buf.writeLong(packet.lastDeathPosition.value());
} else {
buf.writeBoolean(false);
}
ProtocolUtils.writeVarInt(buf, portalCooldown);
ProtocolUtils.writeVarInt(buf, packet.portalCooldown);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
ProtocolUtils.writeVarInt(buf, seaLevel);
ProtocolUtils.writeVarInt(buf, packet.seaLevel);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
buf.writeBoolean(this.enforcesSecureChat);
buf.writeBoolean(packet.enforcesSecureChat);
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

View File

@@ -20,21 +20,16 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class KeepAlivePacket implements MinecraftPacket {
private long randomId;
public record KeepAlivePacket(long randomId) implements MinecraftPacket {
public long getRandomId() {
return randomId;
}
public void setRandomId(long randomId) {
this.randomId = randomId;
}
@Override
public String toString() {
return "KeepAlive{"
@@ -43,29 +38,35 @@ public class KeepAlivePacket implements MinecraftPacket {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_12_2)) {
public boolean handle(MinecraftSessionHandler handler) {
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 (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
} else if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
randomId = ProtocolUtils.readVarInt(buf);
} else {
randomId = buf.readInt();
}
return new KeepAlivePacket(randomId);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_12_2)) {
buf.writeLong(randomId);
} else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
ProtocolUtils.writeVarInt(buf, (int) randomId);
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) randomId);
buf.writeInt((int) packet.randomId);
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

View File

@@ -20,23 +20,33 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class LegacyHandshakePacket implements MinecraftPacket {
public final class LegacyHandshakePacket implements MinecraftPacket {
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
throw new UnsupportedOperationException();
}
public static final LegacyHandshakePacket INSTANCE = new LegacyHandshakePacket();
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
throw new UnsupportedOperationException();
private LegacyHandshakePacket() {
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.legacyping.LegacyMinecraftPingVersion;
import io.netty.buffer.ByteBuf;
import java.net.InetSocketAddress;
import org.checkerframework.checker.nullness.qual.Nullable;
public class LegacyPingPacket implements MinecraftPacket {
public final class LegacyPingPacket implements MinecraftPacket {
private final LegacyMinecraftPingVersion version;
private final @Nullable InetSocketAddress vhost;
@@ -41,26 +42,38 @@ public class LegacyPingPacket implements MinecraftPacket {
this.vhost = vhost;
}
public LegacyMinecraftPingVersion getVersion() {
public LegacyMinecraftPingVersion version() {
return version;
}
public @Nullable InetSocketAddress getVhost() {
public @Nullable InetSocketAddress vhost() {
return vhost;
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
throw new UnsupportedOperationException();
public LegacyMinecraftPingVersion getVersion() {
return version();
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
throw new UnsupportedOperationException();
public @Nullable InetSocketAddress getVhost() {
return vhost();
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
@@ -33,22 +34,20 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.checkerframework.checker.nullness.qual.Nullable;
public class LegacyPlayerListItemPacket implements MinecraftPacket {
public final class LegacyPlayerListItemPacket implements MinecraftPacket {
public static final int ADD_PLAYER = 0;
public static final int UPDATE_GAMEMODE = 1;
public static final int UPDATE_LATENCY = 2;
public static final int UPDATE_DISPLAY_NAME = 3;
public static final int REMOVE_PLAYER = 4;
private int action;
private final List<Item> items = new ArrayList<>();
private final int action;
private final List<Item> items;
public LegacyPlayerListItemPacket(int action, List<Item> items) {
this.action = action;
this.items.addAll(items);
}
public LegacyPlayerListItemPacket() {
this.items = ImmutableList.copyOf(items);
}
public int getAction() {
@@ -60,10 +59,18 @@ public class LegacyPlayerListItemPacket implements MinecraftPacket {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<LegacyPlayerListItemPacket> {
@Override
public LegacyPlayerListItemPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
action = ProtocolUtils.readVarInt(buf);
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));
@@ -98,34 +105,28 @@ public class LegacyPlayerListItemPacket implements MinecraftPacket {
throw new UnsupportedOperationException("Unknown action " + action);
}
}
return new LegacyPlayerListItemPacket(action, items);
} else {
Item item = new Item();
item.setName(ProtocolUtils.readString(buf));
action = buf.readBoolean() ? ADD_PLAYER : REMOVE_PLAYER;
int action = buf.readBoolean() ? ADD_PLAYER : REMOVE_PLAYER;
item.setLatency(buf.readShort());
items.add(item);
return new LegacyPlayerListItemPacket(action, ImmutableList.of(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) {
public void encode(LegacyPlayerListItemPacket packet, 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) {
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 (action) {
switch (packet.action) {
case ADD_PLAYER:
ProtocolUtils.writeString(buf, item.getName());
ProtocolUtils.writeProperties(buf, item.getProperties());
@@ -154,11 +155,11 @@ public class LegacyPlayerListItemPacket implements MinecraftPacket {
// Do nothing, all that is needed is the uuid
break;
default:
throw new UnsupportedOperationException("Unknown action " + action);
throw new UnsupportedOperationException("Unknown action " + packet.action);
}
}
} else {
Item item = items.get(0);
Item item = packet.items.get(0);
Component displayNameComponent = item.getDisplayName();
if (displayNameComponent != null) {
String displayName = LegacyComponentSerializer.legacySection()
@@ -168,17 +169,20 @@ public class LegacyPlayerListItemPacket implements MinecraftPacket {
} else {
ProtocolUtils.writeString(buf, item.getName());
}
buf.writeBoolean(action != REMOVE_PLAYER);
buf.writeBoolean(packet.action != REMOVE_PLAYER);
buf.writeShort(item.getLatency());
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
private static @Nullable Component readOptionalComponent(ByteBuf buf, ProtocolVersion version) {
if (buf.readBoolean()) {
return ProtocolUtils.getJsonChatSerializer(version)
.deserialize(ProtocolUtils.readString(buf));
}
return null;
}
private void writeDisplayName(ByteBuf buf, @Nullable Component displayName,
private static void writeDisplayName(ByteBuf buf, @Nullable Component displayName,
ProtocolVersion version) {
buf.writeBoolean(displayName != null);
if (displayName != null) {
@@ -186,6 +190,7 @@ public class LegacyPlayerListItemPacket implements MinecraftPacket {
.serialize(displayName));
}
}
}
public static class Item {

View File

@@ -20,29 +20,38 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class LoginAcknowledgedPacket implements MinecraftPacket {
public final class LoginAcknowledgedPacket implements MinecraftPacket {
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
}
public static final LoginAcknowledgedPacket INSTANCE = new LoginAcknowledgedPacket();
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
}
@Override
public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
return 0;
private LoginAcknowledgedPacket() {
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.DefaultByteBufHolder;
import io.netty.buffer.Unpooled;
import org.checkerframework.checker.nullness.qual.Nullable;
public class LoginPluginMessagePacket extends DeferredByteBufHolder implements MinecraftPacket {
public final class LoginPluginMessagePacket extends DefaultByteBufHolder implements MinecraftPacket {
private int id;
private @Nullable String channel;
private final int id;
private final String channel;
public LoginPluginMessagePacket() {
super(null);
}
public LoginPluginMessagePacket(int id, @Nullable String channel, ByteBuf data) {
public LoginPluginMessagePacket(int id, String channel, ByteBuf data) {
super(data);
this.id = id;
this.channel = channel;
@@ -47,9 +43,6 @@ public class LoginPluginMessagePacket extends DeferredByteBufHolder implements M
}
public String getChannel() {
if (channel == null) {
throw new IllegalStateException("Channel is not specified!");
}
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
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<LoginPluginMessagePacket> {
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.DefaultByteBufHolder;
import io.netty.buffer.Unpooled;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public class LoginPluginResponsePacket extends DeferredByteBufHolder implements MinecraftPacket {
public final class LoginPluginResponsePacket extends DefaultByteBufHolder
implements MinecraftPacket {
private int id;
private boolean success;
public LoginPluginResponsePacket() {
super(Unpooled.EMPTY_BUFFER);
}
private final int id;
private final boolean success;
public LoginPluginResponsePacket(int id, boolean success, @MonotonicNonNull ByteBuf buf) {
super(buf);
@@ -42,20 +39,20 @@ public class LoginPluginResponsePacket extends DeferredByteBufHolder implements
this.success = success;
}
public int getId() {
public int id() {
return id;
}
public void setId(int id) {
this.id = id;
}
public boolean isSuccess() {
public boolean success() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
public int getId() {
return id();
}
public boolean isSuccess() {
return success();
}
@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
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<LoginPluginResponsePacket> {
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class PingIdentifyPacket implements MinecraftPacket {
private int id;
@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);
}
public record PingIdentifyPacket(int id) implements MinecraftPacket {
@Override
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import io.netty.buffer.DefaultByteBufHolder;
public class PluginMessagePacket extends DeferredByteBufHolder implements MinecraftPacket {
public final class PluginMessagePacket extends DefaultByteBufHolder implements MinecraftPacket {
private @Nullable String channel;
private final String channel;
public PluginMessagePacket() {
super(null);
}
public PluginMessagePacket(String channel,
@MonotonicNonNull ByteBuf backing) {
public PluginMessagePacket(String channel, ByteBuf backing) {
super(backing);
this.channel = channel;
}
public String getChannel() {
if (channel == null) {
throw new IllegalStateException("Channel is not specified.");
}
return channel;
}
public void setChannel(String channel) {
this.channel = channel;
}
@Override
public String toString() {
return "PluginMessage{"
@@ -62,44 +48,6 @@ public 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
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
@@ -145,8 +93,41 @@ public class PluginMessagePacket extends DeferredByteBufHolder implements Minecr
return (PluginMessagePacket) super.touch(hint);
}
public static class Codec implements PacketCodec<PluginMessagePacket> {
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.Collection;
import java.util.UUID;
public class RemovePlayerInfoPacket implements MinecraftPacket {
private Collection<UUID> profilesToRemove;
public RemovePlayerInfoPacket() {
this.profilesToRemove = new ArrayList<>();
}
public RemovePlayerInfoPacket(Collection<UUID> profilesToRemove) {
this.profilesToRemove = profilesToRemove;
}
public record RemovePlayerInfoPacket(Collection<UUID> profilesToRemove) implements MinecraftPacket {
public Collection<UUID> getProfilesToRemove() {
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
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable;
public class RemoveResourcePackPacket implements MinecraftPacket {
public record RemoveResourcePackPacket(@Nullable UUID id) implements MinecraftPacket {
private UUID id;
public RemoveResourcePackPacket() {
}
public RemoveResourcePackPacket(UUID id) {
this.id = id;
}
public UUID getId() {
public @Nullable UUID getId() {
return id;
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
if (buf.readBoolean()) {
this.id = ProtocolUtils.readUuid(buf);
}
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
buf.writeBoolean(id != null);
if (id != null) {
ProtocolUtils.writeUuid(buf, id);
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
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.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
@@ -30,100 +31,47 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import java.util.UUID;
import java.util.regex.Pattern;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ResourcePackRequestPacket implements MinecraftPacket {
public final class ResourcePackRequestPacket implements MinecraftPacket {
private @MonotonicNonNull UUID id; // 1.20.3+
private @MonotonicNonNull String url;
private @MonotonicNonNull String hash;
private boolean isRequired; // 1.17+
private @Nullable ComponentHolder prompt; // 1.17+
private final @Nullable UUID id; // 1.20.3+
private final String url;
private final String hash;
private final boolean isRequired; // 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+
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() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public @Nullable String getUrl() {
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public boolean isRequired() {
return isRequired;
}
public @Nullable String getHash() {
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
public void setRequired(boolean required) {
isRequired = required;
}
public @Nullable ComponentHolder getPrompt() {
return prompt;
}
public void setPrompt(@Nullable ComponentHolder prompt) {
this.prompt = prompt;
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) {
this.id = ProtocolUtils.readUuid(buf);
}
this.url = ProtocolUtils.readString(buf);
this.hash = ProtocolUtils.readString(buf);
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_17)) {
this.isRequired = buf.readBoolean();
if (buf.readBoolean()) {
this.prompt = ComponentHolder.read(buf, protocolVersion);
} else {
this.prompt = null;
}
}
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) {
if (id == null) {
throw new IllegalStateException("Resource pack id not set yet!");
}
ProtocolUtils.writeUuid(buf, id);
}
if (url == null || hash == null) {
throw new IllegalStateException("Packet not fully filled in yet!");
}
ProtocolUtils.writeString(buf, url);
ProtocolUtils.writeString(buf, hash);
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_17)) {
buf.writeBoolean(isRequired);
if (prompt != null) {
buf.writeBoolean(true);
prompt.write(buf);
} else {
buf.writeBoolean(false);
}
}
}
public VelocityResourcePackInfo toServerPromptedPack() {
final ResourcePackInfo.Builder builder =
new VelocityResourcePackInfo.BuilderImpl(Preconditions.checkNotNull(url))
@@ -153,4 +101,51 @@ public class ResourcePackRequestPacket implements MinecraftPacket {
", 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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import java.util.UUID;
public class ResourcePackResponsePacket implements MinecraftPacket {
public record ResourcePackResponsePacket(UUID id, String hash,
Status status) implements MinecraftPacket {
private UUID id;
private String hash = "";
private @MonotonicNonNull Status status;
public ResourcePackResponsePacket() {
}
public 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 UUID getId() {
return id;
}
public String getHash() {
return hash;
}
public UUID getId() {
return id;
}
@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());
public Status getStatus() {
return status;
}
@Override
@@ -85,12 +48,32 @@ public class ResourcePackResponsePacket implements MinecraftPacket {
return handler.handle(this);
}
public static class Codec implements PacketCodec<ResourcePackResponsePacket> {
@Override
public String toString() {
return "ResourcePackResponsePacket{" +
"id=" + id +
", hash='" + hash + '\'' +
", status=" + status +
'}';
public ResourcePackResponsePacket decode(ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
UUID id = null;
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) {
id = ProtocolUtils.readUuid(buf);
}
String hash = "";
if (protocolVersion.noGreaterThan(ProtocolVersion.MINECRAFT_1_9_4)) {
hash = ProtocolUtils.readString(buf);
}
Status status = Status.values()[ProtocolUtils.readVarInt(buf)];
return new ResourcePackResponsePacket(id, hash, status);
}
@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.registry.DimensionInfo;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.Pair;
@@ -28,23 +29,20 @@ import net.kyori.adventure.nbt.BinaryTagIO;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import org.checkerframework.checker.nullness.qual.Nullable;
public class RespawnPacket implements MinecraftPacket {
public final class RespawnPacket implements MinecraftPacket {
private int dimension;
private long partialHashedSeed;
private short difficulty;
private short gamemode;
private String levelType = "";
private byte dataToKeep; // 1.16+
private DimensionInfo dimensionInfo; // 1.16-1.16.1
private short previousGamemode; // 1.16+
private CompoundBinaryTag currentDimensionData; // 1.16.2+
private @Nullable Pair<String, Long> lastDeathPosition; // 1.19+
private int portalCooldown; // 1.20+
private int seaLevel; // 1.21.2+
public RespawnPacket() {
}
private final int dimension;
private final long partialHashedSeed;
private final short difficulty;
private final short gamemode;
private final String levelType;
private final byte dataToKeep;
private final DimensionInfo dimensionInfo;
private final short previousGamemode;
private final CompoundBinaryTag currentDimensionData;
private final @Nullable Pair<String, Long> lastDeathPosition;
private final int portalCooldown;
private final int seaLevel;
public RespawnPacket(int dimension, long partialHashedSeed, short difficulty, short gamemode,
String levelType, byte dataToKeep, DimensionInfo dimensionInfo,
@@ -73,84 +71,60 @@ public class RespawnPacket implements MinecraftPacket {
joinGame.getPortalCooldown(), joinGame.getSeaLevel());
}
public int getDimension() {
return dimension;
public static RespawnPacket fromJoinGame(JoinGamePacket joinGame, int newDimension) {
return new RespawnPacket(newDimension, joinGame.getPartialHashedSeed(),
joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType(),
(byte) 0, joinGame.getDimensionInfo(), joinGame.getPreviousGamemode(),
joinGame.getCurrentDimensionData(), joinGame.getLastDeathPosition(),
joinGame.getPortalCooldown(), joinGame.getSeaLevel());
}
public void setDimension(int dimension) {
this.dimension = dimension;
public int getDimension() {
return dimension;
}
public long getPartialHashedSeed() {
return partialHashedSeed;
}
public void setPartialHashedSeed(long partialHashedSeed) {
this.partialHashedSeed = partialHashedSeed;
}
public short getDifficulty() {
return difficulty;
}
public void setDifficulty(short difficulty) {
this.difficulty = difficulty;
}
public short getGamemode() {
return gamemode;
}
public void setGamemode(short gamemode) {
this.gamemode = gamemode;
}
public String getLevelType() {
return levelType;
}
public void setLevelType(String levelType) {
this.levelType = levelType;
}
public byte getDataToKeep() {
return dataToKeep;
}
public void setDataToKeep(byte dataToKeep) {
this.dataToKeep = dataToKeep;
}
public short getPreviousGamemode() {
return previousGamemode;
}
public void setPreviousGamemode(short previousGamemode) {
this.previousGamemode = previousGamemode;
}
public Pair<String, Long> getLastDeathPosition() {
return lastDeathPosition;
}
public void setLastDeathPosition(Pair<String, Long> lastDeathPosition) {
this.lastDeathPosition = lastDeathPosition;
}
public int getPortalCooldown() {
return portalCooldown;
}
public void setPortalCooldown(int portalCooldown) {
this.portalCooldown = portalCooldown;
}
public int getSeaLevel() {
return seaLevel;
}
public void setSeaLevel(int seaLevel) {
this.seaLevel = seaLevel;
public DimensionInfo getDimensionInfo() {
return dimensionInfo;
}
public CompoundBinaryTag getCurrentDimensionData() {
return currentDimensionData;
}
@Override
@@ -172,13 +146,23 @@ public class RespawnPacket implements MinecraftPacket {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
public boolean handle(MinecraftSessionHandler handler) {
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)) {
this.currentDimensionData = ProtocolUtils.readCompoundTag(buf, version, BinaryTagIO.reader());
currentDimensionData = ProtocolUtils.readCompoundTag(buf, version, BinaryTagIO.reader());
dimensionKey = ProtocolUtils.readString(buf);
} else {
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
@@ -189,106 +173,129 @@ public class RespawnPacket implements MinecraftPacket {
levelName = ProtocolUtils.readString(buf);
}
} else {
this.dimension = buf.readInt();
dimension = buf.readInt();
}
short difficulty = 0;
if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) {
this.difficulty = buf.readUnsignedByte();
difficulty = buf.readUnsignedByte();
}
long partialHashedSeed = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) {
this.partialHashedSeed = buf.readLong();
partialHashedSeed = buf.readLong();
}
this.gamemode = buf.readByte();
short gamemode = buf.readByte();
DimensionInfo dimensionInfo = null;
String levelType = "";
short previousGamemode = 0;
byte dataToKeep = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) {
this.previousGamemode = buf.readByte();
previousGamemode = buf.readByte();
boolean isDebug = buf.readBoolean();
boolean isFlat = buf.readBoolean();
this.dimensionInfo = new DimensionInfo(dimensionKey, levelName, isFlat, isDebug, version);
dimensionInfo = new DimensionInfo(dimensionKey, levelName, isFlat, isDebug, version);
if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
this.dataToKeep = (byte) (buf.readBoolean() ? 1 : 0);
dataToKeep = (byte) (buf.readBoolean() ? 1 : 0);
} else if (version.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
this.dataToKeep = buf.readByte();
dataToKeep = buf.readByte();
}
} else {
this.levelType = ProtocolUtils.readString(buf, 16);
levelType = ProtocolUtils.readString(buf, 16);
}
Pair<String, Long> lastDeathPosition = null;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19) && buf.readBoolean()) {
this.lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong());
lastDeathPosition = Pair.of(ProtocolUtils.readString(buf), buf.readLong());
}
int portalCooldown = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) {
this.portalCooldown = ProtocolUtils.readVarInt(buf);
portalCooldown = ProtocolUtils.readVarInt(buf);
}
int seaLevel = 0;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
this.seaLevel = ProtocolUtils.readVarInt(buf);
seaLevel = ProtocolUtils.readVarInt(buf);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
this.dataToKeep = buf.readByte();
dataToKeep = buf.readByte();
}
return new RespawnPacket(dimension, partialHashedSeed, difficulty, gamemode, levelType,
dataToKeep, dimensionInfo, previousGamemode, currentDimensionData, lastDeathPosition,
portalCooldown, seaLevel);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
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, currentDimensionData);
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier());
ProtocolUtils.writeBinaryTag(buf, version, packet.currentDimensionData);
ProtocolUtils.writeString(buf, packet.dimensionInfo.getRegistryIdentifier());
} else {
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
ProtocolUtils.writeVarInt(buf, dimension);
ProtocolUtils.writeVarInt(buf, packet.dimension);
} else {
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier());
ProtocolUtils.writeString(buf, packet.dimensionInfo.getRegistryIdentifier());
}
ProtocolUtils.writeString(buf, dimensionInfo.getLevelName());
ProtocolUtils.writeString(buf, packet.dimensionInfo.getLevelName());
}
} else {
buf.writeInt(dimension);
buf.writeInt(packet.dimension);
}
if (version.noGreaterThan(ProtocolVersion.MINECRAFT_1_13_2)) {
buf.writeByte(difficulty);
buf.writeByte(packet.difficulty);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) {
buf.writeLong(partialHashedSeed);
buf.writeLong(packet.partialHashedSeed);
}
buf.writeByte(gamemode);
buf.writeByte(packet.gamemode);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) {
buf.writeByte(previousGamemode);
buf.writeBoolean(dimensionInfo.isDebugType());
buf.writeBoolean(dimensionInfo.isFlat());
buf.writeByte(packet.previousGamemode);
buf.writeBoolean(packet.dimensionInfo.isDebugType());
buf.writeBoolean(packet.dimensionInfo.isFlat());
if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
buf.writeBoolean(dataToKeep != 0);
buf.writeBoolean(packet.dataToKeep != 0);
} else if (version.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
buf.writeByte(dataToKeep);
buf.writeByte(packet.dataToKeep);
}
} else {
ProtocolUtils.writeString(buf, levelType);
ProtocolUtils.writeString(buf, packet.levelType);
}
// optional death location
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) {
if (lastDeathPosition != null) {
if (packet.lastDeathPosition != null) {
buf.writeBoolean(true);
ProtocolUtils.writeString(buf, lastDeathPosition.key());
buf.writeLong(lastDeathPosition.value());
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, portalCooldown);
ProtocolUtils.writeVarInt(buf, packet.portalCooldown);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
ProtocolUtils.writeVarInt(buf, seaLevel);
ProtocolUtils.writeVarInt(buf, packet.seaLevel);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
buf.writeByte(dataToKeep);
buf.writeByte(packet.dataToKeep);
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

View File

@@ -21,6 +21,7 @@ import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.util.Favicon;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
@@ -29,14 +30,11 @@ import org.jetbrains.annotations.Nullable;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class ServerDataPacket implements MinecraftPacket {
public final class ServerDataPacket implements MinecraftPacket {
private @Nullable ComponentHolder description;
private @Nullable Favicon favicon;
private boolean secureChatEnforced; // Added in 1.19.1 - Removed in 1.20.5
public ServerDataPacket() {
}
private final @Nullable ComponentHolder description;
private final @Nullable Favicon favicon;
private final boolean secureChatEnforced; // Added in 1.19.1 - Removed in 1.20.5
public ServerDataPacket(@Nullable ComponentHolder description, @Nullable Favicon favicon,
boolean secureChatEnforced) {
@@ -45,63 +43,6 @@ public class ServerDataPacket implements MinecraftPacket {
this.secureChatEnforced = secureChatEnforced;
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4) || buf.readBoolean()) {
this.description = ComponentHolder.read(buf, protocolVersion);
}
if (buf.readBoolean()) {
String iconBase64;
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4)) {
byte[] iconBytes = ProtocolUtils.readByteArray(buf);
iconBase64 = "data:image/png;base64," + new String(Base64.getEncoder().encode(iconBytes), StandardCharsets.UTF_8);
} else {
iconBase64 = ProtocolUtils.readString(buf);
}
this.favicon = new Favicon(iconBase64);
}
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
buf.readBoolean();
}
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)
&& protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
this.secureChatEnforced = buf.readBoolean();
}
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
boolean hasDescription = this.description != null;
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19_4)) {
buf.writeBoolean(hasDescription);
}
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4) || hasDescription) {
this.description.write(buf);
}
boolean hasFavicon = this.favicon != null;
buf.writeBoolean(hasFavicon);
if (hasFavicon) {
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4)) {
String cutIconBase64 = favicon.getBase64Url().substring("data:image/png;base64,".length());
byte[] iconBytes = Base64.getDecoder().decode(cutIconBase64.getBytes(StandardCharsets.UTF_8));
ProtocolUtils.writeByteArray(buf, iconBytes);
} else {
ProtocolUtils.writeString(buf, favicon.getBase64Url());
}
}
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
buf.writeBoolean(false);
}
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)
&& protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
buf.writeBoolean(this.secureChatEnforced);
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
@@ -119,12 +60,68 @@ public class ServerDataPacket implements MinecraftPacket {
return secureChatEnforced;
}
public void setSecureChatEnforced(boolean secureChatEnforced) {
this.secureChatEnforced = secureChatEnforced;
public static class Codec implements PacketCodec<ServerDataPacket> {
@Override
public ServerDataPacket decode(ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
ComponentHolder description = null;
Favicon favicon = null;
boolean secureChatEnforced = false;
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4) || buf.readBoolean()) {
description = ComponentHolder.read(buf, protocolVersion);
}
if (buf.readBoolean()) {
String iconBase64;
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_4)) {
byte[] iconBytes = ProtocolUtils.readByteArray(buf);
iconBase64 = "data:image/png;base64," + new String(Base64.getEncoder().encode(iconBytes), StandardCharsets.UTF_8);
} else {
iconBase64 = ProtocolUtils.readString(buf);
}
favicon = new Favicon(iconBase64);
}
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
buf.readBoolean();
}
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)
&& protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
secureChatEnforced = buf.readBoolean();
}
return new ServerDataPacket(description, favicon, secureChatEnforced);
}
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) {
return 8 * 1024;
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,57 +17,54 @@
package com.velocitypowered.proxy.protocol.packet;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ServerLoginPacket implements MinecraftPacket {
public final class ServerLoginPacket implements MinecraftPacket {
private static final QuietDecoderException EMPTY_USERNAME = new QuietDecoderException(
"Empty username!");
private @Nullable String username;
private @Nullable IdentifiedKey playerKey; // Introduced in 1.19.3
private @Nullable UUID holderUuid; // Used for key revision 2
public ServerLoginPacket() {
}
private final String username;
private final @Nullable IdentifiedKey playerKey; // Introduced in 1.19.3
private final @Nullable UUID holderUuid; // Used for key revision 2
public ServerLoginPacket(String username, @Nullable IdentifiedKey playerKey) {
this.username = Preconditions.checkNotNull(username, "username");
this.playerKey = playerKey;
this(username, playerKey, null);
}
public ServerLoginPacket(String username, @Nullable UUID holderUuid) {
this.username = Preconditions.checkNotNull(username, "username");
this.holderUuid = holderUuid;
this.playerKey = null;
this(username, null, holderUuid);
}
public String getUsername() {
if (username == null) {
throw new IllegalStateException("No username found!");
private ServerLoginPacket(String username, @Nullable IdentifiedKey playerKey,
@Nullable UUID holderUuid) {
this.username = username;
this.playerKey = playerKey;
this.holderUuid = holderUuid;
}
public String username() {
return username;
}
public @Nullable IdentifiedKey getPlayerKey() {
public String getUsername() {
return username();
}
public @Nullable IdentifiedKey playerKey() {
return this.playerKey;
}
public void setPlayerKey(IdentifiedKey playerKey) {
this.playerKey = playerKey;
}
public @Nullable UUID getHolderUuid() {
public @Nullable UUID holderUuid() {
return holderUuid;
}
@@ -81,12 +78,22 @@ public class ServerLoginPacket implements MinecraftPacket {
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion version) {
username = ProtocolUtils.readString(buf, 16);
public boolean handle(MinecraftSessionHandler handler) {
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;
@@ -99,8 +106,8 @@ public class ServerLoginPacket implements MinecraftPacket {
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
this.holderUuid = ProtocolUtils.readUuid(buf);
return;
holderUuid = ProtocolUtils.readUuid(buf);
return new ServerLoginPacket(username, playerKey, holderUuid);
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) {
@@ -111,37 +118,40 @@ public class ServerLoginPacket implements MinecraftPacket {
} else {
playerKey = null;
}
return new ServerLoginPacket(username, playerKey, holderUuid);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (username == null) {
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, username);
ProtocolUtils.writeString(buf, packet.username);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) {
if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
if (playerKey != null) {
if (packet.playerKey != null) {
buf.writeBoolean(true);
ProtocolUtils.writePlayerKey(buf, playerKey);
ProtocolUtils.writePlayerKey(buf, packet.playerKey);
} else {
buf.writeBoolean(false);
}
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
ProtocolUtils.writeUuid(buf, this.holderUuid);
ProtocolUtils.writeUuid(buf, packet.holderUuid);
return;
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) {
if (playerKey != null && playerKey.getSignatureHolder() != null) {
if (packet.playerKey != null && packet.playerKey.getSignatureHolder() != null) {
buf.writeBoolean(true);
ProtocolUtils.writeUuid(buf, playerKey.getSignatureHolder());
} else if (this.holderUuid != null) {
ProtocolUtils.writeUuid(buf, packet.playerKey.getSignatureHolder());
} else if (packet.holderUuid != null) {
buf.writeBoolean(true);
ProtocolUtils.writeUuid(buf, this.holderUuid);
ProtocolUtils.writeUuid(buf, packet.holderUuid);
} else {
buf.writeBoolean(false);
}
@@ -150,7 +160,8 @@ public class ServerLoginPacket implements MinecraftPacket {
}
@Override
public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) {
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);
@@ -173,9 +184,5 @@ public class ServerLoginPacket implements MinecraftPacket {
}
return base;
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

View File

@@ -22,52 +22,40 @@ import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.UuidUtils;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.util.VelocityProperties;
import io.netty.buffer.ByteBuf;
import java.util.List;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ServerLoginSuccessPacket implements MinecraftPacket {
public final class ServerLoginSuccessPacket implements MinecraftPacket {
private @Nullable UUID uuid;
private @Nullable String username;
private @Nullable List<GameProfile.Property> properties;
private final UUID uuid;
private final String username;
private final List<GameProfile.Property> properties;
private static final boolean strictErrorHandling = VelocityProperties
.readBoolean("velocity.strictErrorHandling", true);
public UUID getUuid() {
if (uuid == null) {
throw new IllegalStateException("No UUID specified!");
public ServerLoginSuccessPacket(UUID uuid, String username, List<GameProfile.Property> properties) {
this.uuid = uuid;
this.username = username;
this.properties = properties;
}
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public String getUsername() {
if (username == null) {
throw new IllegalStateException("No username specified!");
}
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<GameProfile.Property> getProperties() {
return properties;
}
public void setProperties(List<GameProfile.Property> properties) {
this.properties = properties;
}
@Override
public String toString() {
return "ServerLoginSuccess{"
@@ -78,7 +66,15 @@ public class ServerLoginSuccessPacket implements MinecraftPacket {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<ServerLoginSuccessPacket> {
@Override
public ServerLoginSuccessPacket decode(ByteBuf buf, Direction direction,
ProtocolVersion version) {
UUID uuid;
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) {
uuid = ProtocolUtils.readUuid(buf);
} else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) {
@@ -88,40 +84,38 @@ public class ServerLoginSuccessPacket implements MinecraftPacket {
} else {
uuid = UuidUtils.fromUndashed(ProtocolUtils.readString(buf, 32));
}
username = ProtocolUtils.readString(buf, 16);
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(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (uuid == null) {
throw new IllegalStateException("No UUID specified!");
}
public void encode(ServerLoginSuccessPacket packet, ByteBuf buf, Direction direction,
ProtocolVersion version) {
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) {
ProtocolUtils.writeUuid(buf, uuid);
ProtocolUtils.writeUuid(buf, packet.uuid);
} else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) {
ProtocolUtils.writeUuidIntArray(buf, uuid);
ProtocolUtils.writeUuidIntArray(buf, packet.uuid);
} else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_7_6)) {
ProtocolUtils.writeString(buf, uuid.toString());
ProtocolUtils.writeString(buf, packet.uuid.toString());
} else {
ProtocolUtils.writeString(buf, UuidUtils.toUndashed(uuid));
ProtocolUtils.writeString(buf, UuidUtils.toUndashed(packet.uuid));
}
if (username == null) {
throw new IllegalStateException("No username specified!");
}
ProtocolUtils.writeString(buf, username);
ProtocolUtils.writeString(buf, packet.username);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) {
if (properties == null) {
if (packet.properties == null) {
ProtocolUtils.writeVarInt(buf, 0);
} else {
ProtocolUtils.writeProperties(buf, properties);
ProtocolUtils.writeProperties(buf, packet.properties);
}
}
if (version == ProtocolVersion.MINECRAFT_1_20_5 || version == ProtocolVersion.MINECRAFT_1_21) {
@@ -130,14 +124,11 @@ public class ServerLoginSuccessPacket implements MinecraftPacket {
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ServerboundCookieResponsePacket implements MinecraftPacket {
private Key key;
private byte @Nullable [] payload;
public record ServerboundCookieResponsePacket(Key key,
byte @Nullable [] payload) implements MinecraftPacket {
public Key getKey() {
return key;
@@ -39,34 +38,32 @@ public class ServerboundCookieResponsePacket implements MinecraftPacket {
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
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.DefaultByteBufHolder;
public class ServerboundCustomClickActionPacket extends DeferredByteBufHolder implements MinecraftPacket {
public class ServerboundCustomClickActionPacket extends DefaultByteBufHolder
implements MinecraftPacket {
public ServerboundCustomClickActionPacket() {
super(null);
}
@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());
public ServerboundCustomClickActionPacket(ByteBuf backing) {
super(backing);
}
@Override
@@ -47,7 +39,61 @@ public class ServerboundCustomClickActionPacket extends DeferredByteBufHolder im
}
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) {
return content().readableBytes();
public ServerboundCustomClickActionPacket copy() {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class SetCompressionPacket implements MinecraftPacket {
private int threshold;
public SetCompressionPacket() {
}
public SetCompressionPacket(int threshold) {
this.threshold = threshold;
}
public record SetCompressionPacket(int threshold) implements MinecraftPacket {
public int getThreshold() {
return threshold;
}
public void setThreshold(int threshold) {
this.threshold = threshold;
}
@Override
public String toString() {
return "SetCompression{"
@@ -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
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf;
public class StatusPingPacket implements MinecraftPacket {
private long randomId;
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
randomId = buf.readLong();
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
buf.writeLong(randomId);
}
public record StatusPingPacket(long randomId) implements MinecraftPacket {
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<StatusPingPacket> {
@Override
public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) {
public StatusPingPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
return new StatusPingPacket(buf.readLong());
}
@Override
public void encode(StatusPingPacket packet, ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
buf.writeLong(packet.randomId);
}
@Override
public int decodeExpectedMaxLength(ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
return 8;
}
@Override
public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf;
public class StatusRequestPacket implements MinecraftPacket {
public final class StatusRequestPacket implements MinecraftPacket {
public static final StatusRequestPacket INSTANCE = new StatusRequestPacket();
@@ -32,16 +32,6 @@ 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
public String toString() {
return "StatusRequest";
@@ -52,8 +42,22 @@ public class StatusRequestPacket implements MinecraftPacket {
return handler.handle(this);
}
public static class Codec implements PacketCodec<StatusRequestPacket> {
@Override
public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class StatusResponsePacket implements MinecraftPacket {
public final class StatusResponsePacket implements MinecraftPacket {
private @Nullable CharSequence status;
public StatusResponsePacket() {
}
private final @Nullable CharSequence status;
public StatusResponsePacket(CharSequence status) {
this.status = status;
}
public String getStatus() {
public String status() {
if (status == null) {
throw new IllegalStateException("Status is not specified");
}
return status.toString();
}
public String getStatus() {
return status();
}
@Override
public String toString() {
return "StatusResponse{"
@@ -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
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<StatusResponsePacket> {
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) {
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_9;
import com.google.common.base.MoreObjects;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class TabCompleteRequestPacket implements MinecraftPacket {
public final class TabCompleteRequestPacket implements MinecraftPacket {
private static final int VANILLA_MAX_TAB_COMPLETE_LEN = 2048;
private @Nullable String command;
private int transactionId;
private boolean assumeCommand;
private boolean hasPosition;
private long position;
private final @Nullable String command;
private final int transactionId;
private final boolean assumeCommand;
private final boolean hasPosition;
private final long position;
public String getCommand() {
public TabCompleteRequestPacket() {
this(null, 0, false, false, 0);
}
public TabCompleteRequestPacket(@Nullable String command, int transactionId,
boolean assumeCommand, boolean hasPosition, long position) {
this.command = command;
this.transactionId = transactionId;
this.assumeCommand = assumeCommand;
this.hasPosition = hasPosition;
this.position = position;
}
public String command() {
if (command == null) {
throw new IllegalStateException("Command is not specified");
}
return command;
}
public void setCommand(String command) {
this.command = command;
public String getCommand() {
return command();
}
public boolean isAssumeCommand() {
public boolean assumeCommand() {
return assumeCommand;
}
public void setAssumeCommand(boolean assumeCommand) {
this.assumeCommand = assumeCommand;
public boolean isAssumeCommand() {
return assumeCommand();
}
public boolean hasPosition() {
return hasPosition;
}
public void setHasPosition(boolean hasPosition) {
this.hasPosition = hasPosition;
}
public long getPosition() {
public long position() {
return position;
}
public void setPosition(long position) {
this.position = position;
}
public int getTransactionId() {
public int transactionId() {
return transactionId;
}
public void setTransactionId(int transactionId) {
this.transactionId = transactionId;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("command", command)
.add("transactionId", transactionId)
.add("assumeCommand", assumeCommand)
.add("hasPosition", hasPosition)
.add("position", position)
.toString();
}
@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
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf;
@@ -30,39 +31,54 @@ import java.util.ArrayList;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
public class TabCompleteResponsePacket implements MinecraftPacket {
public final class TabCompleteResponsePacket implements MinecraftPacket {
private int transactionId;
private int start;
private int length;
private final List<Offer> offers = new ArrayList<>();
private final int transactionId;
private final int start;
private final int length;
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;
}
public void setTransactionId(int transactionId) {
this.transactionId = transactionId;
}
public int getStart() {
public int start() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getLength() {
public int length() {
return length;
}
public void setLength(int length) {
this.length = length;
public List<Offer> offers() {
return offers;
}
public int getTransactionId() {
return transactionId();
}
public int getStart() {
return start();
}
public int getLength() {
return length();
}
public List<Offer> getOffers() {
return offers;
return offers();
}
public TabCompleteResponsePacket withOffers(List<Offer> offers) {
return new TabCompleteResponsePacket(transactionId, start, length, offers);
}
@Override
@@ -76,33 +92,45 @@ public class TabCompleteResponsePacket implements MinecraftPacket {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
public boolean handle(MinecraftSessionHandler handler) {
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)) {
this.transactionId = ProtocolUtils.readVarInt(buf);
this.start = ProtocolUtils.readVarInt(buf);
this.length = ProtocolUtils.readVarInt(buf);
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(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
public void encode(TabCompleteResponsePacket packet, 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.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) {
@@ -110,16 +138,12 @@ public class TabCompleteResponsePacket implements MinecraftPacket {
}
}
} else {
ProtocolUtils.writeVarInt(buf, offers.size());
for (Offer offer : offers) {
ProtocolUtils.writeVarInt(buf, packet.offers.size());
for (Offer offer : packet.offers) {
ProtocolUtils.writeString(buf, offer.text);
}
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Offer implements Comparable<Offer> {

View File

@@ -20,22 +20,13 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import java.net.InetSocketAddress;
import org.jetbrains.annotations.Nullable;
public class TransferPacket implements MinecraftPacket {
private String host;
private int port;
public TransferPacket() {
}
public TransferPacket(final String host, final int port) {
this.host = host;
this.port = port;
}
public record TransferPacket(String host, int port) implements MinecraftPacket {
@Nullable
public InetSocketAddress address() {
@@ -45,20 +36,31 @@ public class TransferPacket implements MinecraftPacket {
return new InetSocketAddress(host, port);
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
this.host = ProtocolUtils.readString(buf);
this.port = ProtocolUtils.readVarInt(buf);
public String getHost() {
return host;
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeString(buf, host);
ProtocolUtils.writeVarInt(buf, port);
public int getPort() {
return port;
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import com.velocitypowered.proxy.protocol.packet.chat.RemoteChatSession;
@@ -28,29 +29,18 @@ import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
public class UpsertPlayerInfoPacket implements MinecraftPacket {
public final class UpsertPlayerInfoPacket implements MinecraftPacket {
private static final Action[] ALL_ACTIONS = Action.class.getEnumConstants();
private final EnumSet<Action> actions;
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) {
this.actions = actions;
this.entries = entries;
@@ -68,69 +58,58 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket {
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,
public boolean handle(MinecraftSessionHandler handler) {
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)) {
addAction(ALL_ACTIONS[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 : this.actions) {
for (Action action : actions) {
action.read.read(protocolVersion, buf, entry);
}
addEntry(entry);
entries.add(entry);
}
return new UpsertPlayerInfoPacket(actions, entries);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
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, this.actions.contains(ALL_ACTIONS[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, this.entries.size());
for (Entry entry : this.entries) {
ProtocolUtils.writeVarInt(buf, packet.entries.size());
for (Entry entry : packet.entries) {
ProtocolUtils.writeUuid(buf, entry.profileId);
for (Action action : this.actions) {
for (Action action : packet.actions) {
action.write.write(protocolVersion, buf, entry);
}
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public enum Action {

View File

@@ -20,28 +20,11 @@ package com.velocitypowered.proxy.protocol.packet.chat;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class ChatAcknowledgementPacket implements MinecraftPacket {
int offset;
public ChatAcknowledgementPacket(int offset) {
this.offset = offset;
}
public ChatAcknowledgementPacket() {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
offset = ProtocolUtils.readVarInt(buf);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, offset);
}
public record ChatAcknowledgementPacket(int offset) implements MinecraftPacket {
@Override
public boolean handle(MinecraftSessionHandler handler) {
@@ -55,7 +38,17 @@ public class ChatAcknowledgementPacket implements MinecraftPacket {
'}';
}
public int offset() {
return 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
public void encode(ChatAcknowledgementPacket packet, ByteBuf buf,
ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, packet.offset);
}
}
}

View File

@@ -20,15 +20,17 @@ package com.velocitypowered.proxy.protocol.packet.chat;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class PlayerChatCompletionPacket implements MinecraftPacket {
public final class PlayerChatCompletionPacket implements MinecraftPacket {
private String[] completions;
private Action action;
private final String[] completions;
private final Action action;
public PlayerChatCompletionPacket() {
this(new String[0], Action.ADD);
}
public PlayerChatCompletionPacket(String[] completions, Action action) {
@@ -36,36 +38,14 @@ public class PlayerChatCompletionPacket implements MinecraftPacket {
this.action = action;
}
public String[] getCompletions() {
public String[] completions() {
return completions;
}
public Action getAction() {
public Action action() {
return action;
}
public void setCompletions(String[] completions) {
this.completions = completions;
}
public void setAction(Action action) {
this.action = action;
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
action = Action.values()[ProtocolUtils.readVarInt(buf)];
completions = ProtocolUtils.readStringArray(buf);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, action.ordinal());
ProtocolUtils.writeStringArray(buf, completions);
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
@@ -76,4 +56,21 @@ public class PlayerChatCompletionPacket implements MinecraftPacket {
REMOVE,
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class SystemChatPacket implements MinecraftPacket {
public final class SystemChatPacket implements MinecraftPacket {
private final ComponentHolder component;
private final ChatType type;
public SystemChatPacket() {
this(null, ChatType.SYSTEM);
}
public SystemChatPacket(ComponentHolder component, ChatType type) {
@@ -33,32 +38,47 @@ public class SystemChatPacket implements MinecraftPacket {
this.type = type;
}
private ComponentHolder component;
private ChatType type;
public ChatType getType() {
public ChatType type() {
return type;
}
public ComponentHolder getComponent() {
public ComponentHolder component() {
return component;
}
public ComponentHolder getComponent() {
return component();
}
public ChatType getType() {
return type();
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
component = ComponentHolder.read(buf, version);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)){
public boolean handle(MinecraftSessionHandler handler) {
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(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
component.write(buf);
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 (type) {
switch (packet.type) {
case SYSTEM:
buf.writeBoolean(false);
break;
@@ -69,12 +89,8 @@ public class SystemChatPacket implements MinecraftPacket {
throw new IllegalArgumentException("Invalid chat type");
}
} else {
ProtocolUtils.writeVarInt(buf, type.getId());
ProtocolUtils.writeVarInt(buf, packet.type.getId());
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

View File

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

View File

@@ -23,41 +23,43 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.crypto.EncryptionUtils;
import com.velocitypowered.proxy.crypto.SignaturePair;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf;
import java.time.Instant;
import org.checkerframework.checker.nullness.qual.Nullable;
public class KeyedPlayerChatPacket implements MinecraftPacket {
private String message;
private boolean signedPreview;
private boolean unsigned = false;
private @Nullable Instant expiry;
private @Nullable byte[] signature;
private @Nullable byte[] salt;
private SignaturePair[] previousMessages = new SignaturePair[0];
private @Nullable SignaturePair lastMessage;
public final class KeyedPlayerChatPacket implements MinecraftPacket {
public static final int MAXIMUM_PREVIOUS_MESSAGE_COUNT = 5;
public static final QuietDecoderException INVALID_PREVIOUS_MESSAGES =
new QuietDecoderException("Invalid previous messages");
public KeyedPlayerChatPacket() {
}
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.unsigned = true;
}
public void setExpiry(@Nullable Instant expiry) {
this.signedPreview = signedPreview;
this.unsigned = unsigned;
this.expiry = expiry;
this.signature = signature;
this.salt = salt;
this.previousMessages = previousMessages;
this.lastMessage = lastMessage;
}
public Instant getExpiry() {
public @Nullable Instant getExpiry() {
return expiry;
}
@@ -74,18 +76,30 @@ public class KeyedPlayerChatPacket implements MinecraftPacket {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<KeyedPlayerChatPacket> {
@Override
public KeyedPlayerChatPacket decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
message = ProtocolUtils.readString(buf, 256);
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;
@@ -93,11 +107,14 @@ public class KeyedPlayerChatPacket implements MinecraftPacket {
throw EncryptionUtils.INVALID_SIGNATURE;
}
signedPreview = buf.readBoolean();
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) {
@@ -116,39 +133,38 @@ public class KeyedPlayerChatPacket implements MinecraftPacket {
ProtocolUtils.readByteArray(buf));
}
}
return new KeyedPlayerChatPacket(message, signedPreview, unsigned, expiry, signature, salt,
previousMessages, lastMessage);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
ProtocolUtils.writeString(buf, message);
public void encode(KeyedPlayerChatPacket packet, ByteBuf buf,
ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeString(buf, packet.message);
buf.writeLong(unsigned ? Instant.now().toEpochMilli() : expiry.toEpochMilli());
buf.writeLong(unsigned ? 0L : Longs.fromByteArray(salt));
buf.writeLong(packet.unsigned ? Instant.now().toEpochMilli() : packet.expiry.toEpochMilli());
buf.writeLong(packet.unsigned ? 0L : Longs.fromByteArray(packet.salt));
ProtocolUtils.writeByteArray(buf, unsigned ? EncryptionUtils.EMPTY : signature);
ProtocolUtils.writeByteArray(buf, packet.unsigned ? EncryptionUtils.EMPTY : packet.signature);
buf.writeBoolean(signedPreview);
buf.writeBoolean(packet.signedPreview);
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)) {
ProtocolUtils.writeVarInt(buf, previousMessages.length);
for (SignaturePair previousMessage : previousMessages) {
ProtocolUtils.writeVarInt(buf, packet.previousMessages.length);
for (SignaturePair previousMessage : packet.previousMessages) {
ProtocolUtils.writeUuid(buf, previousMessage.getSigner());
ProtocolUtils.writeByteArray(buf, previousMessage.getSignature());
}
if (lastMessage != null) {
if (packet.lastMessage != null) {
buf.writeBoolean(true);
ProtocolUtils.writeUuid(buf, lastMessage.getSigner());
ProtocolUtils.writeByteArray(buf, lastMessage.getSignature());
ProtocolUtils.writeUuid(buf, packet.lastMessage.getSigner());
ProtocolUtils.writeByteArray(buf, packet.lastMessage.getSignature());
} else {
buf.writeBoolean(false);
}
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

View File

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

View File

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

View File

@@ -20,12 +20,13 @@ package com.velocitypowered.proxy.protocol.packet.chat.legacy;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable;
public class LegacyChatPacket implements MinecraftPacket {
public record LegacyChatPacket(String message, byte type, @Nullable UUID sender) implements MinecraftPacket {
public static final byte CHAT_TYPE = (byte) 0;
public static final byte SYSTEM_TYPE = (byte) 1;
@@ -34,65 +35,23 @@ public class LegacyChatPacket implements MinecraftPacket {
public static final int MAX_SERVERBOUND_MESSAGE_LENGTH = 256;
public static final UUID EMPTY_SENDER = new UUID(0, 0);
private @Nullable String message;
private byte type;
private @Nullable UUID sender;
public LegacyChatPacket() {
}
/**
* Creates a Chat packet.
*/
public LegacyChatPacket(String message, byte type, UUID sender) {
this.message = message;
this.type = type;
this.sender = sender;
}
/**
* Retrieves the Chat message.
*/
public String getMessage() {
if (message == null) {
throw new IllegalStateException("Message is not specified");
}
return message;
}
public void setMessage(String message) {
this.message = message;
}
public byte getType() {
return type;
}
public void setType(byte type) {
this.type = type;
}
public UUID getSenderUuid() {
return sender;
}
public void setSenderUuid(UUID sender) {
this.sender = sender;
}
@Override
public String toString() {
return "Chat{"
+ "message='" + message + '\''
+ ", type=" + type
+ ", sender=" + sender
+ '}';
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<LegacyChatPacket> {
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
message = ProtocolUtils.readString(buf, direction == ProtocolUtils.Direction.CLIENTBOUND
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();
@@ -100,25 +59,20 @@ public class LegacyChatPacket implements MinecraftPacket {
sender = ProtocolUtils.readUuid(buf);
}
}
return new LegacyChatPacket(message, type, sender);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (message == null) {
throw new IllegalStateException("Message is not specified");
}
ProtocolUtils.writeString(buf, message);
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(type);
buf.writeByte(packet.type);
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) {
ProtocolUtils.writeUuid(buf, sender == null ? EMPTY_SENDER : sender);
ProtocolUtils.writeUuid(buf, packet.sender == null ? EMPTY_SENDER : packet.sender);
}
}
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

View File

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

View File

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

View File

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

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.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket {
import java.time.Instant;
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
this.command = ProtocolUtils.readString(buf, ProtocolUtils.DEFAULT_MAX_STRING_SIZE);
}
public final class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket {
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeString(buf, this.command);
public UnsignedPlayerCommandPacket(String command) {
super(command, Instant.EPOCH, 0L, new ArgumentSignatures(), null);
}
@Override
@@ -41,6 +38,7 @@ public class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket {
return this;
}
@Override
public boolean isSigned() {
return false;
}
@@ -56,4 +54,19 @@ public class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket {
"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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import net.kyori.adventure.key.Key;
public class ActiveFeaturesPacket implements MinecraftPacket {
private Key[] activeFeatures;
public ActiveFeaturesPacket(Key[] activeFeatures) {
this.activeFeatures = activeFeatures;
}
public record ActiveFeaturesPacket(Key[] activeFeatures) implements MinecraftPacket {
public ActiveFeaturesPacket() {
this.activeFeatures = new Key[0];
}
public void setActiveFeatures(Key[] activeFeatures) {
this.activeFeatures = activeFeatures;
this(new Key[0]);
}
public Key[] getActiveFeatures() {
return activeFeatures;
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
activeFeatures = ProtocolUtils.readKeyArray(buf);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
ProtocolUtils.writeKeyArray(buf, activeFeatures);
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import java.util.HashMap;
import java.util.Map;
public class ClientboundCustomReportDetailsPacket implements MinecraftPacket {
private Map<String, String> details;
public record ClientboundCustomReportDetailsPacket(Map<String, String> details) implements MinecraftPacket {
public ClientboundCustomReportDetailsPacket() {
}
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);
});
this(Map.of());
}
@Override
@@ -61,7 +37,28 @@ public class ClientboundCustomReportDetailsPacket implements MinecraftPacket {
return handler.handle(this);
}
public Map<String, String> getDetails() {
return details;
public static class Codec implements PacketCodec<ClientboundCustomReportDetailsPacket> {
@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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.List;
public class ClientboundServerLinksPacket implements MinecraftPacket {
private List<ServerLink> serverLinks;
public record ClientboundServerLinksPacket(List<ServerLink> serverLinks) implements MinecraftPacket {
public ClientboundServerLinksPacket() {
}
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);
}
this(List.of());
}
@Override
@@ -62,10 +39,6 @@ public class ClientboundServerLinksPacket implements MinecraftPacket {
return handler.handle(this);
}
public List<ServerLink> getServerLinks() {
return serverLinks;
}
public record ServerLink(int id, ComponentHolder displayName, String url) {
private static ServerLink read(ByteBuf buf, ProtocolVersion version) {
@@ -87,4 +60,28 @@ public class ClientboundServerLinksPacket implements MinecraftPacket {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf;
public class CodeOfConductAcceptPacket implements MinecraftPacket {
public final class CodeOfConductAcceptPacket implements MinecraftPacket {
public static final CodeOfConductAcceptPacket INSTANCE = new CodeOfConductAcceptPacket();
private CodeOfConductAcceptPacket() {
}
@Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
}
@Override
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.DefaultByteBufHolder;
public class CodeOfConductPacket extends DeferredByteBufHolder implements MinecraftPacket {
public class CodeOfConductPacket extends DefaultByteBufHolder implements MinecraftPacket {
public CodeOfConductPacket() {
super(null);
}
@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());
public CodeOfConductPacket(ByteBuf buf) {
super(buf);
}
@Override
@@ -46,7 +37,61 @@ public class CodeOfConductPacket extends DeferredByteBufHolder implements Minecr
}
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) {
return content().readableBytes();
public CodeOfConductPacket copy() {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class FinishedUpdatePacket implements MinecraftPacket {
public final class FinishedUpdatePacket implements MinecraftPacket {
public static final FinishedUpdatePacket INSTANCE = new FinishedUpdatePacket();
private FinishedUpdatePacket() {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
public boolean handle(MinecraftSessionHandler handler) {
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(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
public void encode(FinishedUpdatePacket packet, ByteBuf buf,
ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
}
@Override
public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
ProtocolVersion protocolVersion) {
return 0;
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

View File

@@ -20,43 +20,15 @@ package com.velocitypowered.proxy.protocol.packet.config;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf;
public class KnownPacksPacket implements MinecraftPacket {
public record KnownPacksPacket(KnownPack[] packs) implements MinecraftPacket {
private static final int MAX_LENGTH_PACKS = Integer.getInteger("velocity.max-known-packs", 64);
private static final QuietDecoderException TOO_MANY_PACKS =
new QuietDecoderException("too many known packs");
private KnownPack[] packs;
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
final int packCount = ProtocolUtils.readVarInt(buf);
if (direction == ProtocolUtils.Direction.SERVERBOUND && packCount > MAX_LENGTH_PACKS) {
throw TOO_MANY_PACKS;
}
final KnownPack[] packs = new KnownPack[packCount];
for (int i = 0; i < packCount; i++) {
packs[i] = KnownPack.read(buf);
}
this.packs = packs;
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, packs.length);
for (KnownPack pack : packs) {
pack.write(buf);
}
public KnownPacksPacket() {
this(new KnownPack[0]);
}
@Override
@@ -75,4 +47,37 @@ public class KnownPacksPacket implements MinecraftPacket {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.DefaultByteBufHolder;
public class RegistrySyncPacket extends DeferredByteBufHolder implements MinecraftPacket {
public final class RegistrySyncPacket extends DefaultByteBufHolder implements MinecraftPacket {
public RegistrySyncPacket() {
super(null);
}
// NBT change in 1.20.2 makes it difficult to parse this packet.
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
this.replace(buf.readRetainedSlice(buf.readableBytes()));
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
buf.writeBytes(content());
public RegistrySyncPacket(ByteBuf backing) {
super(backing);
}
@Override
@@ -49,8 +37,22 @@ public class RegistrySyncPacket extends DeferredByteBufHolder implements Minecra
return handler.handle(this);
}
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class StartUpdatePacket implements MinecraftPacket {
public final class StartUpdatePacket implements MinecraftPacket {
public static final StartUpdatePacket INSTANCE = new StartUpdatePacket();
private StartUpdatePacket() {
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
public boolean handle(MinecraftSessionHandler handler) {
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(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion protocolVersion) {
public void encode(StartUpdatePacket packet, ByteBuf buf,
ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
}
@Override
public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
ProtocolVersion protocolVersion) {
return 0;
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

View File

@@ -21,26 +21,33 @@ import com.google.common.collect.ImmutableMap;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf;
import java.util.Map;
public class TagsUpdatePacket implements MinecraftPacket {
public final class TagsUpdatePacket implements MinecraftPacket {
private Map<String, Map<String, int[]>> tags;
private final Map<String, Map<String, int[]>> tags;
public TagsUpdatePacket(Map<String, Map<String, int[]>> tags) {
this.tags = tags;
this.tags = ImmutableMap.copyOf(tags);
}
public TagsUpdatePacket() {
this.tags = Map.of();
public Map<String, Map<String, int[]>> getTags() {
return tags;
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
public static class Codec implements PacketCodec<TagsUpdatePacket> {
@Override
public TagsUpdatePacket decode(ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
ImmutableMap.Builder<String, Map<String, int[]>> builder = ImmutableMap.builder();
int size = ProtocolUtils.readVarInt(buf);
@@ -57,14 +64,14 @@ public class TagsUpdatePacket implements MinecraftPacket {
builder.put(key, innerBuilder.build());
}
tags = builder.build();
return new TagsUpdatePacket(builder.build());
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction,
public void encode(TagsUpdatePacket packet, ByteBuf buf, Direction direction,
ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, tags.size());
for (Map.Entry<String, Map<String, int[]>> entry : tags.entrySet()) {
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());
@@ -77,12 +84,8 @@ public class TagsUpdatePacket implements MinecraftPacket {
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
@Override
public int encodeSizeHint(Direction direction, ProtocolVersion version) {
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());
@@ -98,4 +101,5 @@ public class TagsUpdatePacket implements MinecraftPacket {
return size;
}
}
}

View File

@@ -19,9 +19,7 @@ package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf;
public abstract class GenericTitlePacket implements MinecraftPacket {
@@ -45,10 +43,9 @@ public abstract class GenericTitlePacket implements MinecraftPacket {
}
}
private final ActionType action;
private ActionType action;
protected void setAction(ActionType action) {
protected GenericTitlePacket(ActionType action) {
this.action = action;
}
@@ -60,76 +57,77 @@ public abstract class GenericTitlePacket implements MinecraftPacket {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public void setComponent(ComponentHolder component) {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public int getFadeIn() {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public void setFadeIn(int fadeIn) {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public int getStay() {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public void setStay(int stay) {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public int getFadeOut() {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public void setFadeOut(int fadeOut) {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
@Override
public final void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
throw new UnsupportedOperationException(); // encode only
}
/**
* Creates a version and type dependent TitlePacket.
* 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
* @return GenericTitlePacket instance that follows the invoker type/version
*/
public static GenericTitlePacket constructTitlePacket(ActionType type, ProtocolVersion version) {
GenericTitlePacket packet = null;
public static GenericTitlePacket createClearTitlePacket(ActionType type, ProtocolVersion version) {
if (type != ActionType.HIDE && type != ActionType.RESET) {
throw new IllegalArgumentException("createClearTitlePacket only accepts HIDE and RESET actions");
}
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) {
return new TitleClearPacket(type == ActionType.RESET);
} else {
return new LegacyTitlePacket(type, null, 0, 0, 0);
}
}
/**
* Creates a version and type dependent TitlePacket 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)) {
switch (type) {
case SET_ACTION_BAR:
packet = new TitleActionbarPacket();
break;
return new TitleActionbarPacket(component);
case SET_SUBTITLE:
packet = new TitleSubtitlePacket();
break;
case SET_TIMES:
packet = new TitleTimesPacket();
break;
return new TitleSubtitlePacket(component);
case SET_TITLE:
packet = new TitleTextPacket();
break;
case HIDE:
case RESET:
packet = new TitleClearPacket();
break;
return new TitleTextPacket(component);
default:
throw new IllegalArgumentException("Invalid ActionType");
throw new IllegalArgumentException("Invalid ActionType for component title: " + type);
}
} else {
packet = new LegacyTitlePacket();
return new LegacyTitlePacket(type, component, 0, 0, 0);
}
packet.setAction(type);
return packet;
}
/**
* Creates a version dependent TitlePacket for times.
*
* @param fadeIn Fade in time
* @param stay Stay time
* @param fadeOut Fade out time
* @param version Protocol version of the target player
* @return GenericTitlePacket instance that follows the invoker type/version
*/
public static GenericTitlePacket createTimesTitlePacket(int fadeIn, int stay, int fadeOut,
ProtocolVersion version) {
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) {
return new TitleTimesPacket(fadeIn, stay, fadeOut);
} else {
return new LegacyTitlePacket(ActionType.SET_TIMES, null, fadeIn, stay, fadeOut);
}
}
}

View File

@@ -19,52 +19,26 @@ package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class LegacyTitlePacket extends GenericTitlePacket {
public final class LegacyTitlePacket extends GenericTitlePacket {
private @Nullable ComponentHolder component;
private int fadeIn;
private int stay;
private int fadeOut;
private final @Nullable ComponentHolder component;
private final int fadeIn;
private final int stay;
private final int fadeOut;
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (version.lessThan(ProtocolVersion.MINECRAFT_1_11)
&& getAction() == ActionType.SET_ACTION_BAR) {
throw new IllegalStateException("Action bars are only supported on 1.11 and newer");
}
ProtocolUtils.writeVarInt(buf, getAction().getAction(version));
switch (getAction()) {
case SET_TITLE:
case SET_SUBTITLE:
case SET_ACTION_BAR:
if (component == null) {
throw new IllegalStateException("No component found for " + getAction());
}
component.write(buf);
break;
case SET_TIMES:
buf.writeInt(fadeIn);
buf.writeInt(stay);
buf.writeInt(fadeOut);
break;
case HIDE:
case RESET:
break;
default:
throw new UnsupportedOperationException("Unknown action " + getAction());
}
}
@Override
public void setAction(ActionType action) {
super.setAction(action);
public LegacyTitlePacket(ActionType action, @Nullable ComponentHolder component,
int fadeIn, int stay, int fadeOut) {
super(action);
this.component = component;
this.fadeIn = fadeIn;
this.stay = stay;
this.fadeOut = fadeOut;
}
@Override
@@ -72,44 +46,24 @@ public class LegacyTitlePacket extends GenericTitlePacket {
return component;
}
@Override
public void setComponent(@Nullable ComponentHolder component) {
this.component = component;
}
@Override
public int getFadeIn() {
return fadeIn;
}
@Override
public void setFadeIn(int fadeIn) {
this.fadeIn = fadeIn;
}
@Override
public int getStay() {
return stay;
}
@Override
public void setStay(int stay) {
this.stay = stay;
}
@Override
public int getFadeOut() {
return fadeOut;
}
@Override
public void setFadeOut(int fadeOut) {
this.fadeOut = fadeOut;
}
@Override
public String toString() {
return "GenericTitlePacket{"
return "LegacyTitlePacket{"
+ "action=" + getAction()
+ ", component='" + component + '\''
+ ", fadeIn=" + fadeIn
@@ -122,4 +76,43 @@ public class LegacyTitlePacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf;
public class TitleActionbarPacket extends GenericTitlePacket {
public final class TitleActionbarPacket extends GenericTitlePacket {
private ComponentHolder component;
private final ComponentHolder component;
public TitleActionbarPacket() {
setAction(ActionType.SET_TITLE);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
component.write(buf);
public TitleActionbarPacket(ComponentHolder component) {
super(ActionType.SET_ACTION_BAR);
this.component = component;
}
@Override
@@ -41,15 +38,10 @@ public class TitleActionbarPacket extends GenericTitlePacket {
return component;
}
@Override
public void setComponent(ComponentHolder component) {
this.component = component;
}
@Override
public String toString() {
return "TitleActionbarPacket{"
+ ", component='" + component + '\''
+ "component='" + component + '\''
+ '}';
}
@@ -57,4 +49,18 @@ public class TitleActionbarPacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class TitleClearPacket extends GenericTitlePacket {
public final class TitleClearPacket extends GenericTitlePacket {
public TitleClearPacket() {
setAction(ActionType.HIDE);
private final boolean reset;
public TitleClearPacket(boolean reset) {
super(reset ? ActionType.RESET : ActionType.HIDE);
this.reset = reset;
}
@Override
public void setAction(ActionType action) {
if (action != ActionType.HIDE && action != ActionType.RESET) {
throw new IllegalArgumentException("TitleClearPacket only accepts CLEAR and RESET actions");
}
super.setAction(action);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
buf.writeBoolean(getAction() == ActionType.RESET);
public boolean isReset() {
return reset;
}
@Override
public String toString() {
return "TitleClearPacket{"
+ ", resetTimes=" + (getAction() == ActionType.RESET)
+ "resetTimes=" + reset
+ '}';
}
@@ -52,4 +47,18 @@ public class TitleClearPacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf;
public class TitleSubtitlePacket extends GenericTitlePacket {
public final class TitleSubtitlePacket extends GenericTitlePacket {
private ComponentHolder component;
private final ComponentHolder component;
public TitleSubtitlePacket() {
setAction(ActionType.SET_SUBTITLE);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
component.write(buf);
public TitleSubtitlePacket(ComponentHolder component) {
super(ActionType.SET_SUBTITLE);
this.component = component;
}
@Override
@@ -41,15 +38,10 @@ public class TitleSubtitlePacket extends GenericTitlePacket {
return component;
}
@Override
public void setComponent(ComponentHolder component) {
this.component = component;
}
@Override
public String toString() {
return "TitleSubtitlePacket{"
+ ", component='" + component + '\''
+ "component='" + component + '\''
+ '}';
}
@@ -57,4 +49,18 @@ public class TitleSubtitlePacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import io.netty.buffer.ByteBuf;
public class TitleTextPacket extends GenericTitlePacket {
public final class TitleTextPacket extends GenericTitlePacket {
private ComponentHolder component;
private final ComponentHolder component;
public TitleTextPacket() {
setAction(ActionType.SET_TITLE);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
component.write(buf);
public TitleTextPacket(ComponentHolder component) {
super(ActionType.SET_TITLE);
this.component = component;
}
@Override
@@ -41,15 +38,10 @@ public class TitleTextPacket extends GenericTitlePacket {
return component;
}
@Override
public void setComponent(ComponentHolder component) {
this.component = component;
}
@Override
public String toString() {
return "TitleTextPacket{"
+ ", component='" + component + '\''
+ "component='" + component + '\''
+ '}';
}
@@ -57,4 +49,18 @@ public class TitleTextPacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) {
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.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.PacketCodec;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class TitleTimesPacket extends GenericTitlePacket {
public final class TitleTimesPacket extends GenericTitlePacket {
private int fadeIn;
private int stay;
private int fadeOut;
private final int fadeIn;
private final int stay;
private final int fadeOut;
public TitleTimesPacket() {
setAction(ActionType.SET_TIMES);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
buf.writeInt(fadeIn);
buf.writeInt(stay);
buf.writeInt(fadeOut);
public TitleTimesPacket(int fadeIn, int stay, int fadeOut) {
super(ActionType.SET_TIMES);
this.fadeIn = fadeIn;
this.stay = stay;
this.fadeOut = fadeOut;
}
@Override
@@ -44,35 +41,20 @@ public class TitleTimesPacket extends GenericTitlePacket {
return fadeIn;
}
@Override
public void setFadeIn(int fadeIn) {
this.fadeIn = fadeIn;
}
@Override
public int getStay() {
return stay;
}
@Override
public void setStay(int stay) {
this.stay = stay;
}
@Override
public int getFadeOut() {
return fadeOut;
}
@Override
public void setFadeOut(int fadeOut) {
this.fadeOut = fadeOut;
}
@Override
public String toString() {
return "TitleTimesPacket{"
+ ", fadeIn=" + fadeIn
+ "fadeIn=" + fadeIn
+ ", stay=" + stay
+ ", fadeOut=" + fadeOut
+ '}';
@@ -82,4 +64,20 @@ public class TitleTimesPacket extends GenericTitlePacket {
public boolean handle(MinecraftSessionHandler handler) {
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
public void activated() {
HandshakePacket handshake = new HandshakePacket();
handshake.setIntent(HandshakeIntent.STATUS);
handshake.setServerAddress(this.virtualHostString == null || this.virtualHostString.isEmpty()
? server.getServerInfo().getAddress().getHostString() : this.virtualHostString);
handshake.setPort(server.getServerInfo().getAddress().getPort());
handshake.setProtocolVersion(version);
String serverAddress = this.virtualHostString == null || this.virtualHostString.isEmpty()
? server.getServerInfo().getAddress().getHostString() : this.virtualHostString;
HandshakePacket handshake = new HandshakePacket(version, serverAddress,
server.getServerInfo().getAddress().getPort(), HandshakeIntent.STATUS);
connection.delayedWrite(handshake);
connection.setActiveSessionHandler(StateRegistry.STATUS);

View File

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