13
0
geforkt von Mirrors/Velocity

Merge branch 'dev/1.1.0' into feature/flush-consolidation-v2

Dieser Commit ist enthalten in:
Andrew Steinborn 2019-09-07 00:34:08 -04:00
Commit 983f3012a6
37 geänderte Dateien mit 631 neuen und 254 gelöschten Zeilen

Datei anzeigen

@ -34,15 +34,4 @@ Once you've built Velocity, you can copy and run the `-all` JAR from
and you can configure it from there. and you can configure it from there.
Alternatively, you can get the proxy JAR from the [downloads](https://www.velocitypowered.com/downloads) Alternatively, you can get the proxy JAR from the [downloads](https://www.velocitypowered.com/downloads)
page. page.
## Status
Velocity is currently in beta. Production networks are successfully running
Velocity with many hundreds of concurrent players online, but your mileage
may vary.
Velocity supports Minecraft 1.8-1.14.4. Velocity is best supported with Paper
and SpongeVanilla. Minecraft Forge is fully supported but mod compatibility
may vary. Generally, Velocity will support many mods better than BungeeCord
or Waterfall do but compatibility can not always be ensured.

Datei anzeigen

@ -24,7 +24,7 @@ dependencies {
compile "net.kyori:text-serializer-plain:${textVersion}" compile "net.kyori:text-serializer-plain:${textVersion}"
compile 'com.moandjiezana.toml:toml4j:0.7.2' compile 'com.moandjiezana.toml:toml4j:0.7.2'
compile "org.slf4j:slf4j-api:${slf4jVersion}" compile "org.slf4j:slf4j-api:${slf4jVersion}"
compile 'com.google.inject:guice:4.2.0' compile 'com.google.inject:guice:4.2.2'
compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}" compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}"
compile "org.spongepowered:configurate-hocon:${configurateVersion}" compile "org.spongepowered:configurate-hocon:${configurateVersion}"

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

@ -65,6 +65,13 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage
*/ */
long getPing(); long getPing();
/**
* Returns the player's connection status.
*
* @return true if the player is authenticated with Mojang servers
*/
boolean isOnlineMode();
/** /**
* Sends a chat message to the player's client. * Sends a chat message to the player's client.
* *

Datei anzeigen

@ -20,7 +20,7 @@ allprojects {
ext { ext {
// dependency versions // dependency versions
textVersion = '3.0.1' textVersion = '3.0.2'
junitVersion = '5.3.0-M1' junitVersion = '5.3.0-M1'
slf4jVersion = '1.7.25' slf4jVersion = '1.7.25'
log4jVersion = '2.11.2' log4jVersion = '2.11.2'

Datei anzeigen

@ -2,9 +2,8 @@ package com.velocitypowered.natives.encryption;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.FastThreadLocal;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
@ -24,13 +23,6 @@ public class JavaVelocityCipher implements VelocityCipher {
return new JavaVelocityCipher(false, key); return new JavaVelocityCipher(false, key);
} }
}; };
private static final int INITIAL_BUFFER_SIZE = 1024 * 8;
private static final FastThreadLocal<byte[]> inBufLocal = new FastThreadLocal<byte[]>() {
@Override
protected byte[] initialValue() {
return new byte[INITIAL_BUFFER_SIZE];
}
};
private final Cipher cipher; private final Cipher cipher;
private boolean disposed = false; private boolean disposed = false;
@ -46,20 +38,12 @@ public class JavaVelocityCipher implements VelocityCipher {
ensureNotDisposed(); ensureNotDisposed();
int inBytes = source.readableBytes(); int inBytes = source.readableBytes();
ByteBuf asHeapBuf = asHeapBuf(source); byte[] asBytes = ByteBufUtil.getBytes(source);
int outputSize = cipher.getOutputSize(inBytes); int outputSize = cipher.getOutputSize(inBytes);
if (!destination.hasArray()) { byte[] outBuf = new byte[outputSize];
byte[] outBuf = new byte[outputSize]; cipher.update(asBytes, 0, inBytes, outBuf);
cipher.update(asHeapBuf.array(), asHeapBuf.arrayOffset(), inBytes, outBuf); destination.writeBytes(outBuf);
destination.writeBytes(outBuf);
} else {
// If the destination we write to is an array, we can use the backing array directly.
destination.ensureWritable(outputSize);
int produced = cipher.update(asHeapBuf.array(), asHeapBuf.arrayOffset(), inBytes,
destination.array(), destination.arrayOffset());
destination.writerIndex(destination.writerIndex() + produced);
}
} }
@Override @Override
@ -67,28 +51,31 @@ public class JavaVelocityCipher implements VelocityCipher {
ensureNotDisposed(); ensureNotDisposed();
int inBytes = source.readableBytes(); int inBytes = source.readableBytes();
ByteBuf asHeapBuf = asHeapBuf(source); ByteBuf asHeapBuf = toHeap(source);
ByteBuf out = ctx.alloc().heapBuffer(cipher.getOutputSize(inBytes)); ByteBuf out = ctx.alloc().heapBuffer(cipher.getOutputSize(inBytes));
out.writerIndex(cipher.update(asHeapBuf.array(), asHeapBuf.arrayOffset(), inBytes, out.array(), try {
out.arrayOffset())); out.writerIndex(
return out; cipher.update(asHeapBuf.array(), asHeapBuf.arrayOffset() + asHeapBuf.readerIndex(),
inBytes, out.array(), out.arrayOffset() + out.writerIndex()));
return out;
} catch (ShortBufferException e) {
out.release();
throw e;
} finally {
asHeapBuf.release();
}
} }
private static ByteBuf asHeapBuf(ByteBuf source) { private static ByteBuf toHeap(ByteBuf src) {
if (source.hasArray()) { if (src.hasArray()) {
// If this byte buffer is backed by an array, we can just use this buffer directly. return src.retain();
return source;
} }
int inBytes = source.readableBytes(); // Copy into a temporary heap buffer. We could use a local buffer, but Netty pools all buffers,
byte[] inBuf = inBufLocal.get(); // so we'd lose more than we gain.
if (inBuf.length <= inBytes) { ByteBuf asHeapBuf = src.alloc().heapBuffer(src.readableBytes());
inBuf = new byte[inBytes]; asHeapBuf.writeBytes(src);
inBufLocal.set(inBuf); return asHeapBuf;
}
source.readBytes(inBuf, 0, inBytes);
return Unpooled.wrappedBuffer(inBuf, 0, inBytes);
} }
@Override @Override

Datei anzeigen

@ -1,12 +0,0 @@
package com.velocitypowered.natives.util;
public class NativeConstants {
/**
* The default preferred output buffer size for zlib.
*/
public static final int ZLIB_BUFFER_SIZE = 8192;
private NativeConstants() {
throw new AssertionError();
}
}

Datei anzeigen

@ -505,7 +505,7 @@ public class VelocityServer implements ProxyServer {
Preconditions.checkNotNull(component, "component"); Preconditions.checkNotNull(component, "component");
Chat chat = Chat.createClientbound(component); Chat chat = Chat.createClientbound(component);
for (ConnectedPlayer player : connectionsByUuid.values()) { for (ConnectedPlayer player : connectionsByUuid.values()) {
player.getMinecraftConnection().write(chat); player.getConnection().write(chat);
} }
} }

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

@ -5,20 +5,17 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.LiteralCommandNode;
import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants;
import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionMessages;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.packet.AvailableCommands; import com.velocitypowered.proxy.protocol.packet.AvailableCommands;
import com.velocitypowered.proxy.protocol.packet.AvailableCommands.ProtocolSuggestionProvider; import com.velocitypowered.proxy.protocol.packet.AvailableCommands.ProtocolSuggestionProvider;
import com.velocitypowered.proxy.protocol.packet.BossBar; import com.velocitypowered.proxy.protocol.packet.BossBar;
import com.velocitypowered.proxy.protocol.packet.Disconnect; import com.velocitypowered.proxy.protocol.packet.Disconnect;
import com.velocitypowered.proxy.protocol.packet.JoinGame;
import com.velocitypowered.proxy.protocol.packet.KeepAlive; import com.velocitypowered.proxy.protocol.packet.KeepAlive;
import com.velocitypowered.proxy.protocol.packet.PlayerListItem; import com.velocitypowered.proxy.protocol.packet.PlayerListItem;
import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.PluginMessage;
@ -39,7 +36,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
BackendPlaySessionHandler(VelocityServer server, VelocityServerConnection serverConn) { BackendPlaySessionHandler(VelocityServer server, VelocityServerConnection serverConn) {
this.server = server; this.server = server;
this.serverConn = serverConn; this.serverConn = serverConn;
this.playerConnection = serverConn.getPlayer().getMinecraftConnection(); this.playerConnection = serverConn.getPlayer().getConnection();
MinecraftSessionHandler psh = playerConnection.getSessionHandler(); MinecraftSessionHandler psh = playerConnection.getSessionHandler();
if (!(psh instanceof ClientPlaySessionHandler)) { if (!(psh instanceof ClientPlaySessionHandler)) {
@ -105,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

@ -84,13 +84,13 @@ public class TransitionSessionHandler implements MinecraftSessionHandler {
.whenCompleteAsync((x, error) -> { .whenCompleteAsync((x, error) -> {
// Strap on the ClientPlaySessionHandler if required. // Strap on the ClientPlaySessionHandler if required.
ClientPlaySessionHandler playHandler; ClientPlaySessionHandler playHandler;
if (serverConn.getPlayer().getMinecraftConnection().getSessionHandler() if (serverConn.getPlayer().getConnection().getSessionHandler()
instanceof ClientPlaySessionHandler) { instanceof ClientPlaySessionHandler) {
playHandler = (ClientPlaySessionHandler) serverConn.getPlayer().getMinecraftConnection() playHandler = (ClientPlaySessionHandler) serverConn.getPlayer().getConnection()
.getSessionHandler(); .getSessionHandler();
} else { } else {
playHandler = new ClientPlaySessionHandler(server, serverConn.getPlayer()); playHandler = new ClientPlaySessionHandler(server, serverConn.getPlayer());
serverConn.getPlayer().getMinecraftConnection().setSessionHandler(playHandler); serverConn.getPlayer().getConnection().setSessionHandler(playHandler);
} }
playHandler.handleBackendJoinGame(packet, serverConn); playHandler.handleBackendJoinGame(packet, serverConn);
@ -167,7 +167,7 @@ public class TransitionSessionHandler implements MinecraftSessionHandler {
return true; return true;
} }
serverConn.getPlayer().getMinecraftConnection().write(packet.retain()); serverConn.getPlayer().getConnection().write(packet.retain());
return true; return true;
} }

Datei anzeigen

@ -79,7 +79,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
CompletableFuture<Impl> result = new CompletableFuture<>(); CompletableFuture<Impl> result = new CompletableFuture<>();
// Note: we use the event loop for the connection the player is on. This reduces context // Note: we use the event loop for the connection the player is on. This reduces context
// switches. // switches.
server.createBootstrap(proxyPlayer.getMinecraftConnection().eventLoop()) server.createBootstrap(proxyPlayer.getConnection().eventLoop())
.handler(new ChannelInitializer<Channel>() { .handler(new ChannelInitializer<Channel>() {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
@ -143,13 +143,13 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
PlayerInfoForwarding forwardingMode = server.getConfiguration().getPlayerInfoForwardingMode(); PlayerInfoForwarding forwardingMode = server.getConfiguration().getPlayerInfoForwardingMode();
// Initiate the handshake. // Initiate the handshake.
ProtocolVersion protocolVersion = proxyPlayer.getMinecraftConnection().getNextProtocolVersion(); ProtocolVersion protocolVersion = proxyPlayer.getConnection().getNextProtocolVersion();
Handshake handshake = new Handshake(); Handshake handshake = new Handshake();
handshake.setNextStatus(StateRegistry.LOGIN_ID); handshake.setNextStatus(StateRegistry.LOGIN_ID);
handshake.setProtocolVersion(protocolVersion); handshake.setProtocolVersion(protocolVersion);
if (forwardingMode == PlayerInfoForwarding.LEGACY) { if (forwardingMode == PlayerInfoForwarding.LEGACY) {
handshake.setServerAddress(createLegacyForwardingAddress()); handshake.setServerAddress(createLegacyForwardingAddress());
} else if (proxyPlayer.getMinecraftConnection().getType() == ConnectionTypes.LEGACY_FORGE) { } else if (proxyPlayer.getConnection().getType() == ConnectionTypes.LEGACY_FORGE) {
handshake.setServerAddress(handshake.getServerAddress() + HANDSHAKE_HOSTNAME_TOKEN); handshake.setServerAddress(handshake.getServerAddress() + HANDSHAKE_HOSTNAME_TOKEN);
} else { } else {
handshake.setServerAddress(registeredServer.getServerInfo().getAddress().getHostString()); handshake.setServerAddress(registeredServer.getServerInfo().getAddress().getHostString());

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;
@ -40,7 +41,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Queue; import java.util.Queue;
import java.util.UUID; import java.util.UUID;
import net.kyori.text.Component;
import net.kyori.text.TextComponent; import net.kyori.text.TextComponent;
import net.kyori.text.format.TextColor; import net.kyori.text.format.TextColor;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -78,7 +78,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
.getProtocolVersion()); .getProtocolVersion());
if (!channels.isEmpty()) { if (!channels.isEmpty()) {
PluginMessage register = constructChannelsPacket(player.getProtocolVersion(), channels); PluginMessage register = constructChannelsPacket(player.getProtocolVersion(), channels);
player.getMinecraftConnection().write(register); player.getConnection().write(register);
player.getKnownChannels().addAll(channels); player.getKnownChannels().addAll(channels);
} }
} }
@ -178,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.
@ -275,7 +276,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
public void writabilityChanged() { public void writabilityChanged() {
VelocityServerConnection serverConn = player.getConnectedServer(); VelocityServerConnection serverConn = player.getConnectedServer();
if (serverConn != null) { if (serverConn != null) {
boolean writable = player.getMinecraftConnection().getChannel().isWritable(); boolean writable = player.getConnection().getChannel().isWritable();
MinecraftConnection smc = serverConn.getConnection(); MinecraftConnection smc = serverConn.getConnection();
if (smc != null) { if (smc != null) {
smc.setAutoReading(writable); smc.setAutoReading(writable);
@ -295,7 +296,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
if (!spawned) { if (!spawned) {
// Nothing special to do with regards to spawning the player // Nothing special to do with regards to spawning the player
spawned = true; spawned = true;
player.getMinecraftConnection().delayedWrite(joinGame); player.getConnection().delayedWrite(joinGame);
// Required for Legacy Forge // Required for Legacy Forge
player.getPhase().onFirstJoin(player); player.getPhase().onFirstJoin(player);
@ -315,12 +316,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// Most notably, by having the client accept the join game packet, we can work around the need // Most notably, by having the client accept the join game packet, we can work around the need
// to perform entity ID rewrites, eliminating potential issues from rewriting packets and // to perform entity ID rewrites, eliminating potential issues from rewriting packets and
// improving compatibility with mods. // improving compatibility with mods.
player.getMinecraftConnection().delayedWrite(joinGame); player.getConnection().delayedWrite(joinGame);
int tempDim = joinGame.getDimension() == 0 ? -1 : 0; int tempDim = joinGame.getDimension() == 0 ? -1 : 0;
player.getMinecraftConnection().delayedWrite( player.getConnection().delayedWrite(
new Respawn(tempDim, joinGame.getDifficulty(), joinGame.getGamemode(), new Respawn(tempDim, joinGame.getDifficulty(), joinGame.getGamemode(),
joinGame.getLevelType())); joinGame.getLevelType()));
player.getMinecraftConnection().delayedWrite( player.getConnection().delayedWrite(
new Respawn(joinGame.getDimension(), joinGame.getDifficulty(), joinGame.getGamemode(), new Respawn(joinGame.getDimension(), joinGame.getDifficulty(), joinGame.getGamemode(),
joinGame.getLevelType())); joinGame.getLevelType()));
} }
@ -331,7 +332,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
BossBar deletePacket = new BossBar(); BossBar deletePacket = new BossBar();
deletePacket.setUuid(serverBossBar); deletePacket.setUuid(serverBossBar);
deletePacket.setAction(BossBar.REMOVE); deletePacket.setAction(BossBar.REMOVE);
player.getMinecraftConnection().delayedWrite(deletePacket); player.getConnection().delayedWrite(deletePacket);
} }
serverBossBars.clear(); serverBossBars.clear();
@ -348,11 +349,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
} }
// Clear any title from the previous server. // Clear any title from the previous server.
player.getMinecraftConnection() 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.getMinecraftConnection().flush(); player.getConnection().flush();
serverMc.flush(); serverMc.flush();
destination.completeJoin(); destination.completeJoin();
} }
@ -410,7 +413,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
resp.setLength(length); resp.setLength(length);
resp.getOffers().addAll(offers); resp.getOffers().addAll(offers);
player.getMinecraftConnection().write(resp); player.getConnection().write(resp);
return true; return true;
} }
@ -450,7 +453,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
response.getOffers().add(new Offer(offer, null)); response.getOffers().add(new Offer(offer, null));
} }
response.getOffers().sort(null); response.getOffers().sort(null);
player.getMinecraftConnection().write(response); player.getConnection().write(response);
} catch (Exception e) { } catch (Exception e) {
logger.error("Unable to provide tab list completions for {} for command '{}'", logger.error("Unable to provide tab list completions for {} for command '{}'",
player.getUsername(), player.getUsername(),
@ -469,8 +472,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
for (String s : e.getSuggestions()) { for (String s : e.getSuggestions()) {
response.getOffers().add(new Offer(s)); response.getOffers().add(new Offer(s));
} }
player.getMinecraftConnection().write(response); player.getConnection().write(response);
}, player.getMinecraftConnection().eventLoop()); }, player.getConnection().eventLoop());
} }
/** /**

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;
@ -58,7 +59,6 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import net.kyori.text.Component; import net.kyori.text.Component;
import net.kyori.text.TextComponent; import net.kyori.text.TextComponent;
@ -84,12 +84,13 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
/** /**
* The actual Minecraft connection. This is actually a wrapper object around the Netty channel. * The actual Minecraft connection. This is actually a wrapper object around the Netty channel.
*/ */
private final MinecraftConnection minecraftConnection; private final MinecraftConnection connection;
private final @Nullable InetSocketAddress virtualHost; private final @Nullable InetSocketAddress virtualHost;
private GameProfile profile; private GameProfile profile;
private PermissionFunction permissionFunction; private PermissionFunction permissionFunction;
private int tryIndex = 0; private int tryIndex = 0;
private long ping = -1; private long ping = -1;
private final boolean onlineMode;
private @Nullable VelocityServerConnection connectedServer; private @Nullable VelocityServerConnection connectedServer;
private @Nullable VelocityServerConnection connectionInFlight; private @Nullable VelocityServerConnection connectionInFlight;
private @Nullable PlayerSettings settings; private @Nullable PlayerSettings settings;
@ -102,16 +103,21 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
private @MonotonicNonNull List<String> serversToTry = null; private @MonotonicNonNull List<String> serversToTry = null;
ConnectedPlayer(VelocityServer server, GameProfile profile, ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection,
MinecraftConnection minecraftConnection, @Nullable InetSocketAddress virtualHost) { @Nullable InetSocketAddress virtualHost, boolean onlineMode) {
this.server = server; this.server = server;
this.tabList = new VelocityTabList(minecraftConnection); 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.minecraftConnection = minecraftConnection; this.connection = connection;
this.virtualHost = virtualHost; this.virtualHost = virtualHost;
this.permissionFunction = PermissionFunction.ALWAYS_UNDEFINED; this.permissionFunction = PermissionFunction.ALWAYS_UNDEFINED;
this.connectionPhase = minecraftConnection.getType().getInitialClientPhase(); this.connectionPhase = connection.getType().getInitialClientPhase();
this.knownChannels = CappedSet.create(MAX_PLUGIN_CHANNELS); this.knownChannels = CappedSet.create(MAX_PLUGIN_CHANNELS);
this.onlineMode = onlineMode;
} }
@Override @Override
@ -134,8 +140,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
return profile; return profile;
} }
public MinecraftConnection getMinecraftConnection() { public MinecraftConnection getConnection() {
return minecraftConnection; return connection;
} }
@Override @Override
@ -147,6 +153,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
this.ping = ping; this.ping = ping;
} }
@Override
public boolean isOnlineMode() {
return onlineMode;
}
@Override @Override
public PlayerSettings getPlayerSettings() { public PlayerSettings getPlayerSettings() {
return settings == null ? ClientSettingsWrapper.DEFAULT : this.settings; return settings == null ? ClientSettingsWrapper.DEFAULT : this.settings;
@ -170,7 +181,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
@Override @Override
public InetSocketAddress getRemoteAddress() { public InetSocketAddress getRemoteAddress() {
return (InetSocketAddress) minecraftConnection.getRemoteAddress(); return (InetSocketAddress) connection.getRemoteAddress();
} }
@Override @Override
@ -184,12 +195,12 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
@Override @Override
public boolean isActive() { public boolean isActive() {
return minecraftConnection.getChannel().isActive(); return connection.getChannel().isActive();
} }
@Override @Override
public ProtocolVersion getProtocolVersion() { public ProtocolVersion getProtocolVersion() {
return minecraftConnection.getProtocolVersion(); return connection.getProtocolVersion();
} }
@Override @Override
@ -205,7 +216,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
TitlePacket pkt = new TitlePacket(); TitlePacket pkt = new TitlePacket();
pkt.setAction(TitlePacket.SET_ACTION_BAR); pkt.setAction(TitlePacket.SET_ACTION_BAR);
pkt.setComponent(GsonComponentSerializer.INSTANCE.serialize(component)); pkt.setComponent(GsonComponentSerializer.INSTANCE.serialize(component));
minecraftConnection.write(pkt); connection.write(pkt);
return; return;
} else { } else {
// Due to issues with action bar packets, we'll need to convert the text message into a // Due to issues with action bar packets, we'll need to convert the text message into a
@ -221,7 +232,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
Chat chat = new Chat(); Chat chat = new Chat();
chat.setType(pos); chat.setType(pos);
chat.setMessage(json); chat.setMessage(json);
minecraftConnection.write(chat); connection.write(chat);
} }
@Override @Override
@ -258,23 +269,23 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
public void disconnect(Component reason) { public void disconnect(Component reason) {
logger.info("{} has disconnected: {}", this, logger.info("{} has disconnected: {}", this,
LegacyComponentSerializer.legacy().serialize(reason)); LegacyComponentSerializer.legacy().serialize(reason));
minecraftConnection.closeWith(Disconnect.create(reason)); connection.closeWith(Disconnect.create(reason));
} }
@Override @Override
public void sendTitle(Title title) { public void sendTitle(Title title) {
Preconditions.checkNotNull(title, "title"); Preconditions.checkNotNull(title, "title");
ProtocolVersion protocolVersion = minecraftConnection.getProtocolVersion(); ProtocolVersion protocolVersion = connection.getProtocolVersion();
if (title.equals(Titles.reset())) { if (title.equals(Titles.reset())) {
minecraftConnection.write(TitlePacket.resetForProtocolVersion(protocolVersion)); connection.write(TitlePacket.resetForProtocolVersion(protocolVersion));
} else if (title.equals(Titles.hide())) { } else if (title.equals(Titles.hide())) {
minecraftConnection.write(TitlePacket.hideForProtocolVersion(protocolVersion)); connection.write(TitlePacket.hideForProtocolVersion(protocolVersion));
} else if (title instanceof TextTitle) { } else if (title instanceof TextTitle) {
TextTitle tt = (TextTitle) title; TextTitle tt = (TextTitle) title;
if (tt.isResetBeforeSend()) { if (tt.isResetBeforeSend()) {
minecraftConnection.delayedWrite(TitlePacket.resetForProtocolVersion(protocolVersion)); connection.delayedWrite(TitlePacket.resetForProtocolVersion(protocolVersion));
} }
Optional<Component> titleText = tt.getTitle(); Optional<Component> titleText = tt.getTitle();
@ -282,7 +293,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
TitlePacket titlePkt = new TitlePacket(); TitlePacket titlePkt = new TitlePacket();
titlePkt.setAction(TitlePacket.SET_TITLE); titlePkt.setAction(TitlePacket.SET_TITLE);
titlePkt.setComponent(GsonComponentSerializer.INSTANCE.serialize(titleText.get())); titlePkt.setComponent(GsonComponentSerializer.INSTANCE.serialize(titleText.get()));
minecraftConnection.delayedWrite(titlePkt); connection.delayedWrite(titlePkt);
} }
Optional<Component> subtitleText = tt.getSubtitle(); Optional<Component> subtitleText = tt.getSubtitle();
@ -290,7 +301,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
TitlePacket titlePkt = new TitlePacket(); TitlePacket titlePkt = new TitlePacket();
titlePkt.setAction(TitlePacket.SET_SUBTITLE); titlePkt.setAction(TitlePacket.SET_SUBTITLE);
titlePkt.setComponent(GsonComponentSerializer.INSTANCE.serialize(subtitleText.get())); titlePkt.setComponent(GsonComponentSerializer.INSTANCE.serialize(subtitleText.get()));
minecraftConnection.delayedWrite(titlePkt); connection.delayedWrite(titlePkt);
} }
if (tt.areTimesSet()) { if (tt.areTimesSet()) {
@ -298,9 +309,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
timesPkt.setFadeIn(tt.getFadeIn()); timesPkt.setFadeIn(tt.getFadeIn());
timesPkt.setStay(tt.getStay()); timesPkt.setStay(tt.getStay());
timesPkt.setFadeOut(tt.getFadeOut()); timesPkt.setFadeOut(tt.getFadeOut());
minecraftConnection.delayedWrite(timesPkt); connection.delayedWrite(timesPkt);
} }
minecraftConnection.flush(); connection.flush();
} else { } else {
throw new IllegalArgumentException("Unknown title class " + title.getClass().getName()); throw new IllegalArgumentException("Unknown title class " + title.getClass().getName());
} }
@ -454,7 +465,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
} else { } else {
sendMessage(VelocityMessages.MOVED_TO_NEW_SERVER.append(friendlyReason)); sendMessage(VelocityMessages.MOVED_TO_NEW_SERVER.append(friendlyReason));
} }
}, minecraftConnection.eventLoop()); }, connection.eventLoop());
} else if (event.getResult() instanceof Notify) { } else if (event.getResult() instanceof Notify) {
Notify res = (Notify) event.getResult(); Notify res = (Notify) event.getResult();
if (event.kickedDuringServerConnect()) { if (event.kickedDuringServerConnect()) {
@ -466,7 +477,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
// In case someone gets creative, assume we want to disconnect the player. // In case someone gets creative, assume we want to disconnect the player.
disconnect(friendlyReason); disconnect(friendlyReason);
} }
}, minecraftConnection.eventLoop()); }, connection.eventLoop());
} }
/** /**
@ -579,7 +590,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
Preconditions.checkNotNull(identifier, "identifier"); Preconditions.checkNotNull(identifier, "identifier");
Preconditions.checkNotNull(data, "data"); Preconditions.checkNotNull(data, "data");
PluginMessage message = new PluginMessage(identifier.getId(), Unpooled.wrappedBuffer(data)); PluginMessage message = new PluginMessage(identifier.getId(), Unpooled.wrappedBuffer(data));
minecraftConnection.write(message); connection.write(message);
return true; return true;
} }
@ -598,7 +609,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
ResourcePackRequest request = new ResourcePackRequest(); ResourcePackRequest request = new ResourcePackRequest();
request.setUrl(url); request.setUrl(url);
request.setHash(""); request.setHash("");
minecraftConnection.write(request); connection.write(request);
} }
@Override @Override
@ -610,7 +621,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
ResourcePackRequest request = new ResourcePackRequest(); ResourcePackRequest request = new ResourcePackRequest();
request.setUrl(url); request.setUrl(url);
request.setHash(ByteBufUtil.hexDump(hash)); request.setHash(ByteBufUtil.hexDump(hash));
minecraftConnection.write(request); connection.write(request);
} }
/** /**
@ -619,10 +630,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
* ID last sent by the server. * ID last sent by the server.
*/ */
public void sendKeepAlive() { public void sendKeepAlive() {
if (minecraftConnection.getState() == StateRegistry.PLAY) { if (connection.getState() == StateRegistry.PLAY) {
KeepAlive keepAlive = new KeepAlive(); KeepAlive keepAlive = new KeepAlive();
keepAlive.setRandomId(ThreadLocalRandom.current().nextLong()); keepAlive.setRandomId(ThreadLocalRandom.current().nextLong());
minecraftConnection.write(keepAlive); connection.write(keepAlive);
} }
} }
@ -749,7 +760,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
} else if ((status != null && !status.isSuccessful())) { } else if ((status != null && !status.isSuccessful())) {
resetInFlightConnection(); resetInFlightConnection();
} }
}, minecraftConnection.eventLoop()) }, connection.eventLoop())
.thenApply(x -> x); .thenApply(x -> x);
} }
@ -782,7 +793,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
// The only remaining value is successful (no need to do anything!) // The only remaining value is successful (no need to do anything!)
break; break;
} }
}, minecraftConnection.eventLoop()) }, connection.eventLoop())
.thenApply(Result::isSuccessful); .thenApply(Result::isSuccessful);
} }

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;
@ -231,7 +232,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
// Initiate a regular connection and move over to it. // Initiate a regular connection and move over to it.
ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(), ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(),
mcConnection, mcConnection,
inbound.getVirtualHost().orElse(null)); inbound.getVirtualHost().orElse(null), onlineMode);
this.connectedPlayer = player; this.connectedPlayer = player;
if (!server.canRegisterConnection(player)) { if (!server.canRegisterConnection(player)) {
player.disconnect(VelocityMessages.ALREADY_CONNECTED); player.disconnect(VelocityMessages.ALREADY_CONNECTED);
@ -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
@ -114,7 +114,7 @@ public enum LegacyForgeHandshakeBackendPhase implements BackendConnectionPhase {
serverConnection.setConnectionPhase(newPhase); serverConnection.setConnectionPhase(newPhase);
// Write the packet to the player, we don't need it now. // Write the packet to the player, we don't need it now.
player.getMinecraftConnection().write(message.retain()); player.getConnection().write(message.retain());
return true; return true;
} }

Datei anzeigen

@ -135,7 +135,7 @@ public enum LegacyForgeHandshakeClientPhase implements ClientConnectionPhase {
COMPLETE(null) { COMPLETE(null) {
@Override @Override
public void resetConnectionPhase(ConnectedPlayer player) { public void resetConnectionPhase(ConnectedPlayer player) {
player.getMinecraftConnection().write(LegacyForgeUtil.resetPacket()); player.getConnection().write(LegacyForgeUtil.resetPacket());
player.setPhase(LegacyForgeHandshakeClientPhase.NOT_STARTED); player.setPhase(LegacyForgeHandshakeClientPhase.NOT_STARTED);
} }

Datei anzeigen

@ -34,7 +34,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public final class ConnectionManager { public final class ConnectionManager {
private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 21, private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20,
1 << 21); 1 << 21);
private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class); private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class);
private final Map<InetSocketAddress, Channel> endpoints = new HashMap<>(); private final Map<InetSocketAddress, Channel> endpoints = new HashMap<>();

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

@ -33,7 +33,7 @@ public class MinecraftCompressDecoder extends MessageToMessageDecoder<ByteBuf> {
return; return;
} }
checkFrame(expectedSize >= threshold, "Uncompressed size %s is greater than threshold %s", checkFrame(expectedSize >= threshold, "Uncompressed size %s is less than threshold %s",
expectedSize, threshold); expectedSize, threshold);
int initialCapacity = Math.min(expectedSize, MAXIMUM_UNCOMPRESSED_SIZE); int initialCapacity = Math.min(expectedSize, MAXIMUM_UNCOMPRESSED_SIZE);
ByteBuf compatibleIn = ensureCompatible(ctx.alloc(), compressor, in); ByteBuf compatibleIn = ensureCompatible(ctx.alloc(), compressor, in);

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,119 @@
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();
nameMapping.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);
}
}

Datei anzeigen

@ -271,6 +271,6 @@ public class VelocityBossBar implements com.velocitypowered.api.util.bossbar.Bos
private void sendPacket(Player player, MinecraftPacket packet) { private void sendPacket(Player player, MinecraftPacket packet) {
ConnectedPlayer connected = (ConnectedPlayer) player; ConnectedPlayer connected = (ConnectedPlayer) player;
connected.getMinecraftConnection().write(packet); connected.getConnection().write(packet);
} }
} }