| author | b0in
<b0in@proton.me> 2023-12-07 00:37:50 UTC |
| committer | b0in
<b0in@proton.me> 2023-12-07 00:41:54 UTC |
| parent | 5fd71e2dbd46645f96ab76af97dcbd131b8b3687 |
| crow.game.codec.api/src/main/java/crow/game/codec/Context.java | +37 | -39 |
| crow.game.codec.api/src/main/java/crow/game/codec/InetAddressAndPort.java | +1 | -4 |
| crow.game.codec.api/src/main/java/crow/game/codec/Packet.java | +1 | -2 |
| crow.game.codec.api/src/main/java/crow/game/codec/PacketHandler.java | +77 | -89 |
| crow.game.codec.api/src/main/java/crow/game/codec/PacketRule.java | +83 | -94 |
| crow.game.codec.api/src/main/java/crow/game/codec/package-info.java | +25 | -23 |
| crow.game.codec.netty/pom.xml | +0 | -3 |
| crow.game.codec.netty/src/main/java/crow/game/codec/netty/FrameCodec.java | +65 | -84 |
| crow.game.codec.netty/src/main/java/crow/game/codec/netty/NettyWrappedContext.java | +38 | -41 |
| crow.game.codec.netty/src/main/java/crow/game/codec/netty/PacketCodec.java | +37 | -55 |
| crow.game.codec.netty/src/main/java/crow/game/codec/netty/package-info.java | +1 | -4 |
| crow.game.examples.pingpong/pom.xml | +4 | -0 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/MainClient.java | +22 | -29 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/PingPongClientInitializer.java | +39 | -50 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/package-info.java | +1 | -3 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Constants.java | +3 | -3 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Ping.java | +1 | -3 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PingPacketHandler.java | +13 | -14 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Pong.java | +1 | -3 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PongPacketHandler.java | +13 | -13 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/RootContext.java | +46 | -50 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/package-info.java | +13 | -11 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/MainServer.java | +24 | -30 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/PingPongServerInitializer.java | +42 | -58 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/package-info.java | +1 | -3 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/Main.java | +13 | -15 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/PrefixThreadFactory.java | +13 | -17 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/package-info.java | +1 | -3 |
| pom.xml | +37 | -17 |
diff --git a/crow.game.codec.api/src/main/java/crow/game/codec/Context.java b/crow.game.codec.api/src/main/java/crow/game/codec/Context.java index 14da0b3..0ac7523 100644 --- a/crow.game.codec.api/src/main/java/crow/game/codec/Context.java +++ b/crow.game.codec.api/src/main/java/crow/game/codec/Context.java @@ -4,45 +4,43 @@ import java.util.List; /** * Context wraps the calling context when performing packet logic. - * <p> - * It allows packet handlers to query details about the running system that may - * change based on the given client, the network type, whether we are encoding - * or decoding the packet. + * + * <p>It allows packet handlers to query details about the running system that may change based on + * the given client, the network type, whether we are encoding or decoding the packet. */ public interface Context { - /** - * @return the current RO protocol version as an integer. - */ - int supportedProtocolVersion(); - - List<PacketHandler<?>> lookupPacketHandler(int prefix); - List<PacketHandler<?>> lookupPacketHandler(Class<?> clz); - - /** - * @return the underlying object, usually a Netty connection object. - */ - Object underlyingObject(); - - /** - * @return the location of the codec workflow this context was built in. - */ - Source callingSource(); - - /** - * Source determines where the context was constructed and called into. Packet Rules - * can be constructed in the INIT phase, the DECODE phase, or the ENCODE phase - * and packet handlers may want to know which is being called for now. - */ - enum Source { - INIT, - DECODE, - ENCODE, - } - - default boolean matchesPacketVersion(PacketRule pr) { - return ( - this.supportedProtocolVersion() >= pr.minVersion() && - this.supportedProtocolVersion() <= pr.maxVersion() - ); - } + /** + * @return the current RO protocol version as an integer. + */ + int supportedProtocolVersion(); + + List<PacketHandler<?>> lookupPacketHandler(int prefix); + + List<PacketHandler<?>> lookupPacketHandler(Class<?> clz); + + /** + * @return the underlying object, usually a Netty connection object. + */ + Object underlyingObject(); + + /** + * @return the location of the codec workflow this context was built in. + */ + Source callingSource(); + + /** + * Source determines where the context was constructed and called into. Packet Rules can be + * constructed in the INIT phase, the DECODE phase, or the ENCODE phase and packet handlers may + * want to know which is being called for now. + */ + enum Source { + INIT, + DECODE, + ENCODE, + } + + default boolean matchesPacketVersion(PacketRule pr) { + return (this.supportedProtocolVersion() >= pr.minVersion() + && this.supportedProtocolVersion() <= pr.maxVersion()); + } } diff --git a/crow.game.codec.api/src/main/java/crow/game/codec/InetAddressAndPort.java b/crow.game.codec.api/src/main/java/crow/game/codec/InetAddressAndPort.java index 5038ae3..6392dfe 100644 --- a/crow.game.codec.api/src/main/java/crow/game/codec/InetAddressAndPort.java +++ b/crow.game.codec.api/src/main/java/crow/game/codec/InetAddressAndPort.java @@ -2,8 +2,5 @@ package crow.game.codec; import java.net.InetAddress; -/** - * Utility object for pairing the ip address and port together. - * - */ +/** Utility object for pairing the ip address and port together. */ public record InetAddressAndPort(InetAddress addr, int port) {} diff --git a/crow.game.codec.api/src/main/java/crow/game/codec/Packet.java b/crow.game.codec.api/src/main/java/crow/game/codec/Packet.java index f47ecbc..c61f951 100644 --- a/crow.game.codec.api/src/main/java/crow/game/codec/Packet.java +++ b/crow.game.codec.api/src/main/java/crow/game/codec/Packet.java @@ -5,7 +5,6 @@ import io.netty.buffer.ByteBuf; /** * The packet object pairs the byte stream with the matched rule. * - * The byte buffer does NOT contain the packet ID or length, that is already - * read. + * <p>The byte buffer does NOT contain the packet ID or length, that is already read. */ public record Packet(ByteBuf buffer, PacketRule rule) {} diff --git a/crow.game.codec.api/src/main/java/crow/game/codec/PacketHandler.java b/crow.game.codec.api/src/main/java/crow/game/codec/PacketHandler.java index b3715ba..dd473fb 100644 --- a/crow.game.codec.api/src/main/java/crow/game/codec/PacketHandler.java +++ b/crow.game.codec.api/src/main/java/crow/game/codec/PacketHandler.java @@ -5,104 +5,92 @@ import java.net.InetAddress; import java.net.UnknownHostException; /** - * Interface responsible for converting to and from Packet objects to a POJO - * type + * Interface responsible for converting to and from Packet objects to a POJO type * * @param <T> The POJO that maps to the given packet. * @see Context */ public interface PacketHandler<T> { - /** - * Convert the given buffer to the type. - * - * @param ctx The calling context. - * @param buffer The input buffer stream. - * - * @return The POJO object - * - * @throws Exception - */ - T fromPacket(Context ctx, ByteBuf buffer) throws Exception; + /** + * Convert the given buffer to the type. + * + * @param ctx The calling context. + * @param buffer The input buffer stream. + * @return The POJO object + * @throws Exception + */ + T fromPacket(Context ctx, ByteBuf buffer) throws Exception; - /** - * Writes the object of type T to the byte buffer. - * - * @param ctx The calling context. - * @param buffer The destination buffer. - * @param src The source object we are converting. - * - * @throws Exception - */ - void toPacket(Context ctx, ByteBuf buffer, T src) throws Exception; + /** + * Writes the object of type T to the byte buffer. + * + * @param ctx The calling context. + * @param buffer The destination buffer. + * @param src The source object we are converting. + * @throws Exception + */ + void toPacket(Context ctx, ByteBuf buffer, T src) throws Exception; - /** - * @see PacketRule - * - * @param ctx The calling context. - * @return The packet rule object which specifies when this packet handler - * should be called. - */ - PacketRule rule(Context ctx); + /** + * @see PacketRule + * @param ctx The calling context. + * @return The packet rule object which specifies when this packet handler should be called. + */ + PacketRule rule(Context ctx); - /** - * Parses the address and port pair from the kro server byte stream. - * - * @param buf The input buffer. - * @return The IP address and port pair - * @see InetAddressAndPort - * @throws UnknownHostException - */ - static InetAddressAndPort readInetAddressAndPort(ByteBuf buf) - throws UnknownHostException { - byte[] addrBuf = new byte[4]; - buf.readBytes(addrBuf); + /** + * Parses the address and port pair from the kro server byte stream. + * + * @param buf The input buffer. + * @return The IP address and port pair + * @see InetAddressAndPort + * @throws UnknownHostException + */ + static InetAddressAndPort readInetAddressAndPort(ByteBuf buf) throws UnknownHostException { + byte[] addrBuf = new byte[4]; + buf.readBytes(addrBuf); - InetAddress address = InetAddress.getByAddress(addrBuf); - int port = buf.readShort(); - return new InetAddressAndPort(address, port); - } + InetAddress address = InetAddress.getByAddress(addrBuf); + int port = buf.readShort(); + return new InetAddressAndPort(address, port); + } - /** - * Reads the string from the byte buffer. The byte buffer is read fully so you - * must pass in a sliced buffer of the given size. - * <p> - * Usage: - * <p> - * {@code - * PacketHandler.readStr(buffer.readSlice(N)); - * } - * <br/> - * where N is the length of the string field in the packet. - * - * <p> - * NOTE:Strings in KRO are 0-terminated and use pre-determined lengths. So - * "hello" of length 20 will be "hello" 0x0 0x0 0x0... - * - * @param buf The byte buffer to read from. - * @return The string object. - */ - static String readStr(ByteBuf buf) { - byte x = 0; - StringBuffer out = new StringBuffer(); - do { - x = buf.readByte(); - if (x != 0) { - out.append((char) x); - } - } while (x != 0); - return out.toString(); - } + /** + * Reads the string from the byte buffer. The byte buffer is read fully so you must pass in a + * sliced buffer of the given size. + * + * <p>Usage: + * + * <p>{@code PacketHandler.readStr(buffer.readSlice(N)); } <br> + * where N is the length of the string field in the packet. + * + * <p>NOTE:Strings in KRO are 0-terminated and use pre-determined lengths. So "hello" of length 20 + * will be "hello" 0x0 0x0 0x0... + * + * @param buf The byte buffer to read from. + * @return The string object. + */ + static String readStr(ByteBuf buf) { + byte x = 0; + StringBuffer out = new StringBuffer(); + do { + x = buf.readByte(); + if (x != 0) { + out.append((char) x); + } + } while (x != 0); + return out.toString(); + } - /** - * Writes the string to the given byte buffer, padding the given 0 bytes based - * on the length. - * - * @param buf The buffer to write to. - * @param str The string to write. - * @param len The length of the string in the byte stream. - */ - static void writeStr(ByteBuf buf, String str, int len) { - buf.writeBytes(str.getBytes()); - buf.writeZero(len - str.length()); - } + /** + * Writes the string to the given byte buffer, padding the given 0 bytes based on the length. + * + * @param buf The buffer to write to. + * @param str The string to write. + * @param len The length of the string in the byte stream. + */ + static void writeStr(ByteBuf buf, String str, int len) { + buf.writeBytes(str.getBytes()); + buf.writeZero(len - str.length()); + } } diff --git a/crow.game.codec.api/src/main/java/crow/game/codec/PacketRule.java b/crow.game.codec.api/src/main/java/crow/game/codec/PacketRule.java index 6c0136d..3a3caaa 100644 --- a/crow.game.codec.api/src/main/java/crow/game/codec/PacketRule.java +++ b/crow.game.codec.api/src/main/java/crow/game/codec/PacketRule.java @@ -1,105 +1,94 @@ package crow.game.codec; /** - * Represents a specification for which packet ID (labeled 'prefix' here) is of - * what type (STATIC or DYNAMIC), what name and Java class POJO, and what kRO - * packet version the POJO maps to. - * + * Represents a specification for which packet ID (labeled 'prefix' here) is of what type (STATIC or + * DYNAMIC), what name and Java class POJO, and what kRO packet version the POJO maps to. */ public record PacketRule( - int minVersion, int maxVersion, short prefix, int data, Type type, String name, Class<?> clz -) { - /** - * The type of packet, currently only 2. see classdoc. - */ - public enum Type { - STATIC, - DYNAMIC, - } + int minVersion, int maxVersion, short prefix, int data, Type type, String name, Class<?> clz) { + /** The type of packet, currently only 2. see classdoc. */ + public enum Type { + STATIC, + DYNAMIC, + } - /** - * Simpler packet rule constructor. - * - * @param prefix The 2-byte code prefix for matching the packet. - * @param data The static or dynamic size tag indicator. - * @param type The type of the packet, STATIC or DYNAMIC - * @param clz The class of the POJO we are serializing and deserializing. - */ - public PacketRule(short prefix, int data, Type type, Class<?> clz) { - this(-1, -1, prefix, data, type, clz.getSimpleName(), clz); - } + /** + * Simpler packet rule constructor. + * + * @param prefix The 2-byte code prefix for matching the packet. + * @param data The static or dynamic size tag indicator. + * @param type The type of the packet, STATIC or DYNAMIC + * @param clz The class of the POJO we are serializing and deserializing. + */ + public PacketRule(short prefix, int data, Type type, Class<?> clz) { + this(-1, -1, prefix, data, type, clz.getSimpleName(), clz); + } - /** - * Simpler packet rule constructor. - * - * @param prefix The 2-byte code prefix for matching the packet. - * @param data The static or dynamic size tag indicator. - * @param type The type of the packet, STATIC or DYNAMIC - * @param name The name of the packet, currently only for logging. - * @param clz The class of the POJO we are serializing and deserializing. - */ - public PacketRule( - short prefix, - int data, - Type type, - String name, - Class<?> clz - ) { - this(-1, -1, prefix, data, type, name, clz); - } + /** + * Simpler packet rule constructor. + * + * @param prefix The 2-byte code prefix for matching the packet. + * @param data The static or dynamic size tag indicator. + * @param type The type of the packet, STATIC or DYNAMIC + * @param name The name of the packet, currently only for logging. + * @param clz The class of the POJO we are serializing and deserializing. + */ + public PacketRule(short prefix, int data, Type type, String name, Class<?> clz) { + this(-1, -1, prefix, data, type, name, clz); + } - /** - * Packet rule constructor. - * - * @param minVersion the min version, in YYYYMMDD format, or -1 for "as early as possible". - * @param maxVersion the maximum packet version this rule supports, in YYYYMMDD format, or -1 for "most recent" - * @param prefix The 2-byte code prefix for matching the packet. - * @param data The static or dynamic size tag indicator. - * @param type The type of the packet, STATIC or DYNAMIC - * @param name The name of the packet, currently only for logging. - * @param clz The class of the POJO we are serializing and deserializing. - */ - public PacketRule( - int minVersion, - int maxVersion, - short prefix, - int data, - Type type, - String name, - Class<?> clz - ) { - this.minVersion = minVersion == -1 ? 19990101 : minVersion; - this.maxVersion = maxVersion == -1 ? 30000101 : maxVersion; - this.prefix = prefix; - this.data = data; - this.type = type; - this.name = name; - this.clz = clz; - } + /** + * Packet rule constructor. + * + * @param minVersion the min version, in YYYYMMDD format, or -1 for "as early as possible". + * @param maxVersion the maximum packet version this rule supports, in YYYYMMDD format, or -1 for + * "most recent" + * @param prefix The 2-byte code prefix for matching the packet. + * @param data The static or dynamic size tag indicator. + * @param type The type of the packet, STATIC or DYNAMIC + * @param name The name of the packet, currently only for logging. + * @param clz The class of the POJO we are serializing and deserializing. + */ + public PacketRule( + int minVersion, + int maxVersion, + short prefix, + int data, + Type type, + String name, + Class<?> clz) { + this.minVersion = minVersion == -1 ? 19990101 : minVersion; + this.maxVersion = maxVersion == -1 ? 30000101 : maxVersion; + this.prefix = prefix; + this.data = data; + this.type = type; + this.name = name; + this.clz = clz; + } - /** - * Utility constructor for building a non-packet version specific dynamic packet - * whose name is derived from the given class. - * - * @param prefix The 2-byte code prefix for matching the packet. - * @param sizeTag The size of the following "packet size" indicator, 2 or 4. - * @param clz The class that we are serializing and deserializing. - * @return The packet rule object. - */ - public static PacketRule Dynamic(short prefix, int sizeTag, Class<?> clz) { - return new PacketRule(prefix, sizeTag, Type.DYNAMIC, clz); - } + /** + * Utility constructor for building a non-packet version specific dynamic packet whose name is + * derived from the given class. + * + * @param prefix The 2-byte code prefix for matching the packet. + * @param sizeTag The size of the following "packet size" indicator, 2 or 4. + * @param clz The class that we are serializing and deserializing. + * @return The packet rule object. + */ + public static PacketRule Dynamic(short prefix, int sizeTag, Class<?> clz) { + return new PacketRule(prefix, sizeTag, Type.DYNAMIC, clz); + } - /** - * Utility constructor for building a non-packet version specific static packet - * whose name is derived from the given class. - * - * @param prefix The 2-byte code prefix for matching the packet. - * @param size The size of the following full packet. - * @param clz The class that we are serializing and deserializing. - * @return The packet rule object. - */ - public static PacketRule Static(short prefix, int size, Class<?> clz) { - return new PacketRule(prefix, size, Type.STATIC, clz); - } + /** + * Utility constructor for building a non-packet version specific static packet whose name is + * derived from the given class. + * + * @param prefix The 2-byte code prefix for matching the packet. + * @param size The size of the following full packet. + * @param clz The class that we are serializing and deserializing. + * @return The packet rule object. + */ + public static PacketRule Static(short prefix, int size, Class<?> clz) { + return new PacketRule(prefix, size, Type.STATIC, clz); + } } diff --git a/crow.game.codec.api/src/main/java/crow/game/codec/package-info.java b/crow.game.codec.api/src/main/java/crow/game/codec/package-info.java index a7e5470..b4b4345 100644 --- a/crow.game.codec.api/src/main/java/crow/game/codec/package-info.java +++ b/crow.game.codec.api/src/main/java/crow/game/codec/package-info.java @@ -1,27 +1,29 @@ /** * Package containing the base API objects for the protocol implementation. - * <p> - * This API is currently tied to Netty {@link io.netty.buffer.ByteBuf} but is - * otherwise decoupled from Netty. sub-package contains the Netty-specific code - * for building {@link crow.game.codec.Packet} objects. - * <p> - * TODO: switch to {@link java.nio.ByteBuffer} which can remove the netty dependency. - * <p> - * There are multiple hooks for handling differing packet versions in this API: - * <p> - * You can define your POJO as "MyPacket20NN0101" alongside {@link crow.game.codec.PacketHandler} "MyPacketHandler20NN0101" - * that returns a {@link crow.game.codec.PacketRule} with the version specifiers enabled. - * <p> - * Then you would have a second POJO that maps to everything that the previous packet did not. - * <p> - * OR - * <p> - * You can define a standard packet "MyPacket" and {@link crow.game.codec.PacketHandler} "MyPacketHandle" - * that, when you recieve calls into your handler, you can look at the current packet version - * via {@link crow.game.codec.Context}.supportedProtocolVersion and act accordingly via branching logic. - * <p> - * Implementors note: It's a long shot but maybe it will be possible to support multiple protocol versions in the same - * running server, via client version identification and pinning, then {@link crow.game.codec.Context} - * may contain additional account and client info attached. + * + * <p>This API is currently tied to Netty {@link io.netty.buffer.ByteBuf} but is otherwise decoupled + * from Netty. sub-package contains the Netty-specific code for building {@link + * crow.game.codec.Packet} objects. + * + * <p>TODO: switch to {@link java.nio.ByteBuffer} which can remove the netty dependency. + * + * <p>There are multiple hooks for handling differing packet versions in this API: + * + * <p>You can define your POJO as "MyPacket20NN0101" alongside {@link crow.game.codec.PacketHandler} + * "MyPacketHandler20NN0101" that returns a {@link crow.game.codec.PacketRule} with the version + * specifiers enabled. + * + * <p>Then you would have a second POJO that maps to everything that the previous packet did not. + * + * <p>OR + * + * <p>You can define a standard packet "MyPacket" and {@link crow.game.codec.PacketHandler} + * "MyPacketHandle" that, when you recieve calls into your handler, you can look at the current + * packet version via {@link crow.game.codec.Context}.supportedProtocolVersion and act accordingly + * via branching logic. + * + * <p>Implementors note: It's a long shot but maybe it will be possible to support multiple protocol + * versions in the same running server, via client version identification and pinning, then {@link + * crow.game.codec.Context} may contain additional account and client info attached. */ package crow.game.codec; diff --git a/crow.game.codec.netty/pom.xml b/crow.game.codec.netty/pom.xml index ec58861..643267a 100644 --- a/crow.game.codec.netty/pom.xml +++ b/crow.game.codec.netty/pom.xml @@ -17,13 +17,10 @@ <dependency> <groupId>io.netty</groupId> <artifactId>netty-buffer</artifactId> - <version>4.1.101.Final</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> - <version>1.7.35</version> - <scope>provided</scope> </dependency> </dependencies> </project> diff --git a/crow.game.codec.netty/src/main/java/crow/game/codec/netty/FrameCodec.java b/crow.game.codec.netty/src/main/java/crow/game/codec/netty/FrameCodec.java index 6af1a9d..1742f41 100644 --- a/crow.game.codec.netty/src/main/java/crow/game/codec/netty/FrameCodec.java +++ b/crow.game.codec.netty/src/main/java/crow/game/codec/netty/FrameCodec.java @@ -14,9 +14,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * The FrameCodec converts to and from {@link ByteBuf} streams into individual - * {@link crow.game.codec.Packet} objects. The first two bytes of a packet determine which type - * of packet it is (called the packet ID). + * The FrameCodec converts to and from {@link ByteBuf} streams into individual {@link + * crow.game.codec.Packet} objects. The first two bytes of a packet determine which type of packet + * it is (called the packet ID). * * @see crow.game.codec.PacketRule * @see crow.game.codec.Packet @@ -24,99 +24,80 @@ import org.slf4j.LoggerFactory; */ public class FrameCodec extends ByteToMessageCodec<Packet> { - static Logger logger = LoggerFactory.getLogger(FrameCodec.class); + static Logger logger = LoggerFactory.getLogger(FrameCodec.class); - // - protected final Context context; + // + protected final Context context; - public FrameCodec(Context context) { - this.context = context; - } + public FrameCodec(Context context) { + this.context = context; + } - protected boolean packetRuleHasMoreData(ByteBuf in, PacketRule pr) { - PacketRule.Type s = pr.type(); - return switch (s) { - case DYNAMIC -> true; - case STATIC -> pr.data() <= in.readableBytes() + 2; - }; - } + protected boolean packetRuleHasMoreData(ByteBuf in, PacketRule pr) { + PacketRule.Type s = pr.type(); + return switch (s) { + case DYNAMIC -> true; + case STATIC -> pr.data() <= in.readableBytes() + 2; + }; + } + + protected Optional<PacketRule> resolvePacketRule(Context ctx, int prefix, ByteBuf in) { + return ctx.lookupPacketHandler(prefix).stream() // + .map(((PacketHandler<?> ph) -> ph.rule(ctx))) + .filter((PacketRule pr) -> ctx.matchesPacketVersion(pr)) + .filter((PacketRule pr) -> packetRuleHasMoreData(in, pr)) + .findFirst(); + } + + @Override + protected void encode(ChannelHandlerContext ctx, Packet msg, ByteBuf out) { + PacketRule pr = msg.rule(); - protected Optional<PacketRule> resolvePacketRule( - Context ctx, - int prefix, - ByteBuf in - ) { - return ctx - .lookupPacketHandler(prefix) - .stream() // - .map(((PacketHandler<?> ph) -> ph.rule(ctx))) - .filter((PacketRule pr) -> ctx.matchesPacketVersion(pr)) - .filter((PacketRule pr) -> packetRuleHasMoreData(in, pr)) - .findFirst(); + short prefix = pr.prefix(); + out.writeShortLE(prefix); + if (pr.type() == PacketRule.Type.DYNAMIC) { + if (pr.data() == 2) { + out.writeShortLE(msg.buffer().readableBytes() + 2); + } else { + out.writeIntLE(msg.buffer().readableBytes() + 4); + } } - @Override - protected void encode(ChannelHandlerContext ctx, Packet msg, ByteBuf out) { - PacketRule pr = msg.rule(); + out.writeBytes(msg.buffer()); + } - short prefix = pr.prefix(); - out.writeShortLE(prefix); - if (pr.type() == PacketRule.Type.DYNAMIC) { - if (pr.data() == 2) { - out.writeShortLE(msg.buffer().readableBytes() + 2); - } else { - out.writeIntLE(msg.buffer().readableBytes() + 4); - } - } + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { + short x = in.getShortLE(in.readerIndex()); + String readableX = String.format("0x%x", x); + Context wrapped = new NettyWrappedContext(Context.Source.DECODE, this.context, ctx); - out.writeBytes(msg.buffer()); + Optional<PacketRule> rule = resolvePacketRule(wrapped, x, in); + if (rule.isEmpty()) { + logger.warn("no packet rule for {} - {}", readableX, x); + throw new UnsupportedMessageTypeException(); } - @Override - protected void decode( - ChannelHandlerContext ctx, - ByteBuf in, - List<Object> out - ) { - short x = in.getShortLE(in.readerIndex()); - String readableX = String.format("0x%x", x); - Context wrapped = new NettyWrappedContext( - Context.Source.DECODE, - this.context, - ctx - ); + PacketRule packetRule = rule.get(); - Optional<PacketRule> rule = resolvePacketRule(wrapped, x, in); - if (rule.isEmpty()) { - logger.warn("no packet rule for {} - {}", readableX, x); - throw new UnsupportedMessageTypeException(); + if (packetRule.type().equals(PacketRule.Type.STATIC)) { + out.add(new Packet(in.readBytes(rule.get().data() + 2).copy(), rule.get())); + } else { + int size = -1; + // all packets sizes are just the 2 or 4 byte words AFTER the packet id + if (packetRule.data() == 2) { + if (in.readableBytes() >= 2 + 2) { + size = in.getShortLE(in.readerIndex() + 2); } - - PacketRule packetRule = rule.get(); - - if (packetRule.type().equals(PacketRule.Type.STATIC)) { - out.add( - new Packet( - in.readBytes(rule.get().data() + 2).copy(), - rule.get() - ) - ); - } else { - int size = -1; - // all packets sizes are just the 2 or 4 byte words AFTER the packet id - if (packetRule.data() == 2) { - if (in.readableBytes() >= 2 + 2) { - size = in.getShortLE(in.readerIndex() + 2); - } - } else { - if (in.readableBytes() > 2 + 4) { - size = in.getIntLE(in.readerIndex() + 2); - } - } - if (in.readableBytes() < 2 + size) { - return; - } - out.add(new Packet(in.readBytes(size), rule.get())); + } else { + if (in.readableBytes() > 2 + 4) { + size = in.getIntLE(in.readerIndex() + 2); } + } + if (in.readableBytes() < 2 + size) { + return; + } + out.add(new Packet(in.readBytes(size), rule.get())); } + } } diff --git a/crow.game.codec.netty/src/main/java/crow/game/codec/netty/NettyWrappedContext.java b/crow.game.codec.netty/src/main/java/crow/game/codec/netty/NettyWrappedContext.java index 09e1bb6..ffe6e70 100644 --- a/crow.game.codec.netty/src/main/java/crow/game/codec/netty/NettyWrappedContext.java +++ b/crow.game.codec.netty/src/main/java/crow/game/codec/netty/NettyWrappedContext.java @@ -6,48 +6,45 @@ import io.netty.channel.ChannelHandlerContext; import java.util.List; /** - * Implementation of {@link Context} which wraps an upper-level context and sets the - * {@link crow.game.codec.Context.Source} to the given value and sets the underlying object to an - * instance of {@link ChannelHandlerContext}. + * Implementation of {@link Context} which wraps an upper-level context and sets the {@link + * crow.game.codec.Context.Source} to the given value and sets the underlying object to an instance + * of {@link ChannelHandlerContext}. */ public class NettyWrappedContext implements Context { - protected final Context context; - protected final ChannelHandlerContext nettyContext; - protected final Context.Source source; - - public NettyWrappedContext( - Context.Source src, - Context context, - ChannelHandlerContext nettyContext - ) { - this.context = context; - this.nettyContext = nettyContext; - this.source = src; - } - - @Override - public int supportedProtocolVersion() { - return this.context.supportedProtocolVersion(); - } - - @Override - public List<PacketHandler<?>> lookupPacketHandler(Class<?> clz) { - return this.context.lookupPacketHandler(clz); - } - - @Override - public List<PacketHandler<?>> lookupPacketHandler(int prefix) { - return this.context.lookupPacketHandler(prefix); - } - - @Override - public Object underlyingObject() { - return this.nettyContext; - } - - @Override - public Source callingSource() { - return this.source; - } + protected final Context context; + protected final ChannelHandlerContext nettyContext; + protected final Context.Source source; + + public NettyWrappedContext( + Context.Source src, Context context, ChannelHandlerContext nettyContext) { + this.context = context; + this.nettyContext = nettyContext; + this.source = src; + } + + @Override + public int supportedProtocolVersion() { + return this.context.supportedProtocolVersion(); + } + + @Override + public List<PacketHandler<?>> lookupPacketHandler(Class<?> clz) { + return this.context.lookupPacketHandler(clz); + } + + @Override + public List<PacketHandler<?>> lookupPacketHandler(int prefix) { + return this.context.lookupPacketHandler(prefix); + } + + @Override + public Object underlyingObject() { + return this.nettyContext; + } + + @Override + public Source callingSource() { + return this.source; + } } diff --git a/crow.game.codec.netty/src/main/java/crow/game/codec/netty/PacketCodec.java b/crow.game.codec.netty/src/main/java/crow/game/codec/netty/PacketCodec.java index 3e9e912..80472ad 100644 --- a/crow.game.codec.netty/src/main/java/crow/game/codec/netty/PacketCodec.java +++ b/crow.game.codec.netty/src/main/java/crow/game/codec/netty/PacketCodec.java @@ -15,62 +15,44 @@ import java.util.List; */ public class PacketCodec extends MessageToMessageCodec<Packet, Object> { - protected final Context context; - - public PacketCodec(Context ctx) { - this.context = ctx; - } - - @Override - protected void decode( - ChannelHandlerContext ctx, - Packet msg, - List<Object> out - ) throws Exception { - Context wrCtx = new NettyWrappedContext( - Context.Source.DECODE, - context, - ctx - ); - PacketHandler<?> h = - ( - wrCtx - .lookupPacketHandler(msg.rule().prefix()) // - .stream() // - .filter(hx -> wrCtx.matchesPacketVersion(hx.rule(wrCtx))) // - .findFirst() // - .orElseThrow() - ); - - msg.buffer().readShortLE(); // drop the packet ID - out.add(h.fromPacket(wrCtx, msg.buffer())); - } - - @Override - protected void encode( - ChannelHandlerContext ctx, - Object msg, - List<Object> out - ) throws Exception { - Context wrCtx = new NettyWrappedContext( - Context.Source.ENCODE, - this.context, - ctx - ); - - @SuppressWarnings("unchecked") - PacketHandler<Object> h = (PacketHandler<Object>) wrCtx - .lookupPacketHandler(msg.getClass()) + protected final Context context; + + public PacketCodec(Context ctx) { + this.context = ctx; + } + + @Override + protected void decode(ChannelHandlerContext ctx, Packet msg, List<Object> out) throws Exception { + Context wrCtx = new NettyWrappedContext(Context.Source.DECODE, context, ctx); + PacketHandler<?> h = + (wrCtx + .lookupPacketHandler(msg.rule().prefix()) // .stream() // .filter(hx -> wrCtx.matchesPacketVersion(hx.rule(wrCtx))) // .findFirst() // - .orElseThrow(); - - // TODO: idk, pool this? use better buffer sizes?? - // TODO: does this leak - ByteBuf directBuffer = Unpooled.directBuffer(1024); - - h.toPacket(wrCtx, directBuffer, msg); - out.add(new Packet(directBuffer, h.rule(wrCtx))); - } + .orElseThrow()); + + msg.buffer().readShortLE(); // drop the packet ID + out.add(h.fromPacket(wrCtx, msg.buffer())); + } + + @Override + protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception { + Context wrCtx = new NettyWrappedContext(Context.Source.ENCODE, this.context, ctx); + + @SuppressWarnings("unchecked") + PacketHandler<Object> h = + (PacketHandler<Object>) + wrCtx.lookupPacketHandler(msg.getClass()).stream() // + .filter(hx -> wrCtx.matchesPacketVersion(hx.rule(wrCtx))) // + .findFirst() // + .orElseThrow(); + + // TODO: idk, pool this? use better buffer sizes?? + // TODO: does this leak + ByteBuf directBuffer = Unpooled.directBuffer(1024); + + h.toPacket(wrCtx, directBuffer, msg); + out.add(new Packet(directBuffer, h.rule(wrCtx))); + } } diff --git a/crow.game.codec.netty/src/main/java/crow/game/codec/netty/package-info.java b/crow.game.codec.netty/src/main/java/crow/game/codec/netty/package-info.java index 92c9c67..3d13680 100644 --- a/crow.game.codec.netty/src/main/java/crow/game/codec/netty/package-info.java +++ b/crow.game.codec.netty/src/main/java/crow/game/codec/netty/package-info.java @@ -1,5 +1,2 @@ -/** - * Package containing all the serialization and deserialization logic for the - * protocol. - */ +/** Package containing all the serialization and deserialization logic for the protocol. */ package crow.game.codec.netty; diff --git a/crow.game.examples.pingpong/pom.xml b/crow.game.examples.pingpong/pom.xml index 334750c..60b06a4 100644 --- a/crow.game.examples.pingpong/pom.xml +++ b/crow.game.examples.pingpong/pom.xml @@ -24,5 +24,9 @@ <artifactId>slf4j-simple</artifactId> <version>1.7.36</version> </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty-handler</artifactId> + </dependency> </dependencies> </project> diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/MainClient.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/MainClient.java index 7287ebc..d383914 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/MainClient.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/MainClient.java @@ -13,43 +13,36 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * This wraps the client into a runnable that can be run as a main entrypoint - * OR passed into a Thread. + * This wraps the client into a runnable that can be run as a main entrypoint OR passed into a + * Thread. * * @see crow.game.examples.pingpong.utils.Main */ public class MainClient implements Runnable { - protected static final Logger logger = LoggerFactory.getLogger( - MainClient.class - ); + protected static final Logger logger = LoggerFactory.getLogger(MainClient.class); - public static void main(String[] args) { - new MainClient().run(); - } + public static void main(String[] args) { + new MainClient().run(); + } - public void run() { - EventLoopGroup workerGroup = new NioEventLoopGroup( - new PrefixThreadFactory("client-worker") - ); - Context context = new RootContext(); - try { - Bootstrap b = new Bootstrap(); // + public void run() { + EventLoopGroup workerGroup = new NioEventLoopGroup(new PrefixThreadFactory("client-worker")); + Context context = new RootContext(); + try { + Bootstrap b = new Bootstrap(); // - b - .group(workerGroup) // - .channel(NioSocketChannel.class) // - .handler(new PingPongClientInitializer(context)); + b.group(workerGroup) // + .channel(NioSocketChannel.class) // + .handler(new PingPongClientInitializer(context)); - ChannelFuture f = b - .connect("127.0.0.1", Constants.LISTEN_PORT) - .sync(); - f.channel().closeFuture().sync(); - } catch (InterruptedException e) { - //e.printStackTrace(); - } finally { - workerGroup.shutdownGracefully(); - } - logger.info("client finished"); + ChannelFuture f = b.connect("127.0.0.1", Constants.LISTEN_PORT).sync(); + f.channel().closeFuture().sync(); + } catch (InterruptedException e) { + // e.printStackTrace(); + } finally { + workerGroup.shutdownGracefully(); } + logger.info("client finished"); + } } diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/PingPongClientInitializer.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/PingPongClientInitializer.java index cac4f26..b798dd1 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/PingPongClientInitializer.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/PingPongClientInitializer.java @@ -13,56 +13,45 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Netty clients logic for the ping pong example. it registers - * the standard {@link FrameCodec} and {@link PacketCodec} then - * just sends a {@link Ping} message once the connection is established, finally - * drops the connection once a {@link Pong} is received. + * Netty clients logic for the ping pong example. it registers the standard {@link FrameCodec} and + * {@link PacketCodec} then just sends a {@link Ping} message once the connection is established, + * finally drops the connection once a {@link Pong} is received. */ -public class PingPongClientInitializer - extends ChannelInitializer<SocketChannel> { - - public static final Logger logger = LoggerFactory.getLogger( - PingPongClientInitializer.class - ); - - protected final Context context; - - public PingPongClientInitializer(Context ctx) { - this.context = ctx; - } - - @Override - protected void initChannel(SocketChannel ch) throws Exception { - logger.info("initializing channel"); - ch - .pipeline() // - .addLast(new FrameCodec(context)) // - .addLast(new PacketCodec(context)) // - .addLast( - new ChannelInboundHandlerAdapter() { - @Override - public void channelActive(ChannelHandlerContext ctx) - throws Exception { - ctx.writeAndFlush(new Ping()).sync(); - } - - @Override - public void channelRead( - ChannelHandlerContext ctx, - Object msg - ) throws Exception { - if (msg instanceof Pong) { - logger.info("GOT PONG"); - ctx.close(); - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) - throws Exception { - logger.info("channel done"); - } +public class PingPongClientInitializer extends ChannelInitializer<SocketChannel> { + + public static final Logger logger = LoggerFactory.getLogger(PingPongClientInitializer.class); + + protected final Context context; + + public PingPongClientInitializer(Context ctx) { + this.context = ctx; + } + + @Override + protected void initChannel(SocketChannel ch) throws Exception { + logger.info("initializing channel"); + ch.pipeline() // + .addLast(new FrameCodec(context)) // + .addLast(new PacketCodec(context)) // + .addLast( + new ChannelInboundHandlerAdapter() { + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ctx.writeAndFlush(new Ping()).sync(); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (msg instanceof Pong) { + logger.info("GOT PONG"); + ctx.close(); } - ); - } + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + logger.info("channel done"); + } + }); + } } diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/package-info.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/package-info.java index b9e9579..fc60024 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/package-info.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/client/package-info.java @@ -1,4 +1,2 @@ -/** - * The client code for the ping/pong example. - */ +/** The client code for the ping/pong example. */ package crow.game.examples.pingpong.client; diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Constants.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Constants.java index 74237bc..58a3b6e 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Constants.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Constants.java @@ -2,8 +2,8 @@ package crow.game.examples.pingpong.proto; public class Constants { - public static final int LISTEN_PORT = 9991; + public static final int LISTEN_PORT = 9991; - public static final short PING_PREFIX = (short) 0x9901; - public static final short PONG_PREFIX = (short) 0x9902; + public static final short PING_PREFIX = (short) 0x9901; + public static final short PONG_PREFIX = (short) 0x9902; } diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Ping.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Ping.java index 5bb84d2..0e71785 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Ping.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Ping.java @@ -1,6 +1,4 @@ package crow.game.examples.pingpong.proto; -/** - * The super simple Ping request POJO - */ +/** The super simple Ping request POJO */ public class Ping {} diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PingPacketHandler.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PingPacketHandler.java index 2c3e83e..c921eef 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PingPacketHandler.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PingPacketHandler.java @@ -6,23 +6,22 @@ import crow.game.codec.PacketRule; import io.netty.buffer.ByteBuf; /** - * This is the packet handler for the Ping message. It, ironically, is empty - * since Ping is a 0-byte message (ignoring packet id) so this handler is - * only responsible for constructing and defining the packet details. + * This is the packet handler for the Ping message. It, ironically, is empty since Ping is a 0-byte + * message (ignoring packet id) so this handler is only responsible for constructing and defining + * the packet details. */ public class PingPacketHandler implements PacketHandler<Ping> { - @Override - public Ping fromPacket(Context ctx, ByteBuf buffer) throws Exception { - return new Ping(); - } + @Override + public Ping fromPacket(Context ctx, ByteBuf buffer) throws Exception { + return new Ping(); + } - @Override - public void toPacket(Context ctx, ByteBuf buffer, Ping src) - throws Exception {} + @Override + public void toPacket(Context ctx, ByteBuf buffer, Ping src) throws Exception {} - @Override - public PacketRule rule(Context ctx) { - return PacketRule.Static(Constants.PING_PREFIX, 0, Ping.class); - } + @Override + public PacketRule rule(Context ctx) { + return PacketRule.Static(Constants.PING_PREFIX, 0, Ping.class); + } } diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Pong.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Pong.java index bc1f71f..276ffe1 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Pong.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/Pong.java @@ -1,6 +1,4 @@ package crow.game.examples.pingpong.proto; -/** - * The super simple Pong response POJO - */ +/** The super simple Pong response POJO */ public class Pong {} diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PongPacketHandler.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PongPacketHandler.java index 4854fa8..edbaa24 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PongPacketHandler.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PongPacketHandler.java @@ -6,22 +6,22 @@ import crow.game.codec.PacketRule; import io.netty.buffer.ByteBuf; /** - * This is the packet handler for the Pong message. It, ironically, is empty - * since Pong is a 0-byte message (ignoring packet id) so this handler is - * only responsible for constructing and defining the packet details. + * This is the packet handler for the Pong message. It, ironically, is empty since Pong is a 0-byte + * message (ignoring packet id) so this handler is only responsible for constructing and defining + * the packet details. */ public class PongPacketHandler implements PacketHandler<Pong> { - @Override - public Pong fromPacket(Context ctx, ByteBuf buffer) { - return new Pong(); - } + @Override + public Pong fromPacket(Context ctx, ByteBuf buffer) { + return new Pong(); + } - @Override - public void toPacket(Context ctx, ByteBuf buffer, Pong src) {} + @Override + public void toPacket(Context ctx, ByteBuf buffer, Pong src) {} - @Override - public PacketRule rule(Context ctx) { - return PacketRule.Static(Constants.PONG_PREFIX, 0, Ping.class); - } + @Override + public PacketRule rule(Context ctx) { + return PacketRule.Static(Constants.PONG_PREFIX, 0, Ping.class); + } } diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/RootContext.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/RootContext.java index 5f8f4bb..74bd4ab 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/RootContext.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/RootContext.java @@ -2,64 +2,60 @@ package crow.game.examples.pingpong.proto; import crow.game.codec.Context; import crow.game.codec.PacketHandler; -import java.util.Arrays; import java.util.List; /** - * The root context is the context implementation for the Ping/Pong - * server and defines the packet version and provides the {@link PacketHandler}s. - * <p> - * More complicated client/server implementations could use spring or guice or SPI - * to lookup packet POJOs and {@link PacketHandler}s but this one is too simple - * to justify that level of work. - * <p> - * Ideally we will have a common server or client module that implements a Context - * that does use guice or spring and then that module is just marked as "requiring - * injection library". + * The root context is the context implementation for the Ping/Pong server and defines the packet + * version and provides the {@link PacketHandler}s. * + * <p>More complicated client/server implementations could use spring or guice or SPI to lookup + * packet POJOs and {@link PacketHandler}s but this one is too simple to justify that level of work. + * + * <p>Ideally we will have a common server or client module that implements a Context that does use + * guice or spring and then that module is just marked as "requiring injection library". */ public class RootContext implements Context { - protected final PacketHandler<Ping> pingPacketHandler; - protected final PacketHandler<Pong> pongPacketHandler; - - public RootContext() { - this.pingPacketHandler = new PingPacketHandler(); - this.pongPacketHandler = new PongPacketHandler(); - } - - public Context.Source callingSource() { - return Context.Source.INIT; - } - - public int supportedProtocolVersion() { - return 20231115; + protected final PacketHandler<Ping> pingPacketHandler; + protected final PacketHandler<Pong> pongPacketHandler; + + public RootContext() { + this.pingPacketHandler = new PingPacketHandler(); + this.pongPacketHandler = new PongPacketHandler(); + } + + public Context.Source callingSource() { + return Context.Source.INIT; + } + + public int supportedProtocolVersion() { + return 20231115; + } + + @Override + public List<PacketHandler<?>> lookupPacketHandler(int prefix) { + if (prefix == Constants.PING_PREFIX) { + return List.of(pingPacketHandler); + } else if (prefix == Constants.PONG_PREFIX) { + return List.of(pongPacketHandler); + } else { + return List.of(); } - - @Override - public List<PacketHandler<?>> lookupPacketHandler(int prefix) { - if (prefix == Constants.PING_PREFIX) { - return List.of(pingPacketHandler); - } else if (prefix == Constants.PONG_PREFIX) { - return List.of(pongPacketHandler); - } else { - return List.of(); - } + } + + @Override + public List<PacketHandler<?>> lookupPacketHandler(Class<?> clz) { + if (clz.isAssignableFrom(Ping.class)) { + return List.of(pingPacketHandler); + } else if (clz.isAssignableFrom(Pong.class)) { + return List.of(pongPacketHandler); + } else { + return List.of(); } + } - @Override - public List<PacketHandler<?>> lookupPacketHandler(Class<?> clz) { - if (clz.isAssignableFrom(Ping.class)) { - return List.of(pingPacketHandler); - } else if (clz.isAssignableFrom(Pong.class)) { - return List.of(pongPacketHandler); - } else { - return List.of(); - } - } - - @Override - public Object underlyingObject() { - return this; - } + @Override + public Object underlyingObject() { + return this; + } } diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/package-info.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/package-info.java index 48ca944..7bbfcea 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/package-info.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/package-info.java @@ -1,16 +1,18 @@ /** - * This is the ping pong protocol package and would, in more complex scenarios, - * be in its own JAR. It contains the POJOs and PacketHandlers for both client - * and server needs and is meant to be included in both sides to support bidirectional - * communication. - * <p> - * If you look at the {@link crow.game.examples.pingpong.proto.Ping} and {@link crow.game.examples.pingpong.proto.Pong} - * objects and their corresponding PacketHandlers, you'll notice there is no real - * different between the two. The request/response model in this system is not defined(1) so - * objects can be both, providing their PacketHandlers implement both decode and encode. - * <p> - * (1) NOTE: This may change in the future! + * This is the ping pong protocol package and would, in more complex scenarios, be in its own JAR. + * It contains the POJOs and PacketHandlers for both client and server needs and is meant to be + * included in both sides to support bidirectional communication. + * + * <p>If you look at the {@link crow.game.examples.pingpong.proto.Ping} and {@link + * crow.game.examples.pingpong.proto.Pong} objects and their corresponding PacketHandlers, you'll + * notice there is no real different between the two. The request/response model in this system is + * not defined(1) so objects can be both, providing their PacketHandlers implement both decode and + * encode. + * + * <p>(1) NOTE: This may change in the future! + * * <p> + * * @see crow.game.examples.pingpong.proto.RootContext */ package crow.game.examples.pingpong.proto; diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/MainServer.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/MainServer.java index 4536d2b..c9f4c0b 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/MainServer.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/MainServer.java @@ -14,43 +14,37 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * This wraps the server into a runnable that can be run as a main entrypoint - * OR passed into a Thread. + * This wraps the server into a runnable that can be run as a main entrypoint OR passed into a + * Thread. * * @see crow.game.examples.pingpong.utils.Main */ public class MainServer implements Runnable { - public static final Logger logger = LoggerFactory.getLogger( - MainServer.class - ); + public static final Logger logger = LoggerFactory.getLogger(MainServer.class); - public static void main(String[] args) { - new MainServer().run(); - } + public static void main(String[] args) { + new MainServer().run(); + } - public void run() { - EventLoopGroup bossGroup = new NioEventLoopGroup( - new PrefixThreadFactory("server-boss") - ); - EventLoopGroup workerGroup = new NioEventLoopGroup( - new PrefixThreadFactory("server-worker") - ); - Context context = new RootContext(); - try { - ServerBootstrap b = new ServerBootstrap(); // - b - .group(bossGroup, workerGroup) // - .channel(NioServerSocketChannel.class) // - .childHandler(new PingPongServerInitializer(context)) // - .option(ChannelOption.SO_BACKLOG, 128) // - .childOption(ChannelOption.SO_KEEPALIVE, true); + public void run() { + EventLoopGroup bossGroup = new NioEventLoopGroup(new PrefixThreadFactory("server-boss")); + EventLoopGroup workerGroup = new NioEventLoopGroup(new PrefixThreadFactory("server-worker")); + Context context = new RootContext(); + try { + ServerBootstrap b = new ServerBootstrap(); // + b.group(bossGroup, workerGroup) // + .channel(NioServerSocketChannel.class) // + .childHandler(new PingPongServerInitializer(context)) // + .option(ChannelOption.SO_BACKLOG, 128) // + .childOption(ChannelOption.SO_KEEPALIVE, true); - ChannelFuture f = b.bind(Constants.LISTEN_PORT).sync(); // (7) - f.channel().closeFuture().sync(); - } catch (InterruptedException e) {} finally { - workerGroup.shutdownGracefully(); - bossGroup.shutdownGracefully(); - } + ChannelFuture f = b.bind(Constants.LISTEN_PORT).sync(); // (7) + f.channel().closeFuture().sync(); + } catch (InterruptedException e) { + } finally { + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); } + } } diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/PingPongServerInitializer.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/PingPongServerInitializer.java index df76cd9..29f813d 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/PingPongServerInitializer.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/PingPongServerInitializer.java @@ -15,64 +15,48 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Netty server logic for the ping pong example. it registers - * the standard {@link FrameCodec} and {@link PacketCodec} then - * just waits for a {@link Ping} object to come from a client. - * <p> - * The bottom of the pipeline is a {@link SimpleChannelInboundHandler} with - * two functions: listen for Ping messages and handle exceptions thrown by the - * codecs and report and drop the connection as needed. + * Netty server logic for the ping pong example. it registers the standard {@link FrameCodec} and + * {@link PacketCodec} then just waits for a {@link Ping} object to come from a client. + * + * <p>The bottom of the pipeline is a {@link SimpleChannelInboundHandler} with two functions: listen + * for Ping messages and handle exceptions thrown by the codecs and report and drop the connection + * as needed. */ -public class PingPongServerInitializer - extends ChannelInitializer<SocketChannel> { - - public static final Logger logger = LoggerFactory.getLogger( - PingPongServerInitializer.class - ); - - protected final Context context; - - public PingPongServerInitializer(Context ctx) { - this.context = ctx; - } - - @Override - protected void initChannel(SocketChannel ch) { - ch - .pipeline() // - .addLast(new FrameCodec(context)) // - .addLast(new PacketCodec(context)) // - .addLast( - new SimpleChannelInboundHandler<Ping>() { - @Override - protected void channelRead0( - ChannelHandlerContext channelHandlerContext, - Ping ping - ) { - logger.info("GOT PING, RESPONDING"); - channelHandlerContext.writeAndFlush(new Pong()); - } - - @Override - public void exceptionCaught( - ChannelHandlerContext ctx, - Throwable cause - ) { - if (cause instanceof DecoderException) { - if ( - cause.getCause() instanceof UnsupportedMessageTypeException - ) { - logger.warn( - "dropping connection due to malformed packet" - ); - ctx.close(); - return; - } - } - - logger.error("error", cause); - } +public class PingPongServerInitializer extends ChannelInitializer<SocketChannel> { + + public static final Logger logger = LoggerFactory.getLogger(PingPongServerInitializer.class); + + protected final Context context; + + public PingPongServerInitializer(Context ctx) { + this.context = ctx; + } + + @Override + protected void initChannel(SocketChannel ch) { + ch.pipeline() // + .addLast(new FrameCodec(context)) // + .addLast(new PacketCodec(context)) // + .addLast( + new SimpleChannelInboundHandler<Ping>() { + @Override + protected void channelRead0(ChannelHandlerContext channelHandlerContext, Ping ping) { + logger.info("GOT PING, RESPONDING"); + channelHandlerContext.writeAndFlush(new Pong()); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + if (cause instanceof DecoderException) { + if (cause.getCause() instanceof UnsupportedMessageTypeException) { + logger.warn("dropping connection due to malformed packet"); + ctx.close(); + return; + } } - ); - } + + logger.error("error", cause); + } + }); + } } diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/package-info.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/package-info.java index e4bc059..6212ce8 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/package-info.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/server/package-info.java @@ -1,4 +1,2 @@ -/** - * The server code for the ping/pong example. - */ +/** The server code for the ping/pong example. */ package crow.game.examples.pingpong.server; diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/Main.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/Main.java index f942842..90e0ae1 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/Main.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/Main.java @@ -5,22 +5,20 @@ import crow.game.examples.pingpong.server.MainServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** - * This main runs both client and server as separate threads. - */ +/** This main runs both client and server as separate threads. */ public class Main { - public static void main(String[] args) { - Logger l = LoggerFactory.getLogger(Main.class); - l.info("running..."); - Thread thr1 = new Thread(new MainServer(), "server"); - thr1.start(); - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - new MainClient().run(); - thr1.interrupt(); + public static void main(String[] args) { + Logger l = LoggerFactory.getLogger(Main.class); + l.info("running..."); + Thread thr1 = new Thread(new MainServer(), "server"); + thr1.start(); + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + throw new RuntimeException(e); } + new MainClient().run(); + thr1.interrupt(); + } } diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/PrefixThreadFactory.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/PrefixThreadFactory.java index a2d397e..a0ab15f 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/PrefixThreadFactory.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/PrefixThreadFactory.java @@ -4,27 +4,23 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** - * Thread factory which prefixes the thread with the given - * name and an incremental integer. Useful for debugging - * thread pools built by Netty. + * Thread factory which prefixes the thread with the given name and an incremental integer. Useful + * for debugging thread pools built by Netty. */ public class PrefixThreadFactory implements ThreadFactory { - AtomicInteger at; + AtomicInteger at; - String prefix; + String prefix; - public PrefixThreadFactory(String p) { - at = new AtomicInteger(0); - prefix = p; - } + public PrefixThreadFactory(String p) { + at = new AtomicInteger(0); + prefix = p; + } - @Override - public Thread newThread(Runnable arg0) { - Thread thr = new Thread( - arg0, - String.format("%s-%d", prefix, at.getAndIncrement()) - ); - return thr; - } + @Override + public Thread newThread(Runnable arg0) { + Thread thr = new Thread(arg0, String.format("%s-%d", prefix, at.getAndIncrement())); + return thr; + } } diff --git a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/package-info.java b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/package-info.java index bcfb2f8..8a6808b 100644 --- a/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/package-info.java +++ b/crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/utils/package-info.java @@ -1,4 +1,2 @@ -/** - * Shared utilities and launcher for the ping pong example. - */ +/** Shared utilities and launcher for the ping pong example. */ package crow.game.examples.pingpong.utils; diff --git a/pom.xml b/pom.xml index b8e63f8..b2410cc 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,37 @@ <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.build.timestamp.format>yyyyMMdd_HHmm</maven.build.timestamp.format> + <netty.version>4.1.101.Final</netty.version> </properties> + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>1.7.36</version> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty-buffer</artifactId> + <version>${netty.version}</version> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty-codec</artifactId> + <version>${netty.version}</version> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty-transport</artifactId> + <version>${netty.version}</version> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty-handler</artifactId> + <version>${netty.version}</version> + </dependency> + </dependencies> + </dependencyManagement> <build> <plugins> <plugin> @@ -61,29 +91,19 @@ <sortPlugins>groupId,artifactId</sortPlugins> </sortPom> </pom> + <java> + <includes> + <include>crow*/src/main/java/**/*.java</include> + <include>crow*/src/test/java/**/*.java</include> + </includes> + <googleJavaFormat></googleJavaFormat> + </java> <markdown> <includes> <include>**/*.md</include> </includes> <flexmark></flexmark> </markdown> - <formats> - <format> - <includes> - <include>src/*/java/**/*.java</include> - </includes> - <prettier> - <devDependencies> - <prettier>2.0.5</prettier> - <prettier-plugin-java>1.4.0</prettier-plugin-java> - </devDependencies> - <config> - <tabWidth>4</tabWidth> - <parser>java</parser> - </config> - </prettier> - </format> - </formats> </configuration> <executions> <execution>