Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-12-24 15:20:35 +01:00
Initial client connection pipeline, this isn't done yet.
Dieser Commit ist enthalten in:
Ursprung
6c0ab73a91
Commit
7867c496ec
44
src/main/java/io/minimum/minecraft/velocity/data/ServerInfo.java
Normale Datei
44
src/main/java/io/minimum/minecraft/velocity/data/ServerInfo.java
Normale Datei
@ -0,0 +1,44 @@
|
||||
package io.minimum.minecraft.velocity.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);
|
||||
}
|
||||
}
|
@ -22,7 +22,9 @@ public enum StateRegistry {
|
||||
}
|
||||
},
|
||||
PLAY {
|
||||
|
||||
{
|
||||
TO_CLIENT.register(0x1A, Disconnect.class, Disconnect::new);
|
||||
}
|
||||
},
|
||||
LOGIN {
|
||||
{
|
||||
|
@ -30,9 +30,7 @@ public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
int packetId = ProtocolUtils.readVarInt(msg);
|
||||
StateRegistry.ProtocolMappings mappings = direction == ProtocolConstants.Direction.TO_CLIENT ? state.TO_CLIENT : state.TO_SERVER;
|
||||
MinecraftPacket packet = mappings.createPacket(packetId);
|
||||
System.out.println("Decode!");
|
||||
System.out.println("packet ID: " + packetId);
|
||||
System.out.println("packet hexdump: " + ByteBufUtil.hexDump(slice));
|
||||
System.out.println(direction + " <- " + ByteBufUtil.hexDump(slice));
|
||||
if (packet == null) {
|
||||
msg.skipBytes(msg.readableBytes());
|
||||
out.add(new PacketWrapper(null, slice));
|
||||
|
@ -3,6 +3,7 @@ package io.minimum.minecraft.velocity.protocol.netty;
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.minimum.minecraft.velocity.protocol.*;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
|
||||
@ -22,6 +23,8 @@ public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> {
|
||||
int packetId = mappings.getId(msg);
|
||||
ProtocolUtils.writeVarInt(out, packetId);
|
||||
msg.encode(out, direction, protocolVersion);
|
||||
|
||||
System.out.println(direction + " -> " + ByteBufUtil.hexDump(out));
|
||||
}
|
||||
|
||||
public int getProtocolVersion() {
|
||||
|
@ -4,7 +4,7 @@ import io.minimum.minecraft.velocity.protocol.ProtocolConstants;
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
public class MinecraftPipelineUtils {
|
||||
public static void strapPipeline(Channel ch) {
|
||||
public static void strapPipelineForServer(Channel ch) {
|
||||
ch.pipeline().addLast("legacy-ping-decode", new LegacyPingDecoder());
|
||||
ch.pipeline().addLast("frame-decoder", new MinecraftVarintFrameDecoder());
|
||||
ch.pipeline().addLast("legacy-ping-encode", LegacyPingEncoder.INSTANCE);
|
||||
@ -12,4 +12,13 @@ public class MinecraftPipelineUtils {
|
||||
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("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));
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("Got a varint-prefixed packet length " + packetLength);
|
||||
out.add(in.slice(in.readerIndex(), packetLength).retain());
|
||||
in.skipBytes(packetLength);
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package io.minimum.minecraft.velocity.proxy;
|
||||
|
||||
import io.minimum.minecraft.velocity.proxy.server.ServerConnection;
|
||||
import io.minimum.minecraft.velocity.protocol.packets.Disconnect;
|
||||
import net.kyori.text.TextComponent;
|
||||
import net.kyori.text.format.TextColor;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@ -31,4 +34,17 @@ public class ConnectedPlayer {
|
||||
public ServerConnection getConnectedServer() {
|
||||
return connectedServer;
|
||||
}
|
||||
|
||||
public void handleConnectionException(Throwable throwable) {
|
||||
String error = "Exception: " + throwable.getClass().getName() + ": " + throwable.getMessage();
|
||||
|
||||
if (connectedServer == null) {
|
||||
// The player isn't yet connected to a server - we should disconnect them.
|
||||
Disconnect disconnect = new Disconnect();
|
||||
disconnect.setReason(ComponentSerializers.JSON.serialize(TextComponent.of(error, TextColor.RED)));
|
||||
connection.closeWith(disconnect);
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class InboundMinecraftConnection {
|
||||
public static final AttributeKey<InboundMinecraftConnection> CONNECTION = AttributeKey.newInstance("velocity-connection");
|
||||
|
||||
@ -21,6 +23,7 @@ public class InboundMinecraftConnection {
|
||||
private Handshake handshake;
|
||||
private StateRegistry state;
|
||||
private MinecraftSessionHandler sessionHandler;
|
||||
private ConnectedPlayer connectedPlayer;
|
||||
|
||||
public InboundMinecraftConnection(Channel channel) {
|
||||
this.channel = channel;
|
||||
@ -79,7 +82,7 @@ public class InboundMinecraftConnection {
|
||||
Preconditions.checkState(!closed, "Connection is closed.");
|
||||
}
|
||||
|
||||
private void setStatus(StateRegistry state) {
|
||||
public void setStatus(StateRegistry state) {
|
||||
Preconditions.checkNotNull(state, "state");
|
||||
this.state = state;
|
||||
channel.pipeline().get(MinecraftEncoder.class).setState(state);
|
||||
@ -89,4 +92,12 @@ public class InboundMinecraftConnection {
|
||||
public void teardown() {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
public Optional<ConnectedPlayer> getConnectedPlayer() {
|
||||
return Optional.ofNullable(connectedPlayer);
|
||||
}
|
||||
}
|
||||
|
114
src/main/java/io/minimum/minecraft/velocity/proxy/ServerConnection.java
Normale Datei
114
src/main/java/io/minimum/minecraft/velocity/proxy/ServerConnection.java
Normale Datei
@ -0,0 +1,114 @@
|
||||
package io.minimum.minecraft.velocity.proxy;
|
||||
|
||||
import io.minimum.minecraft.velocity.protocol.MinecraftPacket;
|
||||
import io.minimum.minecraft.velocity.protocol.PacketWrapper;
|
||||
import io.minimum.minecraft.velocity.protocol.ProtocolConstants;
|
||||
import io.minimum.minecraft.velocity.protocol.StateRegistry;
|
||||
import io.minimum.minecraft.velocity.data.ServerInfo;
|
||||
import io.minimum.minecraft.velocity.protocol.netty.MinecraftDecoder;
|
||||
import io.minimum.minecraft.velocity.protocol.netty.MinecraftEncoder;
|
||||
import io.minimum.minecraft.velocity.protocol.netty.MinecraftPipelineUtils;
|
||||
import io.minimum.minecraft.velocity.protocol.packets.Disconnect;
|
||||
import io.minimum.minecraft.velocity.protocol.packets.Handshake;
|
||||
import io.minimum.minecraft.velocity.protocol.packets.ServerLogin;
|
||||
import io.minimum.minecraft.velocity.protocol.packets.ServerLoginSuccess;
|
||||
import io.netty.channel.*;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ServerConnection {
|
||||
private Channel channel;
|
||||
private final ServerInfo info;
|
||||
private final ConnectedPlayer proxyPlayer;
|
||||
private StateRegistry registry;
|
||||
private final VelocityServer server;
|
||||
|
||||
public ServerConnection(ServerInfo target, ConnectedPlayer proxyPlayer, VelocityServer server) {
|
||||
this.info = target;
|
||||
this.proxyPlayer = proxyPlayer;
|
||||
this.server = server;
|
||||
this.registry = StateRegistry.HANDSHAKE;
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
server.initializeGenericBootstrap()
|
||||
.handler(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
MinecraftPipelineUtils.strapPipelineForProxy(ch);
|
||||
ch.pipeline().addLast("state-based-interceptor", new StateBasedInterceptor());
|
||||
}
|
||||
})
|
||||
.connect(info.getAddress())
|
||||
.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
channel = future.channel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class StateBasedInterceptor extends ChannelInboundHandlerAdapter {
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
// Initiate a handshake.
|
||||
Handshake handshake = new Handshake();
|
||||
handshake.setNextStatus(2); // login
|
||||
handshake.setProtocolVersion(ProtocolConstants.MINECRAFT_1_12); // TODO: Expose client version
|
||||
handshake.setServerAddress(info.getAddress().getHostString());
|
||||
handshake.setPort(info.getAddress().getPort());
|
||||
ctx.writeAndFlush(handshake, ctx.voidPromise());
|
||||
|
||||
setRegistry(StateRegistry.LOGIN);
|
||||
|
||||
// Login
|
||||
ServerLogin login = new ServerLogin();
|
||||
login.setUsername(proxyPlayer.getUsername());
|
||||
ctx.writeAndFlush(login, ctx.voidPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof PacketWrapper) {
|
||||
PacketWrapper pw = (PacketWrapper) msg;
|
||||
try {
|
||||
switch (registry) {
|
||||
case LOGIN:
|
||||
onLogin(ctx, pw);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unsupported state " + registry);
|
||||
}
|
||||
} finally {
|
||||
((PacketWrapper) msg).getBuffer().release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onLogin(ChannelHandlerContext ctx, PacketWrapper wrapper) {
|
||||
MinecraftPacket packet = wrapper.getPacket();
|
||||
if (packet instanceof Disconnect) {
|
||||
Disconnect disconnect = (Disconnect) packet;
|
||||
ctx.close();
|
||||
proxyPlayer.handleConnectionException(new IOException("Disconnected from target: " + jsonToPlain(disconnect.getReason())));
|
||||
}
|
||||
|
||||
if (packet instanceof ServerLoginSuccess) {
|
||||
System.out.println("got it");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setRegistry(StateRegistry registry) {
|
||||
this.registry = registry;
|
||||
this.channel.pipeline().get(MinecraftEncoder.class).setState(registry);
|
||||
this.channel.pipeline().get(MinecraftDecoder.class).setState(registry);
|
||||
}
|
||||
|
||||
private static String jsonToPlain(String j) {
|
||||
return ComponentSerializers.LEGACY.serialize(
|
||||
ComponentSerializers.JSON.deserialize(j)
|
||||
).replaceAll("\\u00A7[a-z0-9]", "");
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package io.minimum.minecraft.velocity.proxy;
|
||||
|
||||
import io.minimum.minecraft.velocity.protocol.ProtocolConstants;
|
||||
import io.minimum.minecraft.velocity.protocol.netty.*;
|
||||
import io.minimum.minecraft.velocity.proxy.server.ServerConnection;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.*;
|
||||
@ -11,6 +9,8 @@ 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;
|
||||
|
||||
@ -18,9 +18,14 @@ public class 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)
|
||||
@ -28,7 +33,7 @@ public class VelocityServer {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ch.attr(InboundMinecraftConnection.CONNECTION).set(new InboundMinecraftConnection(ch));
|
||||
MinecraftPipelineUtils.strapPipeline(ch);
|
||||
MinecraftPipelineUtils.strapPipelineForServer(ch);
|
||||
ch.pipeline().addLast("handler", new MinecraftClientSessionHandler());
|
||||
}
|
||||
})
|
||||
|
@ -1,12 +1,14 @@
|
||||
package io.minimum.minecraft.velocity.proxy.handler;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.minimum.minecraft.velocity.data.ServerInfo;
|
||||
import io.minimum.minecraft.velocity.protocol.MinecraftPacket;
|
||||
import io.minimum.minecraft.velocity.protocol.StateRegistry;
|
||||
import io.minimum.minecraft.velocity.protocol.packets.ServerLogin;
|
||||
import io.minimum.minecraft.velocity.protocol.packets.ServerLoginSuccess;
|
||||
import io.minimum.minecraft.velocity.proxy.InboundMinecraftConnection;
|
||||
import io.minimum.minecraft.velocity.proxy.MinecraftSessionHandler;
|
||||
import io.minimum.minecraft.velocity.proxy.*;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -21,12 +23,20 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
public void handle(MinecraftPacket packet) {
|
||||
Preconditions.checkArgument(packet instanceof ServerLogin, "Expected a ServerLogin packet, not " + packet.getClass().getName());
|
||||
|
||||
// TODO: Encryption and compression
|
||||
String username = ((ServerLogin) packet).getUsername();
|
||||
ServerLoginSuccess success = new ServerLoginSuccess();
|
||||
success.setUsername(username);
|
||||
success.setUuid(generateOfflinePlayerUuid(username));
|
||||
|
||||
connection.write(success);
|
||||
|
||||
connection.setStatus(StateRegistry.PLAY);
|
||||
|
||||
ConnectedPlayer player = new ConnectedPlayer(username, generateOfflinePlayerUuid(username), connection);
|
||||
|
||||
ServerInfo info = new ServerInfo("test", new InetSocketAddress("127.0.0.1", 25565));
|
||||
ServerConnection connection = new ServerConnection(info, player, VelocityServer.getServer());
|
||||
connection.connect();
|
||||
}
|
||||
|
||||
private static UUID generateOfflinePlayerUuid(String username) {
|
||||
|
@ -1,19 +0,0 @@
|
||||
package io.minimum.minecraft.velocity.proxy.server;
|
||||
|
||||
import io.minimum.minecraft.velocity.protocol.StateRegistry;
|
||||
import io.minimum.minecraft.velocity.proxy.ConnectedPlayer;
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
public class ServerConnection {
|
||||
private final Channel remoteServer;
|
||||
private final ConnectedPlayer proxyPlayer;
|
||||
private StateRegistry registry;
|
||||
|
||||
public ServerConnection(Channel remoteServer, ConnectedPlayer proxyPlayer) {
|
||||
this.remoteServer = remoteServer;
|
||||
this.proxyPlayer = proxyPlayer;
|
||||
this.registry = StateRegistry.HANDSHAKE;
|
||||
}
|
||||
|
||||
|
||||
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren