3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-11-06 00:00:47 +01:00

Merge pull request #219 from Gabik21MC/master

1.7 Protocol Support
Dieser Commit ist enthalten in:
Andrew Steinborn 2019-09-01 17:36:34 -04:00 committet von GitHub
Commit e8d4db517d
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
24 geänderte Dateien mit 531 neuen und 128 gelöschten Zeilen

Datei anzeigen

@ -13,6 +13,8 @@ import java.util.Set;
public enum ProtocolVersion { public enum ProtocolVersion {
UNKNOWN(-1, "Unknown"), UNKNOWN(-1, "Unknown"),
LEGACY(-2, "Legacy"), LEGACY(-2, "Legacy"),
MINECRAFT_1_7_2(4, "1.7.2"),
MINECRAFT_1_7_6(5, "1.7.6"),
MINECRAFT_1_8(47, "1.8"), MINECRAFT_1_8(47, "1.8"),
MINECRAFT_1_9(107, "1.9"), MINECRAFT_1_9(107, "1.9"),
MINECRAFT_1_9_1(108, "1.9.1"), MINECRAFT_1_9_1(108, "1.9.1"),
@ -39,7 +41,7 @@ public enum ProtocolVersion {
/** /**
* Represents the lowest supported version. * Represents the lowest supported version.
*/ */
public static final ProtocolVersion MINIMUM_VERSION = MINECRAFT_1_8; public static final ProtocolVersion MINIMUM_VERSION = MINECRAFT_1_7_2;
/** /**
* Represents the highest supported version. * Represents the highest supported version.
*/ */

Datei anzeigen

@ -3,6 +3,7 @@ package com.velocitypowered.proxy.connection;
import com.velocitypowered.proxy.connection.backend.BackendConnectionPhases; import com.velocitypowered.proxy.connection.backend.BackendConnectionPhases;
import com.velocitypowered.proxy.connection.client.ClientConnectionPhases; import com.velocitypowered.proxy.connection.client.ClientConnectionPhases;
import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConnectionType; import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConnectionType;
import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeHandshakeClientPhase;
import com.velocitypowered.proxy.connection.util.ConnectionTypeImpl; import com.velocitypowered.proxy.connection.util.ConnectionTypeImpl;
/** /**
@ -24,6 +25,9 @@ public final class ConnectionTypes {
public static final ConnectionType VANILLA = public static final ConnectionType VANILLA =
new ConnectionTypeImpl(ClientConnectionPhases.VANILLA, BackendConnectionPhases.VANILLA); new ConnectionTypeImpl(ClientConnectionPhases.VANILLA, BackendConnectionPhases.VANILLA);
public static final ConnectionType UNDETERMINED_17 = new ConnectionTypeImpl(
LegacyForgeHandshakeClientPhase.NOT_STARTED, BackendConnectionPhases.UNKNOWN);
/** /**
* Indicates that the connection is a 1.8-1.12 Forge * Indicates that the connection is a 1.8-1.12 Forge
* connection. * connection.

Datei anzeigen

@ -102,8 +102,8 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
} }
if (PluginMessageUtil.isMcBrand(packet)) { if (PluginMessageUtil.isMcBrand(packet)) {
PluginMessage rewritten = PluginMessageUtil.rewriteMinecraftBrand(packet, PluginMessage rewritten = PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion(),
server.getVersion()); playerConnection.getProtocolVersion());
playerConnection.write(rewritten); playerConnection.write(rewritten);
return true; return true;
} }

Datei anzeigen

@ -1,6 +1,7 @@
package com.velocitypowered.proxy.connection.client; package com.velocitypowered.proxy.connection.client;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.constructChannelsPacket; import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.constructChannelsPacket;
import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.connection.PluginMessageEvent;
@ -177,7 +178,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
player.getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet)); player.getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet));
backendConn.write(packet.retain()); backendConn.write(packet.retain());
} else if (PluginMessageUtil.isMcBrand(packet)) { } else if (PluginMessageUtil.isMcBrand(packet)) {
backendConn.write(PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion())); backendConn.write(PluginMessageUtil
.rewriteMinecraftBrand(packet, server.getVersion(), player.getProtocolVersion()));
} else { } else {
if (serverConn.getPhase() == BackendConnectionPhases.IN_TRANSITION) { if (serverConn.getPhase() == BackendConnectionPhases.IN_TRANSITION) {
// We must bypass the currently-connected server when forwarding Forge packets. // We must bypass the currently-connected server when forwarding Forge packets.
@ -347,8 +349,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
} }
// Clear any title from the previous server. // Clear any title from the previous server.
player.getConnection() if (player.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) {
.delayedWrite(TitlePacket.resetForProtocolVersion(player.getProtocolVersion())); player.getConnection()
.delayedWrite(TitlePacket.resetForProtocolVersion(player.getProtocolVersion()));
}
// Flush everything // Flush everything
player.getConnection().flush(); player.getConnection().flush();

Datei anzeigen

@ -46,6 +46,7 @@ import com.velocitypowered.proxy.protocol.packet.TitlePacket;
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
import com.velocitypowered.proxy.server.VelocityRegisteredServer; import com.velocitypowered.proxy.server.VelocityRegisteredServer;
import com.velocitypowered.proxy.tablist.VelocityTabList; import com.velocitypowered.proxy.tablist.VelocityTabList;
import com.velocitypowered.proxy.tablist.VelocityTabListLegacy;
import com.velocitypowered.proxy.util.VelocityMessages; import com.velocitypowered.proxy.util.VelocityMessages;
import com.velocitypowered.proxy.util.collect.CappedSet; import com.velocitypowered.proxy.util.collect.CappedSet;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
@ -104,7 +105,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
ConnectedPlayer(VelocityServer server, GameProfile profile, ConnectedPlayer(VelocityServer server, GameProfile profile,
MinecraftConnection connection, @Nullable InetSocketAddress virtualHost) { MinecraftConnection connection, @Nullable InetSocketAddress virtualHost) {
this.server = server; this.server = server;
this.tabList = new VelocityTabList(connection); if (connection.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
this.tabList = new VelocityTabList(connection);
} else {
this.tabList = new VelocityTabListLegacy(connection);
}
this.profile = profile; this.profile = profile;
this.connection = connection; this.connection = connection;
this.virtualHost = virtualHost; this.virtualHost = virtualHost;

Datei anzeigen

@ -114,6 +114,10 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
if (handshake.getServerAddress().endsWith(LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN) if (handshake.getServerAddress().endsWith(LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN)
&& handshake.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_13) < 0) { && handshake.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_13) < 0) {
return ConnectionTypes.LEGACY_FORGE; return ConnectionTypes.LEGACY_FORGE;
} else if (handshake.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_7_6) <= 0) {
// 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;
} else { } else {
// For later: See if we can determine Forge 1.13+ here, else this will need to be UNDETERMINED // For later: See if we can determine Forge 1.13+ here, else this will need to be UNDETERMINED
// until later in the cycle (most likely determinable during the LOGIN phase) // until later in the cycle (most likely determinable during the LOGIN phase)

Datei anzeigen

@ -2,6 +2,7 @@ package com.velocitypowered.proxy.connection.client;
import static com.google.common.net.UrlEscapers.urlFormParameterEscaper; import static com.google.common.net.UrlEscapers.urlFormParameterEscaper;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
import static com.velocitypowered.proxy.VelocityServer.GSON; import static com.velocitypowered.proxy.VelocityServer.GSON;
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY; import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;
import static com.velocitypowered.proxy.connection.VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL; import static com.velocitypowered.proxy.connection.VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL;
@ -260,7 +261,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
} }
int threshold = server.getConfiguration().getCompressionThreshold(); int threshold = server.getConfiguration().getCompressionThreshold();
if (threshold >= 0) { if (threshold >= 0 && mcConnection.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) {
mcConnection.write(new SetCompression(threshold)); mcConnection.write(new SetCompression(threshold));
mcConnection.setCompressionThreshold(threshold); mcConnection.setCompressionThreshold(threshold);
} }

Datei anzeigen

@ -90,7 +90,7 @@ public enum LegacyForgeHandshakeBackendPhase implements BackendConnectionPhase {
@Nullable private final Integer packetToAdvanceOn; @Nullable private final Integer packetToAdvanceOn;
/** /**
* Creates an instance of the {@link LegacyForgeHandshakeClientPhase}. * Creates an instance of the {@link LegacyForgeHandshakeBackendPhase}.
* *
* @param packetToAdvanceOn The ID of the packet discriminator that indicates * @param packetToAdvanceOn The ID of the packet discriminator that indicates
* that the server has moved onto a new phase, and * that the server has moved onto a new phase, and

Datei anzeigen

@ -3,11 +3,11 @@ package com.velocitypowered.proxy.protocol;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -194,12 +194,161 @@ public enum ProtocolUtils {
return properties; return properties;
} }
private static final int FORGE_MAX_ARRAY_LENGTH = Integer.MAX_VALUE & 0x1FFF9A;
/**
* Reads an byte array for legacy version 1.7 from the specified {@code buf}
*
* @param buf the buffer to read from
* @return the read byte array
*/
public static byte[] readByteArray17(ByteBuf buf) {
// Read in a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for
// Forge only)
// No vanilla packet should give a 3 byte packet
int len = readExtendedForgeShort(buf);
Preconditions.checkArgument(len <= (FORGE_MAX_ARRAY_LENGTH),
"Cannot receive array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, len);
byte[] ret = new byte[len];
buf.readBytes(ret);
return ret;
}
/**
* Reads a retained {@link ByteBuf} slice of the specified {@code buf} with the 1.7 style length.
*
* @param buf the buffer to read from
* @return the retained slice
*/
public static ByteBuf readRetainedByteBufSlice17(ByteBuf buf) {
// Read in a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for
// Forge only)
// No vanilla packet should give a 3 byte packet
int len = readExtendedForgeShort(buf);
Preconditions.checkArgument(len <= (FORGE_MAX_ARRAY_LENGTH),
"Cannot receive array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, len);
return buf.readRetainedSlice(len);
}
/**
* Writes an byte array for legacy version 1.7 to the specified {@code buf}
*
* @param b array
* @param buf buf
* @param allowExtended forge
*/
public static void writeByteArray17(byte[] b, ByteBuf buf, boolean allowExtended) {
if (allowExtended) {
Preconditions
.checkArgument(b.length <= (FORGE_MAX_ARRAY_LENGTH),
"Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH,
b.length);
} else {
Preconditions.checkArgument(b.length <= Short.MAX_VALUE,
"Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.length);
}
// Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for
// Forge only)
// No vanilla packet should give a 3 byte packet, this method will still retain vanilla
// behaviour.
writeExtendedForgeShort(buf, b.length);
buf.writeBytes(b);
}
/**
* Writes an {@link ByteBuf} for legacy version 1.7 to the specified {@code buf}
*
* @param b array
* @param buf buf
* @param allowExtended forge
*/
public static void writeByteBuf17(ByteBuf b, ByteBuf buf, boolean allowExtended) {
if (allowExtended) {
Preconditions
.checkArgument(b.readableBytes() <= (FORGE_MAX_ARRAY_LENGTH),
"Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH,
b.readableBytes());
} else {
Preconditions.checkArgument(b.readableBytes() <= Short.MAX_VALUE,
"Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.readableBytes());
}
// Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for
// Forge only)
// No vanilla packet should give a 3 byte packet, this method will still retain vanilla
// behaviour.
writeExtendedForgeShort(buf, b.readableBytes());
buf.writeBytes(b);
}
/**
* Reads a Minecraft-style extended short from the specified {@code buf}.
*
* @param buf buf to write
* @return read extended short
*/
public static int readExtendedForgeShort(ByteBuf buf) {
int low = buf.readUnsignedShort();
int high = 0;
if ((low & 0x8000) != 0) {
low = low & 0x7FFF;
high = buf.readUnsignedByte();
}
return ((high & 0xFF) << 15) | low;
}
/**
* Writes a Minecraft-style extended short to the specified {@code buf}.
*
* @param buf buf to write
* @param toWrite the extended short to write
*/
public static void writeExtendedForgeShort(ByteBuf buf, int toWrite) {
int low = toWrite & 0x7FFF;
int high = (toWrite & 0x7F8000) >> 15;
if (high != 0) {
low = low | 0x8000;
}
buf.writeShort(low);
if (high != 0) {
buf.writeByte(high);
}
}
/**
* Reads a non length-prefixed string from the {@code buf}. We need this for the legacy 1.7
* version, being inconsistent when sending the brand.
*
* @param buf the buffer to read from
* @return the decoded string
*/
public static String readStringWithoutLength(ByteBuf buf) {
int length = buf.readableBytes();
int cap = DEFAULT_MAX_STRING_SIZE;
checkArgument(length >= 0, "Got a negative-length string (%s)", length);
// `cap` is interpreted as a UTF-8 character length. To cover the full Unicode plane, we must
// consider the length of a UTF-8 character, which can be up to a 4 bytes. We do an initial
// sanity check and then check again to make sure our optimistic guess was good.
checkArgument(length <= cap * 4, "Bad string size (got %s, maximum is %s)", length, cap);
checkState(buf.isReadable(length),
"Trying to read a string that is too long (wanted %s, only have %s)", length,
buf.readableBytes());
String str = buf.toString(buf.readerIndex(), length, StandardCharsets.UTF_8);
buf.skipBytes(length);
checkState(str.length() <= cap, "Got a too-long string (got %s, max %s)",
str.length(), cap);
return str;
}
public enum Direction { public enum Direction {
SERVERBOUND, SERVERBOUND,
CLIENTBOUND; CLIENTBOUND;
public StateRegistry.PacketRegistry.ProtocolRegistry getProtocolRegistry(StateRegistry state, public StateRegistry.PacketRegistry.ProtocolRegistry getProtocolRegistry(StateRegistry state,
ProtocolVersion version) { ProtocolVersion version) {
return (this == SERVERBOUND ? state.serverbound : state.clientbound) return (this == SERVERBOUND ? state.serverbound : state.clientbound)
.getProtocolRegistry(version); .getProtocolRegistry(version);
} }

Datei anzeigen

@ -5,6 +5,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12_1; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12_1;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9_4; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9_4;
@ -44,14 +45,12 @@ import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap; import io.netty.util.collection.IntObjectMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.Collections; import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public enum StateRegistry { public enum StateRegistry {
@ -59,20 +58,20 @@ public enum StateRegistry {
HANDSHAKE { HANDSHAKE {
{ {
serverbound.register(Handshake.class, Handshake::new, serverbound.register(Handshake.class, Handshake::new,
map(0x00, MINECRAFT_1_8, false)); map(0x00, MINECRAFT_1_7_2, false));
} }
}, },
STATUS { STATUS {
{ {
serverbound.register(StatusRequest.class, () -> StatusRequest.INSTANCE, serverbound.register(StatusRequest.class, () -> StatusRequest.INSTANCE,
map(0x00, MINECRAFT_1_8, false)); map(0x00, MINECRAFT_1_7_2, false));
serverbound.register(StatusPing.class, StatusPing::new, serverbound.register(StatusPing.class, StatusPing::new,
map(0x01, MINECRAFT_1_8, false)); map(0x01, MINECRAFT_1_7_2, false));
clientbound.register(StatusResponse.class, StatusResponse::new, clientbound.register(StatusResponse.class, StatusResponse::new,
map(0x00, MINECRAFT_1_8, false)); map(0x00, MINECRAFT_1_7_2, false));
clientbound.register(StatusPing.class, StatusPing::new, clientbound.register(StatusPing.class, StatusPing::new,
map(0x01, MINECRAFT_1_8, false)); map(0x01, MINECRAFT_1_7_2, false));
} }
}, },
PLAY { PLAY {
@ -81,33 +80,33 @@ public enum StateRegistry {
clientbound.fallback = false; clientbound.fallback = false;
serverbound.register(TabCompleteRequest.class, TabCompleteRequest::new, serverbound.register(TabCompleteRequest.class, TabCompleteRequest::new,
map(0x14, MINECRAFT_1_8, false), map(0x14, MINECRAFT_1_7_2, false),
map(0x01, MINECRAFT_1_9, false), map(0x01, MINECRAFT_1_9, false),
map(0x02, MINECRAFT_1_12, false), map(0x02, MINECRAFT_1_12, false),
map(0x01, MINECRAFT_1_12_1, false), map(0x01, MINECRAFT_1_12_1, false),
map(0x05, MINECRAFT_1_13, false), map(0x05, MINECRAFT_1_13, false),
map(0x06, MINECRAFT_1_14, false)); map(0x06, MINECRAFT_1_14, false));
serverbound.register(Chat.class, Chat::new, serverbound.register(Chat.class, Chat::new,
map(0x01, MINECRAFT_1_8, false), map(0x01, MINECRAFT_1_7_2, false),
map(0x02, MINECRAFT_1_9, false), map(0x02, MINECRAFT_1_9, false),
map(0x03, MINECRAFT_1_12, false), map(0x03, MINECRAFT_1_12, false),
map(0x02, MINECRAFT_1_12_1, false), map(0x02, MINECRAFT_1_12_1, false),
map(0x03, MINECRAFT_1_14, false)); map(0x03, MINECRAFT_1_14, false));
serverbound.register(ClientSettings.class, ClientSettings::new, serverbound.register(ClientSettings.class, ClientSettings::new,
map(0x15, MINECRAFT_1_8, false), map(0x15, MINECRAFT_1_7_2, false),
map(0x04, MINECRAFT_1_9, false), map(0x04, MINECRAFT_1_9, false),
map(0x05, MINECRAFT_1_12, false), map(0x05, MINECRAFT_1_12, false),
map(0x04, MINECRAFT_1_12_1, false), map(0x04, MINECRAFT_1_12_1, false),
map(0x05, MINECRAFT_1_14, false)); map(0x05, MINECRAFT_1_14, false));
serverbound.register(PluginMessage.class, PluginMessage::new, serverbound.register(PluginMessage.class, PluginMessage::new,
map(0x17, MINECRAFT_1_8, false), map(0x17, MINECRAFT_1_7_2, false),
map(0x09, MINECRAFT_1_9, false), map(0x09, MINECRAFT_1_9, false),
map(0x0A, MINECRAFT_1_12, false), map(0x0A, MINECRAFT_1_12, false),
map(0x09, MINECRAFT_1_12_1, false), map(0x09, MINECRAFT_1_12_1, false),
map(0x0A, MINECRAFT_1_13, false), map(0x0A, MINECRAFT_1_13, false),
map(0x0B, MINECRAFT_1_14, false)); map(0x0B, MINECRAFT_1_14, false));
serverbound.register(KeepAlive.class, KeepAlive::new, serverbound.register(KeepAlive.class, KeepAlive::new,
map(0x00, MINECRAFT_1_8, false), map(0x00, MINECRAFT_1_7_2, false),
map(0x0B, MINECRAFT_1_9, false), map(0x0B, MINECRAFT_1_9, false),
map(0x0C, MINECRAFT_1_12, false), map(0x0C, MINECRAFT_1_12, false),
map(0x0B, MINECRAFT_1_12_1, false), map(0x0B, MINECRAFT_1_12_1, false),
@ -123,37 +122,37 @@ public enum StateRegistry {
clientbound.register(BossBar.class, BossBar::new, clientbound.register(BossBar.class, BossBar::new,
map(0x0C, MINECRAFT_1_9, false)); map(0x0C, MINECRAFT_1_9, false));
clientbound.register(Chat.class, Chat::new, clientbound.register(Chat.class, Chat::new,
map(0x02, MINECRAFT_1_8, true), map(0x02, MINECRAFT_1_7_2, true),
map(0x0F, MINECRAFT_1_9, true), map(0x0F, MINECRAFT_1_9, true),
map(0x0E, MINECRAFT_1_13, true)); map(0x0E, MINECRAFT_1_13, true));
clientbound.register(TabCompleteResponse.class, TabCompleteResponse::new, clientbound.register(TabCompleteResponse.class, TabCompleteResponse::new,
map(0x3A, MINECRAFT_1_8, false), map(0x3A, MINECRAFT_1_7_2, false),
map(0x0E, MINECRAFT_1_9, false), map(0x0E, MINECRAFT_1_9, false),
map(0x10, MINECRAFT_1_13, false)); map(0x10, MINECRAFT_1_13, false));
clientbound.register(AvailableCommands.class, AvailableCommands::new, clientbound.register(AvailableCommands.class, AvailableCommands::new,
map(0x11, MINECRAFT_1_13, false)); map(0x11, MINECRAFT_1_13, false));
clientbound.register(PluginMessage.class, PluginMessage::new, clientbound.register(PluginMessage.class, PluginMessage::new,
map(0x3F, MINECRAFT_1_8, false), map(0x3F, MINECRAFT_1_7_2, false),
map(0x18, MINECRAFT_1_9, false), map(0x18, MINECRAFT_1_9, false),
map(0x19, MINECRAFT_1_13, false), map(0x19, MINECRAFT_1_13, false),
map(0x18, MINECRAFT_1_14, false)); map(0x18, MINECRAFT_1_14, false));
clientbound.register(Disconnect.class, Disconnect::new, clientbound.register(Disconnect.class, Disconnect::new,
map(0x40, MINECRAFT_1_8, false), map(0x40, MINECRAFT_1_7_2, false),
map(0x1A, MINECRAFT_1_9, false), map(0x1A, MINECRAFT_1_9, false),
map(0x1B, MINECRAFT_1_13, false), map(0x1B, MINECRAFT_1_13, false),
map(0x1A, MINECRAFT_1_14, false)); map(0x1A, MINECRAFT_1_14, false));
clientbound.register(KeepAlive.class, KeepAlive::new, clientbound.register(KeepAlive.class, KeepAlive::new,
map(0x00, MINECRAFT_1_8, false), map(0x00, MINECRAFT_1_7_2, false),
map(0x1F, MINECRAFT_1_9, false), map(0x1F, MINECRAFT_1_9, false),
map(0x21, MINECRAFT_1_13, false), map(0x21, MINECRAFT_1_13, false),
map(0x20, MINECRAFT_1_14, false)); map(0x20, MINECRAFT_1_14, false));
clientbound.register(JoinGame.class, JoinGame::new, clientbound.register(JoinGame.class, JoinGame::new,
map(0x01, MINECRAFT_1_8, false), map(0x01, MINECRAFT_1_7_2, false),
map(0x23, MINECRAFT_1_9, false), map(0x23, MINECRAFT_1_9, false),
map(0x25, MINECRAFT_1_13, false), map(0x25, MINECRAFT_1_13, false),
map(0x25, MINECRAFT_1_14, false)); map(0x25, MINECRAFT_1_14, false));
clientbound.register(Respawn.class, Respawn::new, clientbound.register(Respawn.class, Respawn::new,
map(0x07, MINECRAFT_1_8, true), map(0x07, MINECRAFT_1_7_2, true),
map(0x33, MINECRAFT_1_9, true), map(0x33, MINECRAFT_1_9, true),
map(0x34, MINECRAFT_1_12, true), map(0x34, MINECRAFT_1_12, true),
map(0x35, MINECRAFT_1_12_1, true), map(0x35, MINECRAFT_1_12_1, true),
@ -182,7 +181,7 @@ public enum StateRegistry {
map(0x4B, MINECRAFT_1_13, true), map(0x4B, MINECRAFT_1_13, true),
map(0x4F, MINECRAFT_1_14, true)); map(0x4F, MINECRAFT_1_14, true));
clientbound.register(PlayerListItem.class, PlayerListItem::new, clientbound.register(PlayerListItem.class, PlayerListItem::new,
map(0x38, MINECRAFT_1_8, false), map(0x38, MINECRAFT_1_7_2, false),
map(0x2D, MINECRAFT_1_9, false), map(0x2D, MINECRAFT_1_9, false),
map(0x2E, MINECRAFT_1_12_1, false), map(0x2E, MINECRAFT_1_12_1, false),
map(0x30, MINECRAFT_1_13, false), map(0x30, MINECRAFT_1_13, false),
@ -192,17 +191,17 @@ public enum StateRegistry {
LOGIN { LOGIN {
{ {
serverbound.register(ServerLogin.class, ServerLogin::new, serverbound.register(ServerLogin.class, ServerLogin::new,
map(0x00, MINECRAFT_1_8, false)); map(0x00, MINECRAFT_1_7_2, false));
serverbound.register(EncryptionResponse.class, EncryptionResponse::new, serverbound.register(EncryptionResponse.class, EncryptionResponse::new,
map(0x01, MINECRAFT_1_8, false)); map(0x01, MINECRAFT_1_7_2, false));
serverbound.register(LoginPluginResponse.class, LoginPluginResponse::new, serverbound.register(LoginPluginResponse.class, LoginPluginResponse::new,
map(0x02, MINECRAFT_1_13, false)); map(0x02, MINECRAFT_1_13, false));
clientbound.register(Disconnect.class, Disconnect::new, clientbound.register(Disconnect.class, Disconnect::new,
map(0x00, MINECRAFT_1_8, false)); map(0x00, MINECRAFT_1_7_2, false));
clientbound.register(EncryptionRequest.class, EncryptionRequest::new, clientbound.register(EncryptionRequest.class, EncryptionRequest::new,
map(0x01, MINECRAFT_1_8, false)); map(0x01, MINECRAFT_1_7_2, false));
clientbound.register(ServerLoginSuccess.class, ServerLoginSuccess::new, clientbound.register(ServerLoginSuccess.class, ServerLoginSuccess::new,
map(0x02, MINECRAFT_1_8, false)); map(0x02, MINECRAFT_1_7_2, false));
clientbound.register(SetCompression.class, SetCompression::new, clientbound.register(SetCompression.class, SetCompression::new,
map(0x03, MINECRAFT_1_8, false)); map(0x03, MINECRAFT_1_8, false));
clientbound.register(LoginPluginMessage.class, LoginPluginMessage::new, clientbound.register(LoginPluginMessage.class, LoginPluginMessage::new,
@ -246,7 +245,7 @@ public enum StateRegistry {
} }
<P extends MinecraftPacket> void register(Class<P> clazz, Supplier<P> packetSupplier, <P extends MinecraftPacket> void register(Class<P> clazz, Supplier<P> packetSupplier,
PacketMapping... mappings) { PacketMapping... mappings) {
if (mappings.length == 0) { if (mappings.length == 0) {
throw new IllegalArgumentException("At least one mapping must be provided."); throw new IllegalArgumentException("At least one mapping must be provided.");
} }
@ -382,8 +381,8 @@ public enum StateRegistry {
/** /**
* Creates a PacketMapping using the provided arguments. * Creates a PacketMapping using the provided arguments.
* *
* @param id Packet Id * @param id Packet Id
* @param version Protocol version * @param version Protocol version
* @param encodeOnly When true packet decoding will be disabled * @param encodeOnly When true packet decoding will be disabled
* @return PacketMapping with the provided arguments * @return PacketMapping with the provided arguments
*/ */

Datei anzeigen

@ -56,7 +56,7 @@ public class Chat implements MinecraftPacket {
@Override @Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
message = ProtocolUtils.readString(buf); message = ProtocolUtils.readString(buf);
if (direction == ProtocolUtils.Direction.CLIENTBOUND) { if (direction == ProtocolUtils.Direction.CLIENTBOUND && version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
type = buf.readByte(); type = buf.readByte();
} }
} }
@ -67,7 +67,7 @@ public class Chat implements MinecraftPacket {
throw new IllegalStateException("Message is not specified"); throw new IllegalStateException("Message is not specified");
} }
ProtocolUtils.writeString(buf, message); ProtocolUtils.writeString(buf, message);
if (direction == ProtocolUtils.Direction.CLIENTBOUND) { if (direction == ProtocolUtils.Direction.CLIENTBOUND && version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
buf.writeByte(type); buf.writeByte(type);
} }
} }

Datei anzeigen

@ -13,6 +13,7 @@ public class ClientSettings implements MinecraftPacket {
private byte viewDistance; private byte viewDistance;
private int chatVisibility; private int chatVisibility;
private boolean chatColors; private boolean chatColors;
private byte difficulty; // 1.7 Protocol
private short skinParts; private short skinParts;
private int mainHand; private int mainHand;
@ -98,6 +99,11 @@ public class ClientSettings implements MinecraftPacket {
this.viewDistance = buf.readByte(); this.viewDistance = buf.readByte();
this.chatVisibility = ProtocolUtils.readVarInt(buf); this.chatVisibility = ProtocolUtils.readVarInt(buf);
this.chatColors = buf.readBoolean(); this.chatColors = buf.readBoolean();
if (version.compareTo(ProtocolVersion.MINECRAFT_1_7_6) <= 0) {
this.difficulty = buf.readByte();
}
this.skinParts = buf.readUnsignedByte(); this.skinParts = buf.readUnsignedByte();
if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) {
@ -114,6 +120,11 @@ public class ClientSettings implements MinecraftPacket {
buf.writeByte(viewDistance); buf.writeByte(viewDistance);
ProtocolUtils.writeVarInt(buf, chatVisibility); ProtocolUtils.writeVarInt(buf, chatVisibility);
buf.writeBoolean(chatColors); buf.writeBoolean(chatColors);
if (version.compareTo(ProtocolVersion.MINECRAFT_1_7_6) <= 0) {
buf.writeByte(difficulty);
}
buf.writeByte(skinParts); buf.writeByte(skinParts);
if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) {

Datei anzeigen

@ -42,15 +42,27 @@ public class EncryptionRequest implements MinecraftPacket {
@Override @Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
this.serverId = ProtocolUtils.readString(buf, 20); this.serverId = ProtocolUtils.readString(buf, 20);
publicKey = ProtocolUtils.readByteArray(buf, 256);
verifyToken = ProtocolUtils.readByteArray(buf, 16); if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
publicKey = ProtocolUtils.readByteArray(buf, 256);
verifyToken = ProtocolUtils.readByteArray(buf, 16);
} else {
publicKey = ProtocolUtils.readByteArray17(buf);
verifyToken = ProtocolUtils.readByteArray17(buf);
}
} }
@Override @Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
ProtocolUtils.writeString(buf, this.serverId); ProtocolUtils.writeString(buf, this.serverId);
ProtocolUtils.writeByteArray(buf, publicKey);
ProtocolUtils.writeByteArray(buf, verifyToken); if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
ProtocolUtils.writeByteArray(buf, publicKey);
ProtocolUtils.writeByteArray(buf, verifyToken);
} else {
ProtocolUtils.writeByteArray17(publicKey, buf, false);
ProtocolUtils.writeByteArray17(verifyToken, buf, false);
}
} }
@Override @Override

Datei anzeigen

@ -40,14 +40,24 @@ public class EncryptionResponse implements MinecraftPacket {
@Override @Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
this.sharedSecret = ProtocolUtils.readByteArray(buf, 256); if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
this.verifyToken = ProtocolUtils.readByteArray(buf, 128); this.sharedSecret = ProtocolUtils.readByteArray(buf, 256);
this.verifyToken = ProtocolUtils.readByteArray(buf, 128);
} else {
this.sharedSecret = ProtocolUtils.readByteArray17(buf);
this.verifyToken = ProtocolUtils.readByteArray17(buf);
}
} }
@Override @Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
ProtocolUtils.writeByteArray(buf, sharedSecret); if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
ProtocolUtils.writeByteArray(buf, verifyToken); ProtocolUtils.writeByteArray(buf, sharedSecret);
ProtocolUtils.writeByteArray(buf, verifyToken);
} else {
ProtocolUtils.writeByteArray17(sharedSecret, buf, false);
ProtocolUtils.writeByteArray17(verifyToken, buf, false);
}
} }
@Override @Override

Datei anzeigen

@ -116,7 +116,9 @@ public class JoinGame implements MinecraftPacket {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_14) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_14) >= 0) {
this.viewDistance = ProtocolUtils.readVarInt(buf); this.viewDistance = ProtocolUtils.readVarInt(buf);
} }
this.reducedDebugInfo = buf.readBoolean(); if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
this.reducedDebugInfo = buf.readBoolean();
}
} }
@Override @Override
@ -137,9 +139,11 @@ public class JoinGame implements MinecraftPacket {
} }
ProtocolUtils.writeString(buf, levelType); ProtocolUtils.writeString(buf, levelType);
if (version.compareTo(ProtocolVersion.MINECRAFT_1_14) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_14) >= 0) {
ProtocolUtils.writeVarInt(buf,viewDistance); ProtocolUtils.writeVarInt(buf, viewDistance);
}
if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
buf.writeBoolean(reducedDebugInfo);
} }
buf.writeBoolean(reducedDebugInfo);
} }
@Override @Override

Datei anzeigen

@ -29,8 +29,10 @@ public class KeepAlive implements MinecraftPacket {
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_12_2) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_12_2) >= 0) {
randomId = buf.readLong(); randomId = buf.readLong();
} else { } else if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
randomId = ProtocolUtils.readVarInt(buf); randomId = ProtocolUtils.readVarInt(buf);
} else {
randomId = buf.readInt();
} }
} }
@ -38,8 +40,10 @@ public class KeepAlive implements MinecraftPacket {
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_12_2) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_12_2) >= 0) {
buf.writeLong(randomId); buf.writeLong(randomId);
} else { } else if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
ProtocolUtils.writeVarInt(buf, (int) randomId); ProtocolUtils.writeVarInt(buf, (int) randomId);
} else {
buf.writeInt((int) randomId);
} }
} }

Datei anzeigen

@ -13,6 +13,7 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import net.kyori.text.Component; import net.kyori.text.Component;
import net.kyori.text.serializer.gson.GsonComponentSerializer; import net.kyori.text.serializer.gson.GsonComponentSerializer;
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class PlayerListItem implements MinecraftPacket { public class PlayerListItem implements MinecraftPacket {
@ -43,35 +44,43 @@ public class PlayerListItem implements MinecraftPacket {
@Override @Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
action = ProtocolUtils.readVarInt(buf); if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
int length = ProtocolUtils.readVarInt(buf); action = ProtocolUtils.readVarInt(buf);
int length = ProtocolUtils.readVarInt(buf);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
Item item = new Item(ProtocolUtils.readUuid(buf)); Item item = new Item(ProtocolUtils.readUuid(buf));
items.add(item); items.add(item);
switch (action) { switch (action) {
case ADD_PLAYER: case ADD_PLAYER:
item.setName(ProtocolUtils.readString(buf)); item.setName(ProtocolUtils.readString(buf));
item.setProperties(ProtocolUtils.readProperties(buf)); item.setProperties(ProtocolUtils.readProperties(buf));
item.setGameMode(ProtocolUtils.readVarInt(buf)); item.setGameMode(ProtocolUtils.readVarInt(buf));
item.setLatency(ProtocolUtils.readVarInt(buf)); item.setLatency(ProtocolUtils.readVarInt(buf));
item.setDisplayName(readOptionalComponent(buf)); item.setDisplayName(readOptionalComponent(buf));
break; break;
case UPDATE_GAMEMODE: case UPDATE_GAMEMODE:
item.setGameMode(ProtocolUtils.readVarInt(buf)); item.setGameMode(ProtocolUtils.readVarInt(buf));
break; break;
case UPDATE_LATENCY: case UPDATE_LATENCY:
item.setLatency(ProtocolUtils.readVarInt(buf)); item.setLatency(ProtocolUtils.readVarInt(buf));
break; break;
case UPDATE_DISPLAY_NAME: case UPDATE_DISPLAY_NAME:
item.setDisplayName(readOptionalComponent(buf)); item.setDisplayName(readOptionalComponent(buf));
break; break;
case REMOVE_PLAYER: case REMOVE_PLAYER:
//Do nothing, all that is needed is the uuid //Do nothing, all that is needed is the uuid
break; break;
default: default:
throw new UnsupportedOperationException("Unknown action " + action); throw new UnsupportedOperationException("Unknown action " + action);
}
} }
} else {
Item item = new Item();
item.setName(ProtocolUtils.readString(buf));
action = buf.readBoolean() ? ADD_PLAYER : REMOVE_PLAYER;
item.setLatency(buf.readShort());
items.add(item);
} }
} }
@ -84,34 +93,47 @@ public class PlayerListItem implements MinecraftPacket {
@Override @Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
ProtocolUtils.writeVarInt(buf, action); if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
ProtocolUtils.writeVarInt(buf, items.size()); ProtocolUtils.writeVarInt(buf, action);
for (Item item : items) { ProtocolUtils.writeVarInt(buf, items.size());
ProtocolUtils.writeUuid(buf, item.getUuid()); for (Item item : items) {
switch (action) { ProtocolUtils.writeUuid(buf, item.getUuid());
case ADD_PLAYER: switch (action) {
ProtocolUtils.writeString(buf, item.getName()); case ADD_PLAYER:
ProtocolUtils.writeProperties(buf, item.getProperties()); ProtocolUtils.writeString(buf, item.getName());
ProtocolUtils.writeVarInt(buf, item.getGameMode()); ProtocolUtils.writeProperties(buf, item.getProperties());
ProtocolUtils.writeVarInt(buf, item.getLatency()); ProtocolUtils.writeVarInt(buf, item.getGameMode());
ProtocolUtils.writeVarInt(buf, item.getLatency());
writeDisplayName(buf, item.getDisplayName()); writeDisplayName(buf, item.getDisplayName());
break; break;
case UPDATE_GAMEMODE: case UPDATE_GAMEMODE:
ProtocolUtils.writeVarInt(buf, item.getGameMode()); ProtocolUtils.writeVarInt(buf, item.getGameMode());
break; break;
case UPDATE_LATENCY: case UPDATE_LATENCY:
ProtocolUtils.writeVarInt(buf, item.getLatency()); ProtocolUtils.writeVarInt(buf, item.getLatency());
break; break;
case UPDATE_DISPLAY_NAME: case UPDATE_DISPLAY_NAME:
writeDisplayName(buf, item.getDisplayName()); writeDisplayName(buf, item.getDisplayName());
break; break;
case REMOVE_PLAYER: case REMOVE_PLAYER:
//Do nothing, all that is needed is the uuid //Do nothing, all that is needed is the uuid
break; break;
default: default:
throw new UnsupportedOperationException("Unknown action " + action); throw new UnsupportedOperationException("Unknown action " + action);
}
} }
} else {
Item item = items.get(0);
if (item.getDisplayName() != null) {
String displayName = LegacyComponentSerializer.legacy().serialize(item.getDisplayName());
ProtocolUtils.writeString(buf,
displayName.length() > 16 ? displayName.substring(0, 16) : displayName);
} else {
ProtocolUtils.writeString(buf, item.getName());
}
buf.writeBoolean(action != REMOVE_PLAYER);
buf.writeShort(item.getLatency());
} }
} }
@ -136,6 +158,10 @@ public class PlayerListItem implements MinecraftPacket {
private int latency; private int latency;
private @Nullable Component displayName; private @Nullable Component displayName;
public Item() {
uuid = null;
}
public Item(UUID uuid) { public Item(UUID uuid) {
this.uuid = uuid; this.uuid = uuid;
} }
@ -149,7 +175,7 @@ public class PlayerListItem implements MinecraftPacket {
.setDisplayName(entry.getDisplayName().orElse(null)); .setDisplayName(entry.getDisplayName().orElse(null));
} }
public UUID getUuid() { public @Nullable UUID getUuid() {
return uuid; return uuid;
} }

Datei anzeigen

@ -50,7 +50,12 @@ public class PluginMessage extends DeferredByteBufHolder implements MinecraftPac
if (version.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0) {
this.channel = transformLegacyToModernChannel(this.channel); this.channel = transformLegacyToModernChannel(this.channel);
} }
this.replace(buf.readRetainedSlice(buf.readableBytes())); if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
this.replace(buf.readRetainedSlice(buf.readableBytes()));
} else {
this.replace(ProtocolUtils.readRetainedByteBufSlice17(buf));
}
} }
@Override @Override
@ -63,7 +68,12 @@ public class PluginMessage extends DeferredByteBufHolder implements MinecraftPac
} else { } else {
ProtocolUtils.writeString(buf, this.channel); ProtocolUtils.writeString(buf, this.channel);
} }
buf.writeBytes(content()); if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
buf.writeBytes(content());
} else {
ProtocolUtils.writeByteBuf17(content(), buf, true); // True for Forge support
}
} }
@Override @Override

Datei anzeigen

@ -1,6 +1,7 @@
package com.velocitypowered.proxy.protocol.packet; package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.util.UuidUtils;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
@ -45,7 +46,11 @@ public class ServerLoginSuccess implements MinecraftPacket {
@Override @Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
uuid = UUID.fromString(ProtocolUtils.readString(buf, 36)); if (version.compareTo(ProtocolVersion.MINECRAFT_1_7_6) >= 0) {
uuid = UUID.fromString(ProtocolUtils.readString(buf, 36));
} else {
uuid = UuidUtils.fromUndashed(ProtocolUtils.readString(buf, 32));
}
username = ProtocolUtils.readString(buf, 16); username = ProtocolUtils.readString(buf, 16);
} }

Datei anzeigen

@ -1,6 +1,7 @@
package com.velocitypowered.proxy.protocol.packet; package com.velocitypowered.proxy.protocol.packet;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; 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 static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
@ -83,9 +84,11 @@ public class TabCompleteRequest implements MinecraftPacket {
if (version.compareTo(MINECRAFT_1_9) >= 0) { if (version.compareTo(MINECRAFT_1_9) >= 0) {
this.assumeCommand = buf.readBoolean(); this.assumeCommand = buf.readBoolean();
} }
this.hasPosition = buf.readBoolean(); if (version.compareTo(MINECRAFT_1_8) >= 0) {
if (hasPosition) { this.hasPosition = buf.readBoolean();
this.position = buf.readLong(); if (hasPosition) {
this.position = buf.readLong();
}
} }
} }
} }
@ -104,9 +107,11 @@ public class TabCompleteRequest implements MinecraftPacket {
if (version.compareTo(MINECRAFT_1_9) >= 0) { if (version.compareTo(MINECRAFT_1_9) >= 0) {
buf.writeBoolean(assumeCommand); buf.writeBoolean(assumeCommand);
} }
buf.writeBoolean(hasPosition); if (version.compareTo(MINECRAFT_1_8) >= 0) {
if (hasPosition) { buf.writeBoolean(hasPosition);
buf.writeLong(position); if (hasPosition) {
buf.writeLong(position);
}
} }
} }
} }

Datei anzeigen

@ -11,9 +11,7 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.PluginMessage;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.util.ByteProcessor;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -127,7 +125,8 @@ public class PluginMessageUtil {
* @param version the proxy version * @param version the proxy version
* @return the rewritten plugin message * @return the rewritten plugin message
*/ */
public static PluginMessage rewriteMinecraftBrand(PluginMessage message, ProxyVersion version) { public static PluginMessage rewriteMinecraftBrand(PluginMessage message, ProxyVersion version,
ProtocolVersion protocolVersion) {
checkNotNull(message, "message"); checkNotNull(message, "message");
checkNotNull(version, "version"); checkNotNull(version, "version");
checkArgument(isMcBrand(message), "message is not a brand plugin message"); checkArgument(isMcBrand(message), "message is not a brand plugin message");
@ -135,8 +134,14 @@ public class PluginMessageUtil {
String toAppend = " (" + version.getName() + ")"; String toAppend = " (" + version.getName() + ")";
ByteBuf rewrittenBuf = Unpooled.buffer(); ByteBuf rewrittenBuf = Unpooled.buffer();
String currentBrand = ProtocolUtils.readString(message.content().slice());
ProtocolUtils.writeString(rewrittenBuf, currentBrand + toAppend); if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
String currentBrand = ProtocolUtils.readString(message.content().slice());
ProtocolUtils.writeString(rewrittenBuf, currentBrand + toAppend);
} else {
String currentBrand = ProtocolUtils.readStringWithoutLength(message.content().slice());
rewrittenBuf.writeBytes((currentBrand + toAppend).getBytes());
}
return new PluginMessage(message.getChannel(), rewrittenBuf); return new PluginMessage(message.getChannel(), rewrittenBuf);
} }

Datei anzeigen

@ -7,6 +7,7 @@ import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.protocol.packet.HeaderAndFooter; import com.velocitypowered.proxy.protocol.packet.HeaderAndFooter;
import com.velocitypowered.proxy.protocol.packet.PlayerListItem; import com.velocitypowered.proxy.protocol.packet.PlayerListItem;
import com.velocitypowered.proxy.protocol.packet.PlayerListItem.Item;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -20,8 +21,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public class VelocityTabList implements TabList { public class VelocityTabList implements TabList {
private final MinecraftConnection connection; protected final MinecraftConnection connection;
private final Map<UUID, VelocityTabListEntry> entries = new ConcurrentHashMap<>(); protected final Map<UUID, VelocityTabListEntry> entries = new ConcurrentHashMap<>();
public VelocityTabList(MinecraftConnection connection) { public VelocityTabList(MinecraftConnection connection) {
this.connection = connection; this.connection = connection;
@ -76,9 +77,9 @@ public class VelocityTabList implements TabList {
} }
/** /**
* Clears all entries from the tab list. Note that the entries are written with * Clears all entries from the tab list. Note that the entries are written with {@link
* {@link MinecraftConnection#delayedWrite(Object)}, so make sure to do an explicit * MinecraftConnection#delayedWrite(Object)}, so make sure to do an explicit {@link
* {@link MinecraftConnection#flush()}. * MinecraftConnection#flush()}.
*/ */
public void clearAll() { public void clearAll() {
List<PlayerListItem.Item> items = new ArrayList<>(); List<PlayerListItem.Item> items = new ArrayList<>();
@ -86,7 +87,9 @@ public class VelocityTabList implements TabList {
items.add(PlayerListItem.Item.from(value)); items.add(PlayerListItem.Item.from(value));
} }
entries.clear(); entries.clear();
connection.delayedWrite(new PlayerListItem(PlayerListItem.REMOVE_PLAYER, items)); if (!items.isEmpty()) {
connection.delayedWrite(new PlayerListItem(PlayerListItem.REMOVE_PLAYER, items));
}
} }
@Override @Override
@ -102,12 +105,14 @@ public class VelocityTabList implements TabList {
/** /**
* Processes a tab list entry packet from the backend. * Processes a tab list entry packet from the backend.
*
* @param packet the packet to process * @param packet the packet to process
*/ */
public void processBackendPacket(PlayerListItem packet) { public void processBackendPacket(PlayerListItem packet) {
// Packets are already forwarded on, so no need to do that here // Packets are already forwarded on, so no need to do that here
for (PlayerListItem.Item item : packet.getItems()) { for (PlayerListItem.Item item : packet.getItems()) {
UUID uuid = item.getUuid(); UUID uuid = item.getUuid();
if (packet.getAction() != PlayerListItem.ADD_PLAYER && !entries.containsKey(uuid)) { if (packet.getAction() != PlayerListItem.ADD_PLAYER && !entries.containsKey(uuid)) {
// Sometimes UPDATE_GAMEMODE is sent before ADD_PLAYER so don't want to warn here // Sometimes UPDATE_GAMEMODE is sent before ADD_PLAYER so don't want to warn here
continue; continue;

Datei anzeigen

@ -0,0 +1,20 @@
package com.velocitypowered.proxy.tablist;
import com.velocitypowered.api.proxy.player.TabListEntry;
import com.velocitypowered.api.util.GameProfile;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
public class VelocityTabListEntryLegacy extends VelocityTabListEntry {
VelocityTabListEntryLegacy(VelocityTabListLegacy tabList, GameProfile profile,
@Nullable Component displayName, int latency, int gameMode) {
super(tabList, profile, displayName, latency, gameMode);
}
@Override
public TabListEntry setDisplayName(@Nullable Component displayName) {
getTabList().removeEntry(getProfile().getId()); // We have to remove first if updating
return super.setDisplayName(displayName);
}
}

Datei anzeigen

@ -0,0 +1,118 @@
package com.velocitypowered.proxy.tablist;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.proxy.player.TabListEntry;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.protocol.packet.PlayerListItem;
import com.velocitypowered.proxy.protocol.packet.PlayerListItem.Item;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.kyori.text.Component;
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.text.serializer.plain.PlainComponentSerializer;
import org.checkerframework.checker.nullness.qual.Nullable;
public class VelocityTabListLegacy extends VelocityTabList {
private final Map<String, UUID> nameMapping = new ConcurrentHashMap<>();
public VelocityTabListLegacy(MinecraftConnection connection) {
super(connection);
}
@Override
public void setHeaderAndFooter(Component header, Component footer) {
}
@Override
public void clearHeaderAndFooter() {
}
@Override
public void addEntry(TabListEntry entry) {
super.addEntry(entry);
nameMapping.put(entry.getProfile().getName(), entry.getProfile().getId());
}
@Override
public Optional<TabListEntry> removeEntry(UUID uuid) {
Optional<TabListEntry> entry = super.removeEntry(uuid);
entry.map(TabListEntry::getProfile).map(GameProfile::getName).ifPresent(nameMapping::remove);
return entry;
}
@Override
public void clearAll() {
for (TabListEntry value : entries.values()) {
connection.delayedWrite(new PlayerListItem(PlayerListItem.REMOVE_PLAYER,
Collections.singletonList(PlayerListItem.Item.from(value))));
}
entries.clear();
}
@Override
public void processBackendPacket(PlayerListItem packet) {
Item item = packet.getItems().get(0); // Only one item per packet in 1.7
Component displayName = LegacyComponentSerializer.legacy().deserialize(item.getName());
String strippedName = PlainComponentSerializer.INSTANCE.serialize(displayName);
switch (packet.getAction()) {
case PlayerListItem.ADD_PLAYER:
if (nameMapping.containsKey(strippedName)) { // ADD_PLAYER also used for updating ping
VelocityTabListEntry entry = entries.get(nameMapping.get(strippedName));
if (entry != null) {
entry.setLatency(item.getLatency());
}
} else {
UUID uuid = UUID.randomUUID(); // Use a fake uuid to preserve function of custom entries
nameMapping.put(strippedName, uuid);
entries.put(uuid, (VelocityTabListEntry) TabListEntry.builder()
.tabList(this)
.profile(new GameProfile(uuid, strippedName, ImmutableList.of()))
.displayName(displayName)
.latency(item.getLatency())
.build());
}
break;
case PlayerListItem.REMOVE_PLAYER:
UUID removedUuid = nameMapping.remove(strippedName);
if (removedUuid != null) {
entries.remove(removedUuid);
}
break;
default:
// For 1.7 there is only add and remove
break;
}
}
@Override
void updateEntry(int action, TabListEntry entry) {
if (entries.containsKey(entry.getProfile().getId())) {
switch (action) {
case PlayerListItem.UPDATE_LATENCY:
case PlayerListItem.UPDATE_DISPLAY_NAME: // Add here because we removed beforehand
connection
.write(new PlayerListItem(PlayerListItem.ADD_PLAYER, // ADD_PLAYER also updates ping
Collections.singletonList(PlayerListItem.Item.from(entry))));
break;
default:
// Can't do anything else
break;
}
}
}
@Override
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
int gameMode) {
return new VelocityTabListEntryLegacy(this, profile, displayName, latency, gameMode);
}
}