diff --git a/src/main/java/io/minimum/minecraft/velocity/protocol/StateRegistry.java b/src/main/java/io/minimum/minecraft/velocity/protocol/StateRegistry.java index b1075d9d6..41208faee 100644 --- a/src/main/java/io/minimum/minecraft/velocity/protocol/StateRegistry.java +++ b/src/main/java/io/minimum/minecraft/velocity/protocol/StateRegistry.java @@ -23,6 +23,9 @@ public enum StateRegistry { }, PLAY { { + TO_SERVER.register(0x02, Chat.class, Chat::new); + + TO_CLIENT.register(0x0F, Chat.class, Chat::new); TO_CLIENT.register(0x1A, Disconnect.class, Disconnect::new); } }, diff --git a/src/main/java/io/minimum/minecraft/velocity/protocol/packets/Chat.java b/src/main/java/io/minimum/minecraft/velocity/protocol/packets/Chat.java new file mode 100644 index 000000000..645d6dd7e --- /dev/null +++ b/src/main/java/io/minimum/minecraft/velocity/protocol/packets/Chat.java @@ -0,0 +1,51 @@ +package io.minimum.minecraft.velocity.protocol.packets; + +import io.minimum.minecraft.velocity.protocol.MinecraftPacket; +import io.minimum.minecraft.velocity.protocol.ProtocolConstants; +import io.minimum.minecraft.velocity.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); + } + } +} diff --git a/src/main/java/io/minimum/minecraft/velocity/proxy/ConnectedPlayer.java b/src/main/java/io/minimum/minecraft/velocity/proxy/ConnectedPlayer.java index ef13ed19d..c603ba204 100644 --- a/src/main/java/io/minimum/minecraft/velocity/proxy/ConnectedPlayer.java +++ b/src/main/java/io/minimum/minecraft/velocity/proxy/ConnectedPlayer.java @@ -1,5 +1,7 @@ package io.minimum.minecraft.velocity.proxy; +import io.minimum.minecraft.velocity.data.ServerInfo; +import io.minimum.minecraft.velocity.protocol.packets.Chat; import io.minimum.minecraft.velocity.protocol.packets.Disconnect; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; @@ -35,19 +37,30 @@ public class ConnectedPlayer { return connectedServer; } - public void handleConnectionException(Throwable throwable) { - String error = "Exception: " + throwable.getClass().getName() + ": " + throwable.getMessage(); + public void handleConnectionException(ServerInfo info, Throwable throwable) { + String error = String.format("%s: %s", + throwable.getClass().getName(), throwable.getMessage()); Disconnect disconnect = new Disconnect(); disconnect.setReason(ComponentSerializers.JSON.serialize(TextComponent.of(error, TextColor.RED))); - handleConnectionException(disconnect); + handleConnectionException(info, disconnect); } - public void handleConnectionException(Disconnect 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. - connection.closeWith(disconnect); + Disconnect d = new Disconnect(); + d.setReason(ComponentSerializers.JSON.serialize(component)); + connection.closeWith(d); } else { - // TODO + Chat chat = new Chat(); + chat.setMessage(ComponentSerializers.JSON.serialize(component)); + connection.write(chat); } } diff --git a/src/main/java/io/minimum/minecraft/velocity/proxy/ServerConnection.java b/src/main/java/io/minimum/minecraft/velocity/proxy/ServerConnection.java index 5ac9be83d..38a0a24a5 100644 --- a/src/main/java/io/minimum/minecraft/velocity/proxy/ServerConnection.java +++ b/src/main/java/io/minimum/minecraft/velocity/proxy/ServerConnection.java @@ -39,7 +39,11 @@ public class ServerConnection { .addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { - channel = future.channel(); + if (future.isSuccess()) { + channel = future.channel(); + } else { + proxyPlayer.handleConnectionException(info, future.cause()); + } } }); } @@ -49,11 +53,11 @@ public class ServerConnection { channel = null; } - public void forward(ByteBuf buf) { + public void forward(Object o) { if (registry != StateRegistry.PLAY) { throw new IllegalStateException("Not accepting player information until PLAY state"); } - channel.writeAndFlush(buf.retain()); + channel.writeAndFlush(o, channel.voidPromise()); } private class StateBasedInterceptor extends ChannelInboundHandlerAdapter { @@ -62,7 +66,7 @@ public class ServerConnection { // Initiate a handshake. Handshake handshake = new Handshake(); handshake.setNextStatus(2); // login - handshake.setProtocolVersion(proxyPlayer.getConnection().getProtocolVersion()); // TODO: Expose client version + handshake.setProtocolVersion(proxyPlayer.getConnection().getProtocolVersion()); handshake.setServerAddress(info.getAddress().getHostString()); handshake.setPort(info.getAddress().getPort()); ctx.writeAndFlush(handshake, ctx.voidPromise()); @@ -113,7 +117,7 @@ public class ServerConnection { if (packet instanceof Disconnect) { Disconnect disconnect = (Disconnect) packet; ctx.close(); - proxyPlayer.handleConnectionException(disconnect); + proxyPlayer.handleConnectionException(info, disconnect); } if (packet instanceof SetCompression) { diff --git a/src/main/java/io/minimum/minecraft/velocity/proxy/handler/PlaySessionHandler.java b/src/main/java/io/minimum/minecraft/velocity/proxy/handler/PlaySessionHandler.java index 1e6a7d0f8..4deda9bd8 100644 --- a/src/main/java/io/minimum/minecraft/velocity/proxy/handler/PlaySessionHandler.java +++ b/src/main/java/io/minimum/minecraft/velocity/proxy/handler/PlaySessionHandler.java @@ -1,6 +1,8 @@ package io.minimum.minecraft.velocity.proxy.handler; import io.minimum.minecraft.velocity.protocol.MinecraftPacket; +import io.minimum.minecraft.velocity.protocol.packets.Chat; +import io.minimum.minecraft.velocity.protocol.packets.Ping; import io.minimum.minecraft.velocity.proxy.ConnectedPlayer; import io.minimum.minecraft.velocity.proxy.MinecraftSessionHandler; import io.minimum.minecraft.velocity.proxy.ServerConnection; @@ -17,11 +19,20 @@ public class PlaySessionHandler implements MinecraftSessionHandler { @Override public void handle(MinecraftPacket packet) { + if (packet instanceof Ping) { + // Handle the ping. + player.getConnection().write(packet); + return; + } + if (packet instanceof Chat) { + // TODO: handle this ourselves, for now do this + player.getConnectedServer().forward(packet); + } } @Override public void handleUnknown(ByteBuf buf) { - connection.forward(buf); + connection.forward(buf.retain()); } }