diff --git a/example/src/main/java/org/geysermc/mcprotocollib/network/example/TestProtocol.java b/example/src/main/java/org/geysermc/mcprotocollib/network/example/TestProtocol.java index 58ef30164..161d263dc 100644 --- a/example/src/main/java/org/geysermc/mcprotocollib/network/example/TestProtocol.java +++ b/example/src/main/java/org/geysermc/mcprotocollib/network/example/TestProtocol.java @@ -9,8 +9,8 @@ import org.geysermc.mcprotocollib.network.crypt.EncryptionConfig; import org.geysermc.mcprotocollib.network.packet.DefaultPacketHeader; import org.geysermc.mcprotocollib.network.packet.PacketHeader; -import org.geysermc.mcprotocollib.network.packet.PacketProtocol; import org.geysermc.mcprotocollib.network.packet.PacketRegistry; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import org.geysermc.mcprotocollib.protocol.codec.MinecraftTypes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,7 +18,7 @@ import javax.crypto.SecretKey; import java.security.GeneralSecurityException; -public class TestProtocol extends PacketProtocol { +public class TestProtocol extends MinecraftProtocol { private static final Logger log = LoggerFactory.getLogger(TestProtocol.class); private final PacketHeader header = new DefaultPacketHeader(); private final PacketRegistry registry = new PacketRegistry(); diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/Server.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/Server.java index 5a9cc5f1d..4d71f96e3 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/Server.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/Server.java @@ -1,8 +1,8 @@ package org.geysermc.mcprotocollib.network; import org.geysermc.mcprotocollib.network.event.server.ServerListener; -import org.geysermc.mcprotocollib.network.packet.PacketProtocol; import org.geysermc.mcprotocollib.network.server.AbstractServer; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import java.net.SocketAddress; import java.util.List; @@ -25,7 +25,7 @@ public interface Server { * * @return The server's packet protocol. */ - Supplier getPacketProtocol(); + Supplier getPacketProtocol(); /** * Returns true if the listener is listening. diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/Session.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/Session.java index 043e0edef..70e10b44d 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/Session.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/Session.java @@ -10,7 +10,7 @@ import org.geysermc.mcprotocollib.network.event.session.SessionListener; import org.geysermc.mcprotocollib.network.netty.FlushHandler; import org.geysermc.mcprotocollib.network.packet.Packet; -import org.geysermc.mcprotocollib.network.packet.PacketProtocol; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import java.net.SocketAddress; import java.util.List; @@ -42,7 +42,7 @@ public interface Session { * * @return The session's packet protocol. */ - PacketProtocol getPacketProtocol(); + MinecraftProtocol getPacketProtocol(); /** * Gets this session's set flags. If this session belongs to a server, the server's diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/event/session/PacketSendingEvent.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/event/session/PacketSendingEvent.java index bb9b67acc..b2b05aa40 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/event/session/PacketSendingEvent.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/event/session/PacketSendingEvent.java @@ -34,17 +34,11 @@ public Session getSession() { /** * Gets the packet involved in this event as the required type. * - * @param Type of the packet. * @return The event's packet as the required type. * @throws IllegalStateException If the packet's value isn't of the required type. */ - @SuppressWarnings("unchecked") - public T getPacket() { - try { - return (T) this.packet; - } catch (ClassCastException e) { - throw new IllegalStateException("Tried to get packet as the wrong type. Actual type: " + this.packet.getClass().getName()); - } + public Packet getPacket() { + return this.packet; } /** diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/factory/ClientNetworkSessionFactory.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/factory/ClientNetworkSessionFactory.java index 0ea96e143..f520882f1 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/factory/ClientNetworkSessionFactory.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/factory/ClientNetworkSessionFactory.java @@ -5,8 +5,8 @@ import lombok.experimental.Accessors; import org.geysermc.mcprotocollib.network.ProxyInfo; import org.geysermc.mcprotocollib.network.netty.DefaultPacketHandlerExecutor; -import org.geysermc.mcprotocollib.network.packet.PacketProtocol; import org.geysermc.mcprotocollib.network.session.ClientNetworkSession; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -18,7 +18,7 @@ @NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) public final class ClientNetworkSessionFactory { private SocketAddress remoteSocketAddress; - private PacketProtocol protocol; + private MinecraftProtocol protocol; private Executor packetHandlerExecutor; private SocketAddress bindSocketAddress; private ProxyInfo proxy; diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/MinecraftChannelInitializer.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/MinecraftChannelInitializer.java index d1aa2494e..7dd2b05d2 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/MinecraftChannelInitializer.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/MinecraftChannelInitializer.java @@ -10,7 +10,7 @@ import org.geysermc.mcprotocollib.network.BuiltinFlags; import org.geysermc.mcprotocollib.network.NetworkConstants; import org.geysermc.mcprotocollib.network.Session; -import org.geysermc.mcprotocollib.network.packet.PacketProtocol; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import java.util.function.Function; @@ -31,7 +31,7 @@ protected S createSession(Channel ch) { } protected void addHandlers(S session, Channel ch) { - PacketProtocol protocol = session.getPacketProtocol(); + MinecraftProtocol protocol = session.getPacketProtocol(); ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(NetworkConstants.READ_TIMEOUT_NAME, new ReadTimeoutHandler(session.getFlag(BuiltinFlags.READ_TIMEOUT, 30))); diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketCodec.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketCodec.java index 42a5100bc..94c3523e6 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketCodec.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketCodec.java @@ -2,15 +2,13 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.MessageToMessageCodec; +import io.netty.handler.codec.ByteToMessageCodec; import org.geysermc.mcprotocollib.network.Session; import org.geysermc.mcprotocollib.network.codec.PacketDefinition; import org.geysermc.mcprotocollib.network.event.session.PacketErrorEvent; import org.geysermc.mcprotocollib.network.packet.Packet; -import org.geysermc.mcprotocollib.network.packet.PacketProtocol; import org.geysermc.mcprotocollib.network.packet.PacketRegistry; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; @@ -18,7 +16,7 @@ import java.util.List; -public class PacketCodec extends MessageToMessageCodec { +public class PacketCodec extends ByteToMessageCodec { private static final Marker marker = MarkerFactory.getMarker("packet_logging"); private static final Logger log = LoggerFactory.getLogger(PacketCodec.class); @@ -32,22 +30,20 @@ public PacketCodec(Session session, boolean client) { @SuppressWarnings({"rawtypes", "unchecked"}) @Override - public void encode(ChannelHandlerContext ctx, Packet packet, List out) { + public void encode(ChannelHandlerContext ctx, Packet packet, ByteBuf out) { if (log.isTraceEnabled()) { log.trace(marker, "Encoding packet: {}", packet.getClass().getSimpleName()); } - PacketProtocol packetProtocol = this.session.getPacketProtocol(); + int initial = out.writerIndex(); + MinecraftProtocol packetProtocol = this.session.getPacketProtocol(); PacketRegistry packetRegistry = packetProtocol.getOutboundPacketRegistry(); try { int packetId = this.client ? packetRegistry.getServerboundId(packet) : packetRegistry.getClientboundId(packet); PacketDefinition definition = this.client ? packetRegistry.getServerboundDefinition(packetId) : packetRegistry.getClientboundDefinition(packetId); - ByteBuf buf = ctx.alloc().buffer(); - packetProtocol.getPacketHeader().writePacketId(buf, packetId); - definition.getSerializer().serialize(buf, packet); - - out.add(buf); + packetProtocol.getPacketHeader().writePacketId(out, packetId); + definition.getSerializer().serialize(out, packet); if (log.isDebugEnabled()) { log.debug(marker, "Encoded packet {} ({})", packet.getClass().getSimpleName(), packetId); @@ -55,10 +51,13 @@ public void encode(ChannelHandlerContext ctx, Packet packet, List out) { } catch (Throwable t) { log.debug(marker, "Error encoding packet", t); + // Reset writer index to make sure incomplete data is not written out. + out.writerIndex(initial); + PacketErrorEvent e = new PacketErrorEvent(this.session, t, packet); this.session.callEvent(e); if (!e.shouldSuppress()) { - throw new EncoderException(t); + throw t; } } } @@ -72,7 +71,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List out) int initial = buf.readerIndex(); - PacketProtocol packetProtocol = this.session.getPacketProtocol(); + MinecraftProtocol packetProtocol = this.session.getPacketProtocol(); PacketRegistry packetRegistry = packetProtocol.getInboundPacketRegistry(); Packet packet = null; try { @@ -104,7 +103,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List out) PacketErrorEvent e = new PacketErrorEvent(this.session, t, packet); this.session.callEvent(e); if (!e.shouldSuppress()) { - throw new DecoderException(t); + throw t; } } finally { if (packet != null && packet.isTerminal()) { diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketCompressionCodec.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketCompressionCodec.java index 5abff5602..254ec860b 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketCompressionCodec.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketCompressionCodec.java @@ -2,8 +2,8 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageCodec; import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.MessageToMessageCodec; import lombok.RequiredArgsConstructor; import org.geysermc.mcprotocollib.network.NetworkConstants; import org.geysermc.mcprotocollib.network.compression.CompressionConfig; @@ -12,7 +12,7 @@ import java.util.List; @RequiredArgsConstructor -public class PacketCompressionCodec extends MessageToMessageCodec { +public class PacketCompressionCodec extends ByteToMessageCodec { private static final int MAX_UNCOMPRESSED_SIZE = 8 * 1024 * 1024; // 8MiB @Override @@ -26,10 +26,10 @@ public void handlerRemoved(ChannelHandlerContext ctx) { } @Override - public void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) { + public void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) { CompressionConfig config = ctx.channel().attr(NetworkConstants.COMPRESSION_ATTRIBUTE_KEY).get(); if (config == null) { - out.add(msg.retain()); + out.writeBytes(msg); return; } @@ -39,29 +39,33 @@ public void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) { } ByteBuf outBuf = ctx.alloc().directBuffer(uncompressed); - if (uncompressed < config.threshold()) { - // Under the threshold, there is nothing to do. - MinecraftTypes.writeVarInt(outBuf, 0); - outBuf.writeBytes(msg); - } else { - MinecraftTypes.writeVarInt(outBuf, uncompressed); - config.compression().deflate(msg, outBuf); - } + try { + if (uncompressed < config.threshold()) { + // Under the threshold, there is nothing to do. + MinecraftTypes.writeVarInt(outBuf, 0); + outBuf.writeBytes(msg); + } else { + MinecraftTypes.writeVarInt(outBuf, uncompressed); + config.compression().deflate(msg, outBuf); + } - out.add(outBuf); + out.writeBytes(outBuf); + } finally { + outBuf.release(); + } } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { CompressionConfig config = ctx.channel().attr(NetworkConstants.COMPRESSION_ATTRIBUTE_KEY).get(); if (config == null) { - out.add(in.retain()); + out.add(in.readBytes(in.readableBytes())); return; } int claimedUncompressedSize = MinecraftTypes.readVarInt(in); if (claimedUncompressedSize == 0) { - out.add(in.retain()); + out.add(in.readBytes(in.readableBytes())); return; } diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketEncryptorCodec.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketEncryptorCodec.java index a0b5a1817..45c1e3c12 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketEncryptorCodec.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketEncryptorCodec.java @@ -3,20 +3,20 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageCodec; import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.MessageToMessageCodec; import org.geysermc.mcprotocollib.network.NetworkConstants; import org.geysermc.mcprotocollib.network.crypt.EncryptionConfig; import java.util.List; -public class PacketEncryptorCodec extends MessageToMessageCodec { +public class PacketEncryptorCodec extends ByteToMessageCodec { @Override - public void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) { + public void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) { EncryptionConfig config = ctx.channel().attr(NetworkConstants.ENCRYPTION_ATTRIBUTE_KEY).get(); if (config == null) { - out.add(msg.retain()); + out.writeBytes(msg); return; } @@ -27,10 +27,11 @@ public void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) { try { config.encryption().encrypt(heapBuf.array(), baseOffset, inBytes, heapBuf.array(), baseOffset); - out.add(heapBuf); + out.writeBytes(heapBuf); } catch (Exception e) { - heapBuf.release(); throw new EncoderException("Error encrypting packet", e); + } finally { + heapBuf.release(); } } @@ -38,7 +39,7 @@ public void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) { protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { EncryptionConfig config = ctx.channel().attr(NetworkConstants.ENCRYPTION_ATTRIBUTE_KEY).get(); if (config == null) { - out.add(in.retain()); + out.add(in.readBytes(in.readableBytes())); return; } diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketSizerCodec.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketSizerCodec.java index 50908194c..d1eab3c35 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketSizerCodec.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/netty/PacketSizerCodec.java @@ -29,30 +29,30 @@ public void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) { } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List out) { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { int size = header.getLengthSize(); if (size == 0) { - out.add(buf.retain()); + out.add(in.readBytes(in.readableBytes())); return; } - buf.markReaderIndex(); + in.markReaderIndex(); byte[] lengthBytes = new byte[size]; for (int index = 0; index < lengthBytes.length; index++) { - if (!buf.isReadable()) { - buf.resetReaderIndex(); + if (!in.isReadable()) { + in.resetReaderIndex(); return; } - lengthBytes[index] = buf.readByte(); + lengthBytes[index] = in.readByte(); if ((header.isLengthVariable() && lengthBytes[index] >= 0) || index == size - 1) { - int length = header.readLength(Unpooled.wrappedBuffer(lengthBytes), buf.readableBytes()); - if (buf.readableBytes() < length) { - buf.resetReaderIndex(); + int length = header.readLength(Unpooled.wrappedBuffer(lengthBytes), in.readableBytes()); + if (in.readableBytes() < length) { + in.resetReaderIndex(); return; } - out.add(buf.readBytes(length)); + out.add(in.readBytes(length)); return; } } diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/server/AbstractServer.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/server/AbstractServer.java index fb4bee950..e7353290b 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/server/AbstractServer.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/server/AbstractServer.java @@ -11,7 +11,7 @@ import org.geysermc.mcprotocollib.network.event.server.ServerListener; import org.geysermc.mcprotocollib.network.event.server.SessionAddedEvent; import org.geysermc.mcprotocollib.network.event.server.SessionRemovedEvent; -import org.geysermc.mcprotocollib.network.packet.PacketProtocol; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import java.net.SocketAddress; import java.util.ArrayList; @@ -23,14 +23,14 @@ public abstract class AbstractServer implements Server { private final SocketAddress bindAddress; - private final Supplier protocolSupplier; + private final Supplier protocolSupplier; private final List sessions = new ArrayList<>(); private final Map flags = new HashMap<>(); private final List listeners = new ArrayList<>(); - public AbstractServer(SocketAddress bindAddress, Supplier protocolSupplier) { + public AbstractServer(SocketAddress bindAddress, Supplier protocolSupplier) { this.bindAddress = bindAddress; this.protocolSupplier = protocolSupplier; } @@ -41,11 +41,11 @@ public SocketAddress getBindAddress() { } @Override - public Supplier getPacketProtocol() { + public Supplier getPacketProtocol() { return this.protocolSupplier; } - protected PacketProtocol createPacketProtocol() { + protected MinecraftProtocol createPacketProtocol() { return this.protocolSupplier.get(); } diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/server/NetworkServer.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/server/NetworkServer.java index e30c8e6dc..16572c1a2 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/server/NetworkServer.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/server/NetworkServer.java @@ -12,9 +12,9 @@ import org.geysermc.mcprotocollib.network.helper.TransportHelper; import org.geysermc.mcprotocollib.network.netty.DefaultPacketHandlerExecutor; import org.geysermc.mcprotocollib.network.netty.MinecraftChannelInitializer; -import org.geysermc.mcprotocollib.network.packet.PacketProtocol; import org.geysermc.mcprotocollib.network.session.NetworkSession; import org.geysermc.mcprotocollib.network.session.ServerNetworkSession; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,11 +31,11 @@ public class NetworkServer extends AbstractServer { private EventLoopGroup workerGroup; private Channel channel; - public NetworkServer(SocketAddress bindAddress, Supplier protocol) { + public NetworkServer(SocketAddress bindAddress, Supplier protocol) { this(bindAddress, protocol, DefaultPacketHandlerExecutor::createExecutor); } - public NetworkServer(SocketAddress bindAddress, Supplier protocol, Supplier packetHandlerExecutorFactory) { + public NetworkServer(SocketAddress bindAddress, Supplier protocol, Supplier packetHandlerExecutorFactory) { super(bindAddress, protocol); this.packetHandlerExecutorFactory = packetHandlerExecutorFactory; } @@ -104,7 +104,7 @@ protected void setOptions(ServerBootstrap bootstrap) { protected ChannelHandler getChannelHandler() { return new MinecraftChannelInitializer<>(channel -> { - PacketProtocol protocol = createPacketProtocol(); + MinecraftProtocol protocol = createPacketProtocol(); NetworkSession session = new ServerNetworkSession(channel.remoteAddress(), protocol, NetworkServer.this, packetHandlerExecutorFactory.get()); session.getPacketProtocol().newServerSession(NetworkServer.this, session); diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/ClientNetworkSession.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/ClientNetworkSession.java index 4048ede38..9cd4df2be 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/ClientNetworkSession.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/ClientNetworkSession.java @@ -15,7 +15,7 @@ import org.geysermc.mcprotocollib.network.helper.NettyHelper; import org.geysermc.mcprotocollib.network.helper.TransportHelper; import org.geysermc.mcprotocollib.network.netty.MinecraftChannelInitializer; -import org.geysermc.mcprotocollib.network.packet.PacketProtocol; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; @@ -37,7 +37,7 @@ public class ClientNetworkSession extends NetworkSession implements ClientSessio public ClientNetworkSession( @NonNull SocketAddress remoteAddress, - @NonNull PacketProtocol protocol, + @NonNull MinecraftProtocol protocol, @NonNull Executor packetHandlerExecutor, @Nullable SocketAddress bindAddress, @Nullable ProxyInfo proxy @@ -67,7 +67,7 @@ public void connect(boolean wait) { CompletableFuture handleFuture = new CompletableFuture<>(); bootstrap.connect().addListener((futureListener) -> { if (!futureListener.isSuccess()) { - exceptionCaught(null, futureListener.cause()); + this.disconnect(this.getGenericDisconnectMessage(futureListener.cause()), futureListener.cause()); } handleFuture.complete(null); @@ -104,7 +104,7 @@ protected void setOptions(Bootstrap bootstrap) { protected ChannelHandler getChannelHandler() { return new MinecraftChannelInitializer<>(channel -> { - PacketProtocol protocol = getPacketProtocol(); + MinecraftProtocol protocol = getPacketProtocol(); protocol.newClientSession(ClientNetworkSession.this); return ClientNetworkSession.this; diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/NetworkSession.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/NetworkSession.java index 61401b42f..71837f6dd 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/NetworkSession.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/NetworkSession.java @@ -19,7 +19,7 @@ import org.geysermc.mcprotocollib.network.event.session.SessionEvent; import org.geysermc.mcprotocollib.network.event.session.SessionListener; import org.geysermc.mcprotocollib.network.packet.Packet; -import org.geysermc.mcprotocollib.network.packet.PacketProtocol; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,25 +28,35 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; import java.util.function.Supplier; public abstract class NetworkSession extends SimpleChannelInboundHandler implements Session { private static final Logger log = LoggerFactory.getLogger(NetworkSession.class); protected final SocketAddress remoteAddress; - protected final PacketProtocol protocol; + protected final MinecraftProtocol protocol; protected final Executor packetHandlerExecutor; + private final Queue> pendingActions = new ConcurrentLinkedQueue<>(); private final Map flags = new HashMap<>(); private final List listeners = new CopyOnWriteArrayList<>(); private Channel channel; protected boolean disconnected = false; - - public NetworkSession(SocketAddress remoteAddress, PacketProtocol protocol, Executor packetHandlerExecutor) { + @Nullable + private volatile Component delayedDisconnect; + + public NetworkSession( + @NonNull SocketAddress remoteAddress, + @NonNull MinecraftProtocol protocol, + @NonNull Executor packetHandlerExecutor + ) { this.remoteAddress = remoteAddress; this.protocol = protocol; this.packetHandlerExecutor = packetHandlerExecutor; @@ -63,7 +73,7 @@ public SocketAddress getRemoteAddress() { } @Override - public PacketProtocol getPacketProtocol() { + public MinecraftProtocol getPacketProtocol() { return this.protocol; } @@ -123,7 +133,7 @@ public void callEvent(SessionEvent event) { event.call(listener); } } catch (Throwable t) { - exceptionCaught(null, t); + this.disconnect(this.getGenericDisconnectMessage(t), t); } } @@ -134,7 +144,7 @@ public void callPacketReceived(Packet packet) { listener.packetReceived(this, packet); } } catch (Throwable t) { - exceptionCaught(null, t); + this.disconnect(this.getGenericDisconnectMessage(t), t); } } @@ -145,7 +155,7 @@ public void callPacketSent(Packet packet) { listener.packetSent(this, packet); } } catch (Throwable t) { - exceptionCaught(null, t); + this.disconnect(this.getGenericDisconnectMessage(t), t); } } @@ -171,51 +181,63 @@ public void setEncryption(@Nullable EncryptionConfig encryptionConfig) { @Override public boolean isConnected() { - return this.channel != null && this.channel.isOpen() && !this.disconnected; + return this.channel != null && this.channel.isOpen(); } @Override public void send(@NonNull Packet packet, @Nullable Runnable onSent) { - if (this.channel == null) { - return; + if (this.isConnected()) { + this.flushQueue(); + this.sendPacket(packet, onSent); + } else { + this.pendingActions.add(session -> session.sendPacket(packet, onSent)); } + } - // Same behaviour as vanilla, always offload packet sending to the event loop - if (!this.channel.eventLoop().inEventLoop()) { - this.channel.eventLoop().execute(() -> this.send(packet, onSent)); - return; + private void sendPacket(@NonNull Packet packet, @Nullable Runnable onSent) { + if (this.channel.eventLoop().inEventLoop()) { + this.doSendPacket(packet, onSent); + } else { + this.channel.eventLoop().execute(() -> this.doSendPacket(packet, onSent)); } + } + private void doSendPacket(@NonNull Packet packet, @Nullable Runnable onSent) { PacketSendingEvent sendingEvent = new PacketSendingEvent(this, packet); this.callEvent(sendingEvent); if (!sendingEvent.isCancelled()) { final Packet toSend = sendingEvent.getPacket(); this.channel.writeAndFlush(toSend).addListener((ChannelFutureListener) future -> { - if (future.isSuccess()) { - if (onSent != null) { - onSent.run(); - } - - callPacketSent(toSend); - } else { - exceptionCaught(null, future.cause()); + if (!future.isSuccess()) { + return; } - }); + + if (onSent != null) { + onSent.run(); + } + + callPacketSent(toSend); + }).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); } } @Override public void disconnect(@NonNull Component reason, @Nullable Throwable cause) { + if (this.channel == null) { + this.delayedDisconnect = reason; + } + if (this.disconnected) { return; } this.disconnected = true; - if (this.channel != null && this.channel.isOpen()) { + if (this.isConnected()) { this.callEvent(new DisconnectingEvent(this, reason, cause)); - this.channel.flush().close().addListener((ChannelFutureListener) future -> callEvent(new DisconnectedEvent(NetworkSession.this, reason, cause))); + this.channel.flush().close().awaitUninterruptibly(); + this.callEvent(new DisconnectedEvent(NetworkSession.this, reason, cause)); } else { this.callEvent(new DisconnectedEvent(this, reason, cause)); } @@ -240,37 +262,59 @@ public Executor getPacketHandlerExecutor() { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { - if (this.disconnected || this.channel != null) { - ctx.channel().close(); - return; - } + super.channelActive(ctx); this.channel = ctx.channel(); - this.callEvent(new ConnectedEvent(this)); + Component delayedDisconnect = this.delayedDisconnect; + if (delayedDisconnect != null) { + this.disconnect(delayedDisconnect); + } else { + this.callEvent(new ConnectedEvent(this)); + this.flushQueue(); + } } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (ctx.channel() == this.channel) { - this.disconnect(Component.translatable("disconnect.endOfStream")); - } + this.disconnect(Component.translatable("disconnect.endOfStream")); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - Component message; - if (cause instanceof TimeoutException) { - message = Component.translatable("disconnect.timeout"); - } else { - message = Component.translatable("disconnect.genericReason", Component.text("Internal Exception: " + cause)); + if (this.channel.isOpen()) { + Component message; + if (cause instanceof TimeoutException) { + message = Component.translatable("disconnect.timeout"); + } else { + message = this.getGenericDisconnectMessage(cause); + } + + this.disconnect(message, cause); + } + } + + private void flushQueue() { + if (this.channel != null && this.channel.isOpen()) { + synchronized (this.pendingActions) { + Consumer consumer; + while ((consumer = this.pendingActions.poll()) != null) { + consumer.accept(this); + } + } } + } - this.disconnect(message, cause); + protected Component getGenericDisconnectMessage(Throwable cause) { + return Component.translatable("disconnect.genericReason", Component.text("Internal Exception: " + cause)); } @Override protected void channelRead0(ChannelHandlerContext ctx, Packet packet) { + if (!this.channel.isOpen()) { + return; + } + if (packet.shouldRunOnGameThread()) { packetHandlerExecutor.execute(() -> this.callPacketReceived(packet)); } else { diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/ServerNetworkSession.java b/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/ServerNetworkSession.java index 04229a6d9..56191c6d5 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/ServerNetworkSession.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/network/session/ServerNetworkSession.java @@ -1,10 +1,11 @@ package org.geysermc.mcprotocollib.network.session; import io.netty.channel.ChannelHandlerContext; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.mcprotocollib.network.Flag; import org.geysermc.mcprotocollib.network.ServerSession; -import org.geysermc.mcprotocollib.network.packet.PacketProtocol; import org.geysermc.mcprotocollib.network.server.NetworkServer; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import java.net.SocketAddress; import java.util.HashMap; @@ -15,7 +16,12 @@ public class ServerNetworkSession extends NetworkSession implements ServerSession { private final NetworkServer server; - public ServerNetworkSession(SocketAddress remoteAddress, PacketProtocol protocol, NetworkServer server, Executor packetHandlerExecutor) { + public ServerNetworkSession( + @NonNull SocketAddress remoteAddress, + @NonNull MinecraftProtocol protocol, + @NonNull NetworkServer server, + @NonNull Executor packetHandlerExecutor + ) { super(remoteAddress, protocol, packetHandlerExecutor); this.server = server; } diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/ClientListener.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/ClientListener.java index b1b7c9448..3d56b94ad 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/ClientListener.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/ClientListener.java @@ -62,7 +62,7 @@ public class ClientListener extends SessionAdapter { @SneakyThrows @Override public void packetReceived(Session session, Packet packet) { - MinecraftProtocol protocol = (MinecraftProtocol) session.getPacketProtocol(); + MinecraftProtocol protocol = session.getPacketProtocol(); if (protocol.getInboundState() == ProtocolState.LOGIN) { if (packet instanceof ClientboundHelloPacket helloPacket) { GameProfile profile = session.getFlag(MinecraftConstants.PROFILE_KEY); @@ -184,7 +184,7 @@ public void packetReceived(Session session, Packet packet) { @Override public void connected(ConnectedEvent event) { Session session = event.getSession(); - MinecraftProtocol protocol = (MinecraftProtocol) session.getPacketProtocol(); + MinecraftProtocol protocol = session.getPacketProtocol(); ClientIntentionPacket intention = new ClientIntentionPacket( protocol.getCodec().getProtocolVersion(), session.getFlagSupplied(MinecraftConstants.CLIENT_HOST, () -> ((InetSocketAddress) session.getRemoteAddress()).getHostString()), diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/ServerListener.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/ServerListener.java index d6f5c9b24..4769f445e 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/ServerListener.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/ServerListener.java @@ -28,16 +28,16 @@ import org.geysermc.mcprotocollib.protocol.packet.configuration.serverbound.ServerboundFinishConfigurationPacket; import org.geysermc.mcprotocollib.protocol.packet.handshake.serverbound.ClientIntentionPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundConfigurationAcknowledgedPacket; -import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundLoginFinishedPacket; import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundHelloPacket; import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundLoginCompressionPacket; import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundLoginDisconnectPacket; +import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundLoginFinishedPacket; import org.geysermc.mcprotocollib.protocol.packet.login.serverbound.ServerboundHelloPacket; import org.geysermc.mcprotocollib.protocol.packet.login.serverbound.ServerboundKeyPacket; import org.geysermc.mcprotocollib.protocol.packet.login.serverbound.ServerboundLoginAcknowledgedPacket; import org.geysermc.mcprotocollib.protocol.packet.ping.clientbound.ClientboundPongResponsePacket; -import org.geysermc.mcprotocollib.protocol.packet.status.clientbound.ClientboundStatusResponsePacket; import org.geysermc.mcprotocollib.protocol.packet.ping.serverbound.ServerboundPingRequestPacket; +import org.geysermc.mcprotocollib.protocol.packet.status.clientbound.ClientboundStatusResponsePacket; import org.geysermc.mcprotocollib.protocol.packet.status.serverbound.ServerboundStatusRequestPacket; import javax.crypto.SecretKey; @@ -95,7 +95,7 @@ public void connected(ConnectedEvent event) { @Override public void packetReceived(Session session, Packet packet) { - MinecraftProtocol protocol = (MinecraftProtocol) session.getPacketProtocol(); + MinecraftProtocol protocol = session.getPacketProtocol(); if (protocol.getInboundState() == ProtocolState.HANDSHAKE) { if (packet instanceof ClientIntentionPacket intentionPacket) { switch (intentionPacket.getIntent()) { @@ -231,7 +231,7 @@ private void beginLogin(Session session, MinecraftProtocol protocol, ClientInten @Override public void disconnecting(DisconnectingEvent event) { Session session = event.getSession(); - MinecraftProtocol protocol = (MinecraftProtocol) session.getPacketProtocol(); + MinecraftProtocol protocol = session.getPacketProtocol(); if (protocol.getOutboundState() == ProtocolState.LOGIN) { session.send(new ClientboundLoginDisconnectPacket(event.getReason())); } else if (protocol.getOutboundState() == ProtocolState.GAME) { diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/entity/metadata/MetadataTypes.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/entity/metadata/MetadataTypes.java index 487f04554..a21a6fa7a 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/entity/metadata/MetadataTypes.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/entity/metadata/MetadataTypes.java @@ -26,7 +26,7 @@ import java.util.UUID; @Getter -public class MetadataTypes { +public class MetadataTypes { private static final List> VALUES = new ArrayList<>(); public static final ByteMetadataType BYTE = register(id -> new ByteMetadataType(id, ByteBuf::readByte, ByteBuf::writeByte, ByteEntityMetadata::new)); diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/item/component/DataComponentTypes.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/item/component/DataComponentTypes.java index b2af15c6d..250f548d8 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/item/component/DataComponentTypes.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/item/component/DataComponentTypes.java @@ -19,7 +19,7 @@ import java.util.List; @Getter -public class DataComponentTypes { +public class DataComponentTypes { private static final List> VALUES = new ArrayList<>(); public static final DataComponentType CUSTOM_DATA = register(id -> new DataComponentType<>(id, "custom_data", MinecraftTypes::readCompoundTag, MinecraftTypes::writeAnyTag, ObjectDataComponent::new));