Use package com.velocitypowered.proxy
This commit is contained in:
15
src/main/java/com/velocitypowered/proxy/Velocity.java
Normal file
15
src/main/java/com/velocitypowered/proxy/Velocity.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.velocitypowered.proxy;
|
||||
|
||||
import com.velocitypowered.proxy.connection.VelocityServer;
|
||||
|
||||
public class Velocity {
|
||||
public static void main(String... args) throws InterruptedException {
|
||||
VelocityServer server = new VelocityServer();
|
||||
server.initialize();
|
||||
|
||||
while (true) {
|
||||
// temporary until jline is added.
|
||||
Thread.sleep(999999);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,158 @@
|
||||
package com.velocitypowered.proxy.connection;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.proxy.protocol.PacketWrapper;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.compression.JavaVelocityCompressor;
|
||||
import com.velocitypowered.proxy.protocol.netty.MinecraftCompressDecoder;
|
||||
import com.velocitypowered.proxy.protocol.netty.MinecraftCompressEncoder;
|
||||
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
||||
import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder;
|
||||
import com.velocitypowered.proxy.protocol.packets.SetCompression;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
/**
|
||||
* A utility class to make working with the pipeline a little less painful and transparently handles certain Minecraft
|
||||
* protocol mechanics.
|
||||
*/
|
||||
public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
||||
private final Channel channel;
|
||||
private boolean closed;
|
||||
private StateRegistry state;
|
||||
private MinecraftSessionHandler sessionHandler;
|
||||
private int protocolVersion;
|
||||
|
||||
public MinecraftConnection(Channel channel) {
|
||||
this.channel = channel;
|
||||
this.closed = false;
|
||||
this.state = StateRegistry.HANDSHAKE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
if (sessionHandler != null) {
|
||||
sessionHandler.connected();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
if (sessionHandler != null) {
|
||||
sessionHandler.disconnected();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof PacketWrapper) {
|
||||
PacketWrapper pw = (PacketWrapper) msg;
|
||||
try {
|
||||
if (sessionHandler != null) {
|
||||
if (pw.getPacket() == null) {
|
||||
sessionHandler.handleUnknown(pw.getBuffer());
|
||||
} else {
|
||||
sessionHandler.handle(pw.getPacket());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
ReferenceCountUtil.release(pw.getBuffer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
if (ctx.channel().isActive()) {
|
||||
cause.printStackTrace();
|
||||
|
||||
if (sessionHandler != null) {
|
||||
sessionHandler.exception(cause);
|
||||
}
|
||||
|
||||
closed = true;
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void write(Object msg) {
|
||||
ensureOpen();
|
||||
channel.writeAndFlush(msg, channel.voidPromise());
|
||||
}
|
||||
|
||||
public void closeWith(Object msg) {
|
||||
ensureOpen();
|
||||
teardown();
|
||||
channel.writeAndFlush(msg).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
ensureOpen();
|
||||
teardown();
|
||||
channel.close();
|
||||
}
|
||||
|
||||
public void teardown() {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
public Channel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
public StateRegistry getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(StateRegistry state) {
|
||||
this.state = state;
|
||||
this.channel.pipeline().get(MinecraftEncoder.class).setState(state);
|
||||
this.channel.pipeline().get(MinecraftDecoder.class).setState(state);
|
||||
}
|
||||
|
||||
public int getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(int protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.channel.pipeline().get(MinecraftEncoder.class).setProtocolVersion(protocolVersion);
|
||||
this.channel.pipeline().get(MinecraftDecoder.class).setProtocolVersion(protocolVersion);
|
||||
}
|
||||
|
||||
public MinecraftSessionHandler getSessionHandler() {
|
||||
return sessionHandler;
|
||||
}
|
||||
|
||||
public void setSessionHandler(MinecraftSessionHandler sessionHandler) {
|
||||
this.sessionHandler = sessionHandler;
|
||||
}
|
||||
|
||||
private void ensureOpen() {
|
||||
Preconditions.checkState(!closed, "Connection is closed.");
|
||||
}
|
||||
|
||||
public void setCompressionThreshold(int threshold) {
|
||||
channel.writeAndFlush(new SetCompression(threshold), channel.voidPromise());
|
||||
|
||||
if (threshold == -1) {
|
||||
channel.pipeline().remove("compress-decoder");
|
||||
channel.pipeline().remove("compress-encoder");
|
||||
return;
|
||||
}
|
||||
|
||||
JavaVelocityCompressor compressor = new JavaVelocityCompressor();
|
||||
MinecraftCompressEncoder encoder = new MinecraftCompressEncoder(threshold, compressor);
|
||||
MinecraftCompressDecoder decoder = new MinecraftCompressDecoder(threshold, compressor);
|
||||
|
||||
channel.pipeline().addBefore("minecraft-decoder", "compress-decoder", decoder);
|
||||
channel.pipeline().addBefore("minecraft-encoder", "compress-encoder", encoder);
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package com.velocitypowered.proxy.connection;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public interface MinecraftSessionHandler {
|
||||
void handle(MinecraftPacket packet);
|
||||
|
||||
default void handleUnknown(ByteBuf buf) {
|
||||
// No-op: we'll release the buffer later.
|
||||
}
|
||||
|
||||
default void connected() {
|
||||
|
||||
}
|
||||
|
||||
default void disconnected() {
|
||||
|
||||
}
|
||||
|
||||
default void exception(Throwable throwable) {
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package com.velocitypowered.proxy.connection;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.netty.MinecraftPipelineUtils;
|
||||
import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
|
||||
public class VelocityServer {
|
||||
private static VelocityServer server;
|
||||
|
||||
private EventLoopGroup bossGroup;
|
||||
private EventLoopGroup childGroup;
|
||||
|
||||
public VelocityServer() {
|
||||
|
||||
}
|
||||
|
||||
public static VelocityServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
bossGroup = new NioEventLoopGroup();
|
||||
childGroup = new NioEventLoopGroup();
|
||||
server = this;
|
||||
new ServerBootstrap()
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.group(bossGroup, childGroup)
|
||||
.childHandler(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
MinecraftPipelineUtils.strapPipelineForServer(ch);
|
||||
|
||||
MinecraftConnection connection = new MinecraftConnection(ch);
|
||||
connection.setState(StateRegistry.HANDSHAKE);
|
||||
connection.setSessionHandler(new HandshakeSessionHandler(connection));
|
||||
ch.pipeline().addLast("handler", connection);
|
||||
}
|
||||
})
|
||||
.bind(26671)
|
||||
.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
System.out.println("Listening on " + future.channel().localAddress());
|
||||
} else {
|
||||
System.out.println("Can't bind to " + future.channel().localAddress());
|
||||
future.cause().printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Bootstrap initializeGenericBootstrap() {
|
||||
return new Bootstrap()
|
||||
.channel(NioSocketChannel.class)
|
||||
.group(childGroup);
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package com.velocitypowered.proxy.connection.backend;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.packets.Disconnect;
|
||||
import com.velocitypowered.proxy.protocol.packets.ServerLoginSuccess;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.packets.SetCompression;
|
||||
|
||||
public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
private final ServerConnection connection;
|
||||
|
||||
public LoginSessionHandler(ServerConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(MinecraftPacket packet) {
|
||||
if (packet instanceof Disconnect) {
|
||||
Disconnect disconnect = (Disconnect) packet;
|
||||
connection.disconnect();
|
||||
connection.getProxyPlayer().handleConnectionException(connection.getServerInfo(), disconnect);
|
||||
}
|
||||
|
||||
if (packet instanceof SetCompression) {
|
||||
System.out.println("Enabling compression on server connection, this is inefficient!");
|
||||
SetCompression sc = (SetCompression) packet;
|
||||
connection.getChannel().setCompressionThreshold(sc.getThreshold());
|
||||
}
|
||||
|
||||
if (packet instanceof ServerLoginSuccess) {
|
||||
// the player has been logged on.
|
||||
System.out.println("Player connected to remote server");
|
||||
connection.getChannel().setState(StateRegistry.PLAY);
|
||||
connection.getProxyPlayer().setConnectedServer(connection);
|
||||
connection.getProxyPlayer().getConnection().setSessionHandler(new com.velocitypowered.proxy.connection.client.PlaySessionHandler(connection.getProxyPlayer()));
|
||||
connection.getChannel().setSessionHandler(new PlaySessionHandler(connection));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package com.velocitypowered.proxy.connection.backend;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.packets.Disconnect;
|
||||
import com.velocitypowered.proxy.protocol.packets.Ping;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.kyori.text.TextComponent;
|
||||
import net.kyori.text.format.TextColor;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
|
||||
public class PlaySessionHandler implements MinecraftSessionHandler {
|
||||
private final ServerConnection connection;
|
||||
|
||||
public PlaySessionHandler(ServerConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(MinecraftPacket packet) {
|
||||
if (packet instanceof Ping) {
|
||||
// Make sure to reply back to the server so it doesn't think we're gone.
|
||||
connection.getChannel().write(packet);
|
||||
connection.getProxyPlayer().getConnection().write(packet);
|
||||
} else if (packet instanceof Disconnect) {
|
||||
// The server wants to disconnect us. TODO fallback handling
|
||||
Disconnect original = (Disconnect) packet;
|
||||
TextComponent reason = TextComponent.builder()
|
||||
.content("Disconnected from " + connection.getServerInfo().getName() + ":")
|
||||
.color(TextColor.RED)
|
||||
.append(TextComponent.of(" ", TextColor.WHITE))
|
||||
.append(ComponentSerializers.JSON.deserialize(original.getReason()))
|
||||
.build();
|
||||
connection.getProxyPlayer().close(reason);
|
||||
} else {
|
||||
// Just forward the packet on. We don't have anything to handle at this time.
|
||||
connection.getProxyPlayer().getConnection().write(packet);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleUnknown(ByteBuf buf) {
|
||||
connection.getProxyPlayer().getConnection().write(buf.retain());
|
||||
}
|
||||
}
|
@@ -0,0 +1,87 @@
|
||||
package com.velocitypowered.proxy.connection.backend;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.packets.Handshake;
|
||||
import com.velocitypowered.proxy.protocol.packets.ServerLogin;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.data.ServerInfo;
|
||||
import com.velocitypowered.proxy.protocol.netty.MinecraftPipelineUtils;
|
||||
import com.velocitypowered.proxy.connection.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import io.netty.channel.*;
|
||||
|
||||
public class ServerConnection {
|
||||
private final ServerInfo serverInfo;
|
||||
private final ConnectedPlayer proxyPlayer;
|
||||
private final VelocityServer server;
|
||||
private MinecraftConnection channel;
|
||||
|
||||
public ServerConnection(ServerInfo target, ConnectedPlayer proxyPlayer, VelocityServer server) {
|
||||
this.serverInfo = target;
|
||||
this.proxyPlayer = proxyPlayer;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
server.initializeGenericBootstrap()
|
||||
.handler(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
MinecraftPipelineUtils.strapPipelineForProxy(ch);
|
||||
|
||||
MinecraftConnection connection = new MinecraftConnection(ch);
|
||||
connection.setState(StateRegistry.HANDSHAKE);
|
||||
connection.setSessionHandler(new LoginSessionHandler(ServerConnection.this));
|
||||
ch.pipeline().addLast("handler", connection);
|
||||
}
|
||||
})
|
||||
.connect(serverInfo.getAddress())
|
||||
.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
channel = future.channel().pipeline().get(MinecraftConnection.class);
|
||||
|
||||
// Kick off the connection process
|
||||
startHandshake();
|
||||
} else {
|
||||
proxyPlayer.handleConnectionException(serverInfo, future.cause());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void startHandshake() {
|
||||
// Initiate a handshake.
|
||||
Handshake handshake = new Handshake();
|
||||
handshake.setNextStatus(2); // login
|
||||
handshake.setProtocolVersion(proxyPlayer.getConnection().getProtocolVersion());
|
||||
handshake.setServerAddress(serverInfo.getAddress().getHostString());
|
||||
handshake.setPort(serverInfo.getAddress().getPort());
|
||||
channel.write(handshake);
|
||||
|
||||
channel.setState(StateRegistry.LOGIN);
|
||||
|
||||
// Login
|
||||
ServerLogin login = new ServerLogin();
|
||||
login.setUsername(proxyPlayer.getUsername());
|
||||
channel.write(login);
|
||||
}
|
||||
|
||||
public ConnectedPlayer getProxyPlayer() {
|
||||
return proxyPlayer;
|
||||
}
|
||||
|
||||
public MinecraftConnection getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public ServerInfo getServerInfo() {
|
||||
return serverInfo;
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
channel.close();
|
||||
channel = null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
package com.velocitypowered.proxy.connection.client;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.packets.Chat;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.backend.ServerConnection;
|
||||
import com.velocitypowered.proxy.util.ThrowableUtils;
|
||||
import com.velocitypowered.proxy.data.ServerInfo;
|
||||
import com.velocitypowered.proxy.protocol.packets.Disconnect;
|
||||
import net.kyori.text.TextComponent;
|
||||
import net.kyori.text.format.TextColor;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ConnectedPlayer {
|
||||
private final String username;
|
||||
private final UUID uniqueId;
|
||||
private final MinecraftConnection connection;
|
||||
private ServerConnection connectedServer;
|
||||
|
||||
public ConnectedPlayer(String username, UUID uniqueId, MinecraftConnection connection) {
|
||||
this.username = username;
|
||||
this.uniqueId = uniqueId;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public UUID getUniqueId() {
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
public MinecraftConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public ServerConnection getConnectedServer() {
|
||||
return connectedServer;
|
||||
}
|
||||
|
||||
public void handleConnectionException(ServerInfo info, Throwable throwable) {
|
||||
String error = ThrowableUtils.briefDescription(throwable);
|
||||
Disconnect disconnect = new Disconnect();
|
||||
disconnect.setReason(ComponentSerializers.JSON.serialize(TextComponent.of(error, TextColor.RED)));
|
||||
handleConnectionException(info, disconnect);
|
||||
}
|
||||
|
||||
public void handleConnectionException(ServerInfo info, Disconnect disconnect) {
|
||||
TextComponent component = TextComponent.builder()
|
||||
.content("Exception connecting to server " + info.getName() + ": ")
|
||||
.color(TextColor.RED)
|
||||
.append(ComponentSerializers.JSON.deserialize(disconnect.getReason()))
|
||||
.build();
|
||||
|
||||
if (connectedServer == null) {
|
||||
// The player isn't yet connected to a server - we should disconnect them.
|
||||
Disconnect d = new Disconnect();
|
||||
d.setReason(ComponentSerializers.JSON.serialize(component));
|
||||
connection.closeWith(d);
|
||||
} else {
|
||||
Chat chat = new Chat();
|
||||
chat.setMessage(ComponentSerializers.JSON.serialize(component));
|
||||
connection.write(chat);
|
||||
}
|
||||
}
|
||||
|
||||
public void setConnectedServer(ServerConnection serverConnection) {
|
||||
this.connectedServer = serverConnection;
|
||||
}
|
||||
|
||||
public void close(TextComponent reason) {
|
||||
Disconnect disconnect = new Disconnect();
|
||||
disconnect.setReason(ComponentSerializers.JSON.serialize(reason));
|
||||
connection.closeWith(disconnect);
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package com.velocitypowered.proxy.connection.client;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.packets.Handshake;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
|
||||
public class HandshakeSessionHandler implements MinecraftSessionHandler {
|
||||
private final MinecraftConnection connection;
|
||||
|
||||
public HandshakeSessionHandler(MinecraftConnection connection) {
|
||||
this.connection = Preconditions.checkNotNull(connection, "connection");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(MinecraftPacket packet) {
|
||||
if (!(packet instanceof Handshake)) {
|
||||
throw new IllegalArgumentException("Did not expect packet " + packet.getClass().getName());
|
||||
}
|
||||
|
||||
Handshake handshake = (Handshake) packet;
|
||||
connection.setProtocolVersion(handshake.getProtocolVersion());
|
||||
switch (handshake.getNextStatus()) {
|
||||
case 1:
|
||||
// Status protocol
|
||||
connection.setState(StateRegistry.STATUS);
|
||||
connection.setSessionHandler(new StatusSessionHandler(connection));
|
||||
break;
|
||||
case 2:
|
||||
connection.setState(StateRegistry.LOGIN);
|
||||
connection.setSessionHandler(new LoginSessionHandler(connection));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid state " + handshake.getNextStatus());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package com.velocitypowered.proxy.connection.client;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
|
||||
public class InitialConnectSessionHandler implements MinecraftSessionHandler {
|
||||
private final ConnectedPlayer player;
|
||||
|
||||
public InitialConnectSessionHandler(ConnectedPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(MinecraftPacket packet) {
|
||||
// No-op: will never handle packets
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected() {
|
||||
// the user cancelled the login process
|
||||
player.getConnectedServer().disconnect();
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package com.velocitypowered.proxy.connection.client;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.packets.ServerLogin;
|
||||
import com.velocitypowered.proxy.protocol.packets.ServerLoginSuccess;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.backend.ServerConnection;
|
||||
import com.velocitypowered.proxy.data.ServerInfo;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
private final MinecraftConnection inbound;
|
||||
|
||||
public LoginSessionHandler(MinecraftConnection inbound) {
|
||||
this.inbound = Preconditions.checkNotNull(inbound, "inbound");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(MinecraftPacket packet) {
|
||||
Preconditions.checkArgument(packet instanceof ServerLogin, "Expected a ServerLogin packet, not " + packet.getClass().getName());
|
||||
|
||||
// TODO: Encryption
|
||||
inbound.setCompressionThreshold(256);
|
||||
|
||||
String username = ((ServerLogin) packet).getUsername();
|
||||
ServerLoginSuccess success = new ServerLoginSuccess();
|
||||
success.setUsername(username);
|
||||
success.setUuid(generateOfflinePlayerUuid(username));
|
||||
inbound.write(success);
|
||||
|
||||
// Initiate a regular connection and move over to it.
|
||||
ConnectedPlayer player = new ConnectedPlayer(success.getUsername(), success.getUuid(), inbound);
|
||||
ServerInfo info = new ServerInfo("test", new InetSocketAddress("localhost", 25565));
|
||||
ServerConnection connection = new ServerConnection(info, player, VelocityServer.getServer());
|
||||
|
||||
inbound.setState(StateRegistry.PLAY);
|
||||
inbound.setSessionHandler(new InitialConnectSessionHandler(player));
|
||||
connection.connect();
|
||||
}
|
||||
|
||||
private static UUID generateOfflinePlayerUuid(String username) {
|
||||
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package com.velocitypowered.proxy.connection.client;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.packets.Ping;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class PlaySessionHandler implements MinecraftSessionHandler {
|
||||
private final ConnectedPlayer player;
|
||||
|
||||
public PlaySessionHandler(ConnectedPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(MinecraftPacket packet) {
|
||||
if (packet instanceof Ping) {
|
||||
// Handle the ping.
|
||||
player.getConnection().write(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't want to handle this packet, just forward it on.
|
||||
player.getConnectedServer().getChannel().write(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleUnknown(ByteBuf buf) {
|
||||
player.getConnectedServer().getChannel().write(buf.retain());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected() {
|
||||
player.getConnectedServer().disconnect();
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
package com.velocitypowered.proxy.connection.client;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.packets.Ping;
|
||||
import com.velocitypowered.proxy.protocol.packets.StatusRequest;
|
||||
import com.velocitypowered.proxy.protocol.packets.StatusResponse;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.data.ServerPing;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.TextComponent;
|
||||
import net.kyori.text.serializer.GsonComponentSerializer;
|
||||
|
||||
public class StatusSessionHandler implements MinecraftSessionHandler {
|
||||
private static final Gson GSON = new GsonBuilder()
|
||||
.registerTypeHierarchyAdapter(Component.class, new GsonComponentSerializer())
|
||||
.create();
|
||||
private final MinecraftConnection connection;
|
||||
|
||||
public StatusSessionHandler(MinecraftConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(MinecraftPacket packet) {
|
||||
Preconditions.checkArgument(packet instanceof Ping || packet instanceof StatusRequest,
|
||||
"Unrecognized packet type " + packet.getClass().getName());
|
||||
|
||||
if (packet instanceof Ping) {
|
||||
// Just send back the client's packet, no processing to do here.
|
||||
connection.closeWith(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
// Status request
|
||||
System.out.println("Got status request!");
|
||||
ServerPing ping = new ServerPing(
|
||||
new ServerPing.Version(340, "1.12.2"),
|
||||
new ServerPing.Players(0, 0),
|
||||
TextComponent.of("test"),
|
||||
null
|
||||
);
|
||||
StatusResponse response = new StatusResponse();
|
||||
response.setStatus(GSON.toJson(ping));
|
||||
connection.write(response);
|
||||
}
|
||||
}
|
44
src/main/java/com/velocitypowered/proxy/data/ServerInfo.java
Normal file
44
src/main/java/com/velocitypowered/proxy/data/ServerInfo.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package com.velocitypowered.proxy.data;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ServerInfo {
|
||||
private final String name;
|
||||
private final InetSocketAddress address;
|
||||
|
||||
public ServerInfo(String name, InetSocketAddress address) {
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public InetSocketAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServerInfo{" +
|
||||
"name='" + name + '\'' +
|
||||
", address=" + address +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ServerInfo that = (ServerInfo) o;
|
||||
return Objects.equals(name, that.name) &&
|
||||
Objects.equals(address, that.address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, address);
|
||||
}
|
||||
}
|
95
src/main/java/com/velocitypowered/proxy/data/ServerPing.java
Normal file
95
src/main/java/com/velocitypowered/proxy/data/ServerPing.java
Normal file
@@ -0,0 +1,95 @@
|
||||
package com.velocitypowered.proxy.data;
|
||||
|
||||
import net.kyori.text.Component;
|
||||
|
||||
public class ServerPing {
|
||||
private final Version version;
|
||||
private final Players players;
|
||||
private final Component description;
|
||||
private final String favicon;
|
||||
|
||||
public ServerPing(Version version, Players players, Component description, String favicon) {
|
||||
this.version = version;
|
||||
this.players = players;
|
||||
this.description = description;
|
||||
this.favicon = favicon;
|
||||
}
|
||||
|
||||
public Version getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public Players getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
public Component getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getFavicon() {
|
||||
return favicon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServerPing{" +
|
||||
"version=" + version +
|
||||
", players=" + players +
|
||||
", description=" + description +
|
||||
", favicon='" + favicon + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static class Version {
|
||||
private final int protocol;
|
||||
private final String name;
|
||||
|
||||
public Version(int protocol, String name) {
|
||||
this.protocol = protocol;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Version{" +
|
||||
"protocol=" + protocol +
|
||||
", name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class Players {
|
||||
private final int online;
|
||||
private final int max;
|
||||
|
||||
public Players(int online, int max) {
|
||||
this.online = online;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public int getOnline() {
|
||||
return online;
|
||||
}
|
||||
|
||||
public int getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Players{" +
|
||||
"online=" + online +
|
||||
", max=" + max +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package com.velocitypowered.proxy.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public interface MinecraftPacket {
|
||||
void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion);
|
||||
|
||||
void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion);
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package com.velocitypowered.proxy.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
|
||||
public class PacketWrapper {
|
||||
private final MinecraftPacket packet;
|
||||
private final ByteBuf buffer;
|
||||
|
||||
public PacketWrapper(MinecraftPacket packet, ByteBuf buffer) {
|
||||
this.packet = packet;
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public MinecraftPacket getPacket() {
|
||||
return packet;
|
||||
}
|
||||
|
||||
public ByteBuf getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PacketWrapper{" +
|
||||
"packet=" + packet +
|
||||
", buffer=" + ByteBufUtil.hexDump(buffer) +
|
||||
'}';
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package com.velocitypowered.proxy.protocol;
|
||||
|
||||
public enum ProtocolConstants { ;
|
||||
public static final int MINECRAFT_1_12 = 340;
|
||||
|
||||
public enum Direction {
|
||||
TO_SERVER,
|
||||
TO_CLIENT
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
package com.velocitypowered.proxy.protocol;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public enum ProtocolUtils { ;
|
||||
private static final int DEFAULT_MAX_STRING_SIZE = 65536; // 64KiB
|
||||
|
||||
public static int readVarInt(ByteBuf buf) {
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
while (true) {
|
||||
int k = buf.readByte();
|
||||
i |= (k & 0x7F) << j++ * 7;
|
||||
if (j > 5) throw new RuntimeException("VarInt too big");
|
||||
if ((k & 0x80) != 128) break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public static void writeVarInt(ByteBuf buf, int value) {
|
||||
while (true) {
|
||||
if ((value & 0xFFFFFF80) == 0) {
|
||||
buf.writeByte(value);
|
||||
return;
|
||||
}
|
||||
|
||||
buf.writeByte(value & 0x7F | 0x80);
|
||||
value >>>= 7;
|
||||
}
|
||||
}
|
||||
|
||||
public static String readString(ByteBuf buf) {
|
||||
return readString(buf, DEFAULT_MAX_STRING_SIZE);
|
||||
}
|
||||
|
||||
public static String readString(ByteBuf buf, int cap) {
|
||||
int length = readVarInt(buf);
|
||||
Preconditions.checkArgument(length <= cap, "Bad string size (got %s, maximum is %s)", length, cap);
|
||||
byte[] str = new byte[length];
|
||||
buf.readBytes(str);
|
||||
return new String(str, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static void writeString(ByteBuf buf, String str) {
|
||||
byte[] asUtf8 = str.getBytes(StandardCharsets.UTF_8);
|
||||
writeVarInt(buf, asUtf8.length);
|
||||
buf.writeBytes(asUtf8);
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
package com.velocitypowered.proxy.protocol;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.packets.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public enum StateRegistry {
|
||||
HANDSHAKE {
|
||||
{
|
||||
TO_SERVER.register(0x00, Handshake.class, Handshake::new);
|
||||
}
|
||||
},
|
||||
STATUS {
|
||||
{
|
||||
TO_SERVER.register(0x00, StatusRequest.class, StatusRequest::new);
|
||||
TO_SERVER.register(0x01, Ping.class, Ping::new);
|
||||
|
||||
TO_CLIENT.register(0x00, StatusResponse.class, StatusResponse::new);
|
||||
TO_CLIENT.register(0x01, Ping.class, Ping::new);
|
||||
}
|
||||
},
|
||||
PLAY {
|
||||
{
|
||||
TO_SERVER.register(0x02, Chat.class, Chat::new);
|
||||
TO_SERVER.register(0x0b, Ping.class, Ping::new);
|
||||
|
||||
TO_CLIENT.register(0x0F, Chat.class, Chat::new);
|
||||
TO_CLIENT.register(0x1A, Disconnect.class, Disconnect::new);
|
||||
TO_CLIENT.register(0x1F, Ping.class, Ping::new);
|
||||
}
|
||||
},
|
||||
LOGIN {
|
||||
{
|
||||
TO_SERVER.register(0x00, ServerLogin.class, ServerLogin::new);
|
||||
|
||||
TO_CLIENT.register(0x00, Disconnect.class, Disconnect::new);
|
||||
// Encryption Success will follow once Mojang auth/encryption is done
|
||||
TO_CLIENT.register(0x02, ServerLoginSuccess.class, ServerLoginSuccess::new);
|
||||
TO_CLIENT.register(0x03, SetCompression.class, SetCompression::new);
|
||||
}
|
||||
};
|
||||
|
||||
public final ProtocolMappings TO_CLIENT = new ProtocolMappings(ProtocolConstants.Direction.TO_CLIENT, this);
|
||||
public final ProtocolMappings TO_SERVER = new ProtocolMappings(ProtocolConstants.Direction.TO_SERVER, this);
|
||||
|
||||
public static class ProtocolMappings {
|
||||
private final ProtocolConstants.Direction direction;
|
||||
private final StateRegistry state;
|
||||
private final Map<Integer, Supplier<? extends MinecraftPacket>> idsToSuppliers = new HashMap<>();
|
||||
private final Map<Class<? extends MinecraftPacket>, Integer> packetClassesToIds = new HashMap<>();
|
||||
|
||||
public ProtocolMappings(ProtocolConstants.Direction direction, StateRegistry state) {
|
||||
this.direction = direction;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public <P extends MinecraftPacket> void register(int id, Class<P> clazz, Supplier<P> packetSupplier) {
|
||||
idsToSuppliers.put(id, packetSupplier);
|
||||
packetClassesToIds.put(clazz, id);
|
||||
}
|
||||
|
||||
public MinecraftPacket createPacket(int id) {
|
||||
Supplier<? extends MinecraftPacket> supplier = idsToSuppliers.get(id);
|
||||
if (supplier == null) {
|
||||
return null;
|
||||
}
|
||||
return supplier.get();
|
||||
}
|
||||
|
||||
public int getId(MinecraftPacket packet) {
|
||||
Integer id = packetClassesToIds.get(packet.getClass());
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("Supplied packet " + packet.getClass().getName() + " doesn't have a mapping. Direction " + direction + " State " + state);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package com.velocitypowered.proxy.protocol.compression;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
public class JavaVelocityCompressor implements VelocityCompressor {
|
||||
private final Deflater deflater;
|
||||
private final Inflater inflater;
|
||||
private final byte[] buf;
|
||||
private boolean disposed = false;
|
||||
|
||||
public JavaVelocityCompressor() {
|
||||
this.deflater = new Deflater();
|
||||
this.inflater = new Inflater();
|
||||
this.buf = new byte[8192];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
|
||||
ensureNotDisposed();
|
||||
|
||||
byte[] inData = new byte[source.readableBytes()];
|
||||
source.readBytes(inData);
|
||||
inflater.setInput(inData);
|
||||
while (!inflater.finished()) {
|
||||
int read = inflater.inflate(buf);
|
||||
destination.writeBytes(buf, 0, read);
|
||||
}
|
||||
inflater.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
|
||||
ensureNotDisposed();
|
||||
|
||||
byte[] inData = new byte[source.readableBytes()];
|
||||
source.readBytes(inData);
|
||||
deflater.setInput(inData);
|
||||
deflater.finish();
|
||||
while (!deflater.finished()) {
|
||||
int bytes = deflater.deflate(buf);
|
||||
destination.writeBytes(buf, 0, bytes);
|
||||
}
|
||||
deflater.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ensureNotDisposed();
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
private void ensureNotDisposed() {
|
||||
Preconditions.checkState(!disposed, "Object already disposed");
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package com.velocitypowered.proxy.protocol.compression;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
public interface VelocityCompressor {
|
||||
void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException;
|
||||
|
||||
void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException;
|
||||
|
||||
void dispose();
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package com.velocitypowered.proxy.protocol.netty;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.packets.LegacyPing;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LegacyPingDecoder extends ByteToMessageDecoder {
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
if (in.readableBytes() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
short first = in.getUnsignedByte(in.readerIndex());
|
||||
short second = in.getUnsignedByte(in.readerIndex() + 1);
|
||||
if (first == 0xfe && second == 0x01) {
|
||||
in.skipBytes(in.readableBytes());
|
||||
out.add(new LegacyPing());
|
||||
}
|
||||
|
||||
ctx.pipeline().remove(this);
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package com.velocitypowered.proxy.protocol.netty;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.velocitypowered.proxy.protocol.packets.LegacyPingResponse;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public class LegacyPingEncoder extends MessageToByteEncoder<LegacyPingResponse> {
|
||||
public static final LegacyPingEncoder INSTANCE = new LegacyPingEncoder();
|
||||
|
||||
private LegacyPingEncoder() {}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, LegacyPingResponse msg, ByteBuf out) throws Exception {
|
||||
out.writeByte(0xff);
|
||||
String serializedResponse = serialize(msg);
|
||||
out.writeShort(serializedResponse.length());
|
||||
out.writeBytes(serializedResponse.getBytes(StandardCharsets.UTF_16BE));
|
||||
System.out.println(ByteBufUtil.prettyHexDump(out));
|
||||
}
|
||||
|
||||
private String serialize(LegacyPingResponse response) {
|
||||
List<String> parts = ImmutableList.of(
|
||||
"§1",
|
||||
Integer.toString(response.getProtocolVersion()),
|
||||
response.getServerVersion(),
|
||||
response.getMotd(),
|
||||
Integer.toString(response.getPlayersOnline()),
|
||||
Integer.toString(response.getPlayersMax())
|
||||
);
|
||||
return Joiner.on('\0').join(parts);
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package com.velocitypowered.proxy.protocol.netty;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.compression.VelocityCompressor;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MinecraftCompressDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
private static final int MAXIMUM_INITIAL_BUFFER_SIZE = 65536; // 64KiB
|
||||
|
||||
private final int threshold;
|
||||
private final VelocityCompressor compressor;
|
||||
|
||||
public MinecraftCompressDecoder(int threshold, VelocityCompressor compressor) {
|
||||
this.threshold = threshold;
|
||||
this.compressor = compressor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
|
||||
int uncompressedSize = ProtocolUtils.readVarInt(msg);
|
||||
if (uncompressedSize == 0) {
|
||||
// Strip the now-useless uncompressed size, this message is already uncompressed.
|
||||
out.add(msg.slice().retain());
|
||||
msg.skipBytes(msg.readableBytes());
|
||||
return;
|
||||
}
|
||||
|
||||
Preconditions.checkState(uncompressedSize >= threshold, "Uncompressed size %s doesn't make sense with threshold %s", uncompressedSize, threshold);
|
||||
ByteBuf uncompressed = ctx.alloc().buffer(Math.min(uncompressedSize, MAXIMUM_INITIAL_BUFFER_SIZE));
|
||||
try {
|
||||
compressor.inflate(msg, uncompressed);
|
||||
Preconditions.checkState(uncompressedSize == uncompressed.readableBytes(), "Mismatched compression sizes");
|
||||
out.add(uncompressed);
|
||||
} catch (Exception e) {
|
||||
// If something went wrong, rethrow the exception, but ensure we free our temporary buffer first.
|
||||
uncompressed.release();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package com.velocitypowered.proxy.protocol.netty;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.compression.VelocityCompressor;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
|
||||
public class MinecraftCompressEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
private final int threshold;
|
||||
private final VelocityCompressor compressor;
|
||||
|
||||
public MinecraftCompressEncoder(int threshold, VelocityCompressor compressor) {
|
||||
this.threshold = threshold;
|
||||
this.compressor = compressor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception {
|
||||
if (msg.readableBytes() <= threshold) {
|
||||
// Under the threshold, there is nothing to do.
|
||||
ProtocolUtils.writeVarInt(out, 0);
|
||||
out.writeBytes(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
ByteBuf compressedBuffer = ctx.alloc().buffer();
|
||||
try {
|
||||
int uncompressed = msg.readableBytes();
|
||||
compressor.deflate(msg, compressedBuffer);
|
||||
ProtocolUtils.writeVarInt(out, uncompressed);
|
||||
out.writeBytes(compressedBuffer);
|
||||
} finally {
|
||||
compressedBuffer.release();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
package com.velocitypowered.proxy.protocol.netty;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.proxy.protocol.*;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
private StateRegistry state;
|
||||
private final ProtocolConstants.Direction direction;
|
||||
private int protocolVersion;
|
||||
|
||||
public MinecraftDecoder(ProtocolConstants.Direction direction) {
|
||||
this.state = StateRegistry.HANDSHAKE;
|
||||
this.direction = Preconditions.checkNotNull(direction, "direction");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
|
||||
if (!msg.isReadable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ByteBuf slice = msg.slice().retain();
|
||||
|
||||
int packetId = ProtocolUtils.readVarInt(msg);
|
||||
StateRegistry.ProtocolMappings mappings = direction == ProtocolConstants.Direction.TO_CLIENT ? state.TO_CLIENT : state.TO_SERVER;
|
||||
MinecraftPacket packet = mappings.createPacket(packetId);
|
||||
if (packet == null) {
|
||||
msg.skipBytes(msg.readableBytes());
|
||||
out.add(new PacketWrapper(null, slice));
|
||||
} else {
|
||||
try {
|
||||
packet.decode(msg, direction, protocolVersion);
|
||||
} catch (Exception e) {
|
||||
throw new CorruptedFrameException("Error decoding " + packet.getClass() + " Direction " + direction
|
||||
+ " Protocol " + protocolVersion + " State " + state + " ID " + Integer.toHexString(packetId), e);
|
||||
}
|
||||
System.out.println("IN: " + packet);
|
||||
out.add(new PacketWrapper(packet, slice));
|
||||
}
|
||||
}
|
||||
|
||||
public int getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(int protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public StateRegistry getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(StateRegistry state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public ProtocolConstants.Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
package com.velocitypowered.proxy.protocol.netty;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
|
||||
public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> {
|
||||
private StateRegistry state;
|
||||
private final ProtocolConstants.Direction direction;
|
||||
private int protocolVersion;
|
||||
|
||||
public MinecraftEncoder(ProtocolConstants.Direction direction) {
|
||||
this.state = StateRegistry.HANDSHAKE;
|
||||
this.direction = Preconditions.checkNotNull(direction, "direction");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, MinecraftPacket msg, ByteBuf out) throws Exception {
|
||||
StateRegistry.ProtocolMappings mappings = direction == ProtocolConstants.Direction.TO_CLIENT ? state.TO_CLIENT : state.TO_SERVER;
|
||||
int packetId = mappings.getId(msg);
|
||||
ProtocolUtils.writeVarInt(out, packetId);
|
||||
msg.encode(out, direction, protocolVersion);
|
||||
System.out.println("OUT: " + msg);
|
||||
}
|
||||
|
||||
public int getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(int protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public StateRegistry getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(StateRegistry state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public ProtocolConstants.Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package com.velocitypowered.proxy.protocol.netty;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class MinecraftPipelineUtils {
|
||||
public static void strapPipelineForServer(Channel ch) {
|
||||
ch.pipeline().addLast("read-timeout", new ReadTimeoutHandler(30, TimeUnit.SECONDS));
|
||||
ch.pipeline().addLast("legacy-ping-decode", new LegacyPingDecoder());
|
||||
ch.pipeline().addLast("frame-decoder", new MinecraftVarintFrameDecoder());
|
||||
ch.pipeline().addLast("legacy-ping-encode", LegacyPingEncoder.INSTANCE);
|
||||
ch.pipeline().addLast("frame-encoder", MinecraftVarintLengthEncoder.INSTANCE);
|
||||
ch.pipeline().addLast("minecraft-decoder", new MinecraftDecoder(ProtocolConstants.Direction.TO_SERVER));
|
||||
ch.pipeline().addLast("minecraft-encoder", new MinecraftEncoder(ProtocolConstants.Direction.TO_CLIENT));
|
||||
}
|
||||
|
||||
public static void strapPipelineForProxy(Channel ch) {
|
||||
ch.pipeline().addLast("read-timeout", new ReadTimeoutHandler(30, TimeUnit.SECONDS));
|
||||
ch.pipeline().addLast("legacy-ping-decode", new LegacyPingDecoder());
|
||||
ch.pipeline().addLast("frame-decoder", new MinecraftVarintFrameDecoder());
|
||||
ch.pipeline().addLast("legacy-ping-encode", LegacyPingEncoder.INSTANCE);
|
||||
ch.pipeline().addLast("frame-encoder", MinecraftVarintLengthEncoder.INSTANCE);
|
||||
ch.pipeline().addLast("minecraft-decoder", new MinecraftDecoder(ProtocolConstants.Direction.TO_CLIENT));
|
||||
ch.pipeline().addLast("minecraft-encoder", new MinecraftEncoder(ProtocolConstants.Direction.TO_SERVER));
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package com.velocitypowered.proxy.protocol.netty;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
if (in.readableBytes() < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
in.markReaderIndex();
|
||||
int packetLength = ProtocolUtils.readVarInt(in);
|
||||
if (in.readableBytes() < packetLength) {
|
||||
in.resetReaderIndex();
|
||||
return;
|
||||
}
|
||||
|
||||
out.add(in.slice(in.readerIndex(), packetLength).retain());
|
||||
in.skipBytes(packetLength);
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package com.velocitypowered.proxy.protocol.netty;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public class MinecraftVarintLengthEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
public static final MinecraftVarintLengthEncoder INSTANCE = new MinecraftVarintLengthEncoder();
|
||||
|
||||
private MinecraftVarintLengthEncoder() { }
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception {
|
||||
ProtocolUtils.writeVarInt(out, msg.readableBytes());
|
||||
out.writeBytes(msg);
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package com.velocitypowered.proxy.protocol.packets;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class Chat implements MinecraftPacket {
|
||||
private String message;
|
||||
private byte position;
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public byte getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(byte position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Chat{" +
|
||||
"message='" + message + '\'' +
|
||||
", position=" + position +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
message = ProtocolUtils.readString(buf);
|
||||
if (direction == ProtocolConstants.Direction.TO_CLIENT) {
|
||||
position = buf.readByte();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
ProtocolUtils.writeString(buf, message);
|
||||
if (direction == ProtocolConstants.Direction.TO_CLIENT) {
|
||||
buf.writeByte(position);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package com.velocitypowered.proxy.protocol.packets;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class Disconnect implements MinecraftPacket {
|
||||
private String reason;
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Disconnect{" +
|
||||
"reason='" + reason + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
reason = ProtocolUtils.readString(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
ProtocolUtils.writeString(buf, reason);
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
package com.velocitypowered.proxy.protocol.packets;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class Handshake implements MinecraftPacket {
|
||||
private int protocolVersion;
|
||||
private String serverAddress;
|
||||
private int port;
|
||||
private int nextStatus;
|
||||
|
||||
public int getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(int protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public String getServerAddress() {
|
||||
return serverAddress;
|
||||
}
|
||||
|
||||
public void setServerAddress(String serverAddress) {
|
||||
this.serverAddress = serverAddress;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public int getNextStatus() {
|
||||
return nextStatus;
|
||||
}
|
||||
|
||||
public void setNextStatus(int nextStatus) {
|
||||
this.nextStatus = nextStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Handshake{" +
|
||||
"protocolVersion=" + protocolVersion +
|
||||
", serverAddress='" + serverAddress + '\'' +
|
||||
", port=" + port +
|
||||
", nextStatus=" + nextStatus +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
this.protocolVersion = ProtocolUtils.readVarInt(buf);
|
||||
this.serverAddress = ProtocolUtils.readString(buf, 255);
|
||||
this.port = buf.readUnsignedShort();
|
||||
this.nextStatus = ProtocolUtils.readVarInt(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
ProtocolUtils.writeVarInt(buf, this.protocolVersion);
|
||||
ProtocolUtils.writeString(buf, this.serverAddress);
|
||||
buf.writeShort(this.port);
|
||||
ProtocolUtils.writeVarInt(buf, this.nextStatus);
|
||||
}
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
package com.velocitypowered.proxy.protocol.packets;
|
||||
|
||||
public class LegacyPing {
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
package com.velocitypowered.proxy.protocol.packets;
|
||||
|
||||
import com.velocitypowered.proxy.data.ServerPing;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
|
||||
public class LegacyPingResponse {
|
||||
private int protocolVersion;
|
||||
private String serverVersion;
|
||||
private String motd;
|
||||
private int playersOnline;
|
||||
private int playersMax;
|
||||
|
||||
public LegacyPingResponse() {
|
||||
}
|
||||
|
||||
public LegacyPingResponse(int protocolVersion, String serverVersion, String motd, int playersOnline, int playersMax) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.serverVersion = serverVersion;
|
||||
this.motd = motd;
|
||||
this.playersOnline = playersOnline;
|
||||
this.playersMax = playersMax;
|
||||
}
|
||||
|
||||
public int getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(int protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public String getServerVersion() {
|
||||
return serverVersion;
|
||||
}
|
||||
|
||||
public void setServerVersion(String serverVersion) {
|
||||
this.serverVersion = serverVersion;
|
||||
}
|
||||
|
||||
public String getMotd() {
|
||||
return motd;
|
||||
}
|
||||
|
||||
public void setMotd(String motd) {
|
||||
this.motd = motd;
|
||||
}
|
||||
|
||||
public int getPlayersOnline() {
|
||||
return playersOnline;
|
||||
}
|
||||
|
||||
public void setPlayersOnline(int playersOnline) {
|
||||
this.playersOnline = playersOnline;
|
||||
}
|
||||
|
||||
public int getPlayersMax() {
|
||||
return playersMax;
|
||||
}
|
||||
|
||||
public void setPlayersMax(int playersMax) {
|
||||
this.playersMax = playersMax;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LegacyPingResponse{" +
|
||||
"protocolVersion=" + protocolVersion +
|
||||
", serverVersion='" + serverVersion + '\'' +
|
||||
", motd='" + motd + '\'' +
|
||||
", playersOnline=" + playersOnline +
|
||||
", playersMax=" + playersMax +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static LegacyPingResponse from(ServerPing ping) {
|
||||
return new LegacyPingResponse(ping.getVersion().getProtocol(),
|
||||
ping.getVersion().getName(),
|
||||
ComponentSerializers.LEGACY.serialize(ping.getDescription()),
|
||||
ping.getPlayers().getOnline(),
|
||||
ping.getPlayers().getMax());
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package com.velocitypowered.proxy.protocol.packets;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class Ping implements MinecraftPacket {
|
||||
private long randomId;
|
||||
|
||||
public long getRandomId() {
|
||||
return randomId;
|
||||
}
|
||||
|
||||
public void setRandomId(long randomId) {
|
||||
this.randomId = randomId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Ping{" +
|
||||
"randomId=" + randomId +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
randomId = buf.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
buf.writeLong(randomId);
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package com.velocitypowered.proxy.protocol.packets;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class ServerLogin implements MinecraftPacket {
|
||||
private String username;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServerLogin{" +
|
||||
"username='" + username + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
username = ProtocolUtils.readString(buf, 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
ProtocolUtils.writeString(buf, username);
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
package com.velocitypowered.proxy.protocol.packets;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ServerLoginSuccess implements MinecraftPacket {
|
||||
private UUID uuid;
|
||||
private String username;
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServerLoginSuccess{" +
|
||||
"uuid=" + uuid +
|
||||
", username='" + username + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
uuid = UUID.fromString(ProtocolUtils.readString(buf, 36));
|
||||
username = ProtocolUtils.readString(buf, 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
ProtocolUtils.writeString(buf, uuid.toString());
|
||||
ProtocolUtils.writeString(buf, username);
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package com.velocitypowered.proxy.protocol.packets;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class SetCompression implements MinecraftPacket {
|
||||
private int threshold;
|
||||
|
||||
public SetCompression() {}
|
||||
|
||||
public SetCompression(int threshold) {
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
public int getThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
public void setThreshold(int threshold) {
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SetCompression{" +
|
||||
"threshold=" + threshold +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
this.threshold = ProtocolUtils.readVarInt(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
ProtocolUtils.writeVarInt(buf, threshold);
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.velocitypowered.proxy.protocol.packets;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class StatusRequest implements MinecraftPacket {
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package com.velocitypowered.proxy.protocol.packets;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class StatusResponse implements MinecraftPacket {
|
||||
private String status;
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StatusResponse{" +
|
||||
"status='" + status + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
status = ProtocolUtils.readString(buf, Short.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
ProtocolUtils.writeString(buf, status);
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package com.velocitypowered.proxy.util;
|
||||
|
||||
public enum ThrowableUtils {
|
||||
;
|
||||
|
||||
public static String briefDescription(Throwable throwable) {
|
||||
return throwable.getClass().getName() + ": " + throwable.getMessage();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user