Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-12-24 23:30:26 +01:00
Merge branch 'dev/1.1.0' into feature/flush-consolidation-v2
Dieser Commit ist enthalten in:
Commit
983f3012a6
11
README.md
11
README.md
@ -35,14 +35,3 @@ 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.
|
|
||||||
|
@ -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}"
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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.
|
||||||
*
|
*
|
||||||
|
@ -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'
|
||||||
|
@ -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(asHeapBuf.array(), asHeapBuf.arrayOffset(), inBytes, outBuf);
|
cipher.update(asBytes, 0, 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(
|
||||||
|
cipher.update(asHeapBuf.array(), asHeapBuf.arrayOffset() + asHeapBuf.readerIndex(),
|
||||||
|
inBytes, out.array(), out.arrayOffset() + out.writerIndex()));
|
||||||
return out;
|
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
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
@ -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) {
|
||||||
|
player.getConnection()
|
||||||
.delayedWrite(TitlePacket.resetForProtocolVersion(player.getProtocolVersion()));
|
.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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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<>();
|
||||||
|
@ -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,6 +194,155 @@ 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;
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
|
||||||
publicKey = ProtocolUtils.readByteArray(buf, 256);
|
publicKey = ProtocolUtils.readByteArray(buf, 256);
|
||||||
verifyToken = ProtocolUtils.readByteArray(buf, 16);
|
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);
|
||||||
|
|
||||||
|
if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
|
||||||
ProtocolUtils.writeByteArray(buf, publicKey);
|
ProtocolUtils.writeByteArray(buf, publicKey);
|
||||||
ProtocolUtils.writeByteArray(buf, verifyToken);
|
ProtocolUtils.writeByteArray(buf, verifyToken);
|
||||||
|
} else {
|
||||||
|
ProtocolUtils.writeByteArray17(publicKey, buf, false);
|
||||||
|
ProtocolUtils.writeByteArray17(verifyToken, buf, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -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) {
|
||||||
|
if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
|
||||||
this.sharedSecret = ProtocolUtils.readByteArray(buf, 256);
|
this.sharedSecret = ProtocolUtils.readByteArray(buf, 256);
|
||||||
this.verifyToken = ProtocolUtils.readByteArray(buf, 128);
|
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) {
|
||||||
|
if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
|
||||||
ProtocolUtils.writeByteArray(buf, sharedSecret);
|
ProtocolUtils.writeByteArray(buf, sharedSecret);
|
||||||
ProtocolUtils.writeByteArray(buf, verifyToken);
|
ProtocolUtils.writeByteArray(buf, verifyToken);
|
||||||
|
} else {
|
||||||
|
ProtocolUtils.writeByteArray17(sharedSecret, buf, false);
|
||||||
|
ProtocolUtils.writeByteArray17(verifyToken, buf, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -116,8 +116,10 @@ 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);
|
||||||
}
|
}
|
||||||
|
if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
|
||||||
this.reducedDebugInfo = buf.readBoolean();
|
this.reducedDebugInfo = buf.readBoolean();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
||||||
@ -137,10 +139,12 @@ 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
|
||||||
public boolean handle(MinecraftSessionHandler handler) {
|
public boolean handle(MinecraftSessionHandler handler) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,6 +44,7 @@ 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) {
|
||||||
|
if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
|
||||||
action = ProtocolUtils.readVarInt(buf);
|
action = ProtocolUtils.readVarInt(buf);
|
||||||
int length = ProtocolUtils.readVarInt(buf);
|
int length = ProtocolUtils.readVarInt(buf);
|
||||||
|
|
||||||
@ -73,6 +75,13 @@ public class PlayerListItem implements MinecraftPacket {
|
|||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable Component readOptionalComponent(ByteBuf buf) {
|
private static @Nullable Component readOptionalComponent(ByteBuf buf) {
|
||||||
@ -84,6 +93,7 @@ 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) {
|
||||||
|
if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
|
||||||
ProtocolUtils.writeVarInt(buf, action);
|
ProtocolUtils.writeVarInt(buf, action);
|
||||||
ProtocolUtils.writeVarInt(buf, items.size());
|
ProtocolUtils.writeVarInt(buf, items.size());
|
||||||
for (Item item : items) {
|
for (Item item : items) {
|
||||||
@ -113,6 +123,18 @@ public class PlayerListItem implements MinecraftPacket {
|
|||||||
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
|
||||||
this.replace(buf.readRetainedSlice(buf.readableBytes()));
|
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);
|
||||||
}
|
}
|
||||||
|
if (version.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
|
||||||
buf.writeBytes(content());
|
buf.writeBytes(content());
|
||||||
|
} else {
|
||||||
|
ProtocolUtils.writeByteBuf17(content(), buf, true); // True for Forge support
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -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) {
|
||||||
|
if (version.compareTo(ProtocolVersion.MINECRAFT_1_7_6) >= 0) {
|
||||||
uuid = UUID.fromString(ProtocolUtils.readString(buf, 36));
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,12 +84,14 @@ 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();
|
||||||
}
|
}
|
||||||
|
if (version.compareTo(MINECRAFT_1_8) >= 0) {
|
||||||
this.hasPosition = buf.readBoolean();
|
this.hasPosition = buf.readBoolean();
|
||||||
if (hasPosition) {
|
if (hasPosition) {
|
||||||
this.position = buf.readLong();
|
this.position = buf.readLong();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
||||||
@ -104,12 +107,14 @@ 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);
|
||||||
}
|
}
|
||||||
|
if (version.compareTo(MINECRAFT_1_8) >= 0) {
|
||||||
buf.writeBoolean(hasPosition);
|
buf.writeBoolean(hasPosition);
|
||||||
if (hasPosition) {
|
if (hasPosition) {
|
||||||
buf.writeLong(position);
|
buf.writeLong(position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(MinecraftSessionHandler handler) {
|
public boolean handle(MinecraftSessionHandler handler) {
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
|
||||||
String currentBrand = ProtocolUtils.readString(message.content().slice());
|
String currentBrand = ProtocolUtils.readString(message.content().slice());
|
||||||
ProtocolUtils.writeString(rewrittenBuf, currentBrand + toAppend);
|
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);
|
||||||
}
|
}
|
||||||
|
@ -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,8 +87,10 @@ public class VelocityTabList implements TabList {
|
|||||||
items.add(PlayerListItem.Item.from(value));
|
items.add(PlayerListItem.Item.from(value));
|
||||||
}
|
}
|
||||||
entries.clear();
|
entries.clear();
|
||||||
|
if (!items.isEmpty()) {
|
||||||
connection.delayedWrite(new PlayerListItem(PlayerListItem.REMOVE_PLAYER, items));
|
connection.delayedWrite(new PlayerListItem(PlayerListItem.REMOVE_PLAYER, items));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<TabListEntry> getEntries() {
|
public Collection<TabListEntry> getEntries() {
|
||||||
@ -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;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren