| author | b0in
<b0in@proton.me> 2023-12-07 01:26:28 UTC |
| committer | b0in
<b0in@proton.me> 2023-12-07 01:27:36 UTC |
| parent | a7c072e4906549d562056f5e553c8fb0f2b186cb |
| crow.game.codec.api/src/main/java/crow/game/codec/InetAddressAndPort.java | +8 | -4 |
| crow.game.codec.api/src/main/java/crow/game/codec/PacketHandler.java | +2 | -4 |
| crow.game.codec.api/src/main/java/crow/game/codec/PacketReader.java | +58 | -0 |
| crow.game.codec.api/src/main/java/crow/game/codec/PacketRule.java | +8 | -0 |
| crow.game.codec.api/src/main/java/crow/game/codec/package-info.java | +7 | -11 |
| crow.game.codec.netty/src/main/java/crow/game/codec/netty/ByteBufPacketReader.java | +66 | -0 |
| crow.game.codec.netty/src/main/java/crow/game/codec/netty/FrameCodec.java | +11 | -4 |
| crow.game.codec.netty/src/main/java/crow/game/codec/netty/PacketCodec.java | +11 | -1 |
| crow.game.codec.netty/src/main/java/crow/game/codec/netty/package-info.java | +4 | -1 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PingPacketHandler.java | +2 | -1 |
| crow.game.examples.pingpong/src/main/java/crow/game/examples/pingpong/proto/PongPacketHandler.java | +2 | -1 |
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 6392dfe..f5c445d 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 @@ -1,6 +1,10 @@ package crow.game.codec; -import java.net.InetAddress; - -/** Utility object for pairing the ip address and port together. */ -public record InetAddressAndPort(InetAddress addr, int port) {} +/** + * Utility object for pairing the ip address and port together. + * + * <p>NOTE: We don't particularly care about the address itself we just need to store it so we can + * send it to clients, so we don't bother storing a parsed address, just the byte array that is read + * from the packet. + */ +public record InetAddressAndPort(byte[] addr, int port) {} 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 b23e2f0..9a33166 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 @@ -1,6 +1,5 @@ package crow.game.codec; -import java.net.InetAddress; import java.net.UnknownHostException; import io.netty.buffer.ByteBuf; @@ -20,7 +19,7 @@ public interface PacketHandler<T> { * @return The POJO object * @throws Exception */ - T fromPacket(Context ctx, ByteBuf buffer) throws Exception; + T fromPacket(Context ctx, PacketReader reader) throws Exception; /** * Writes the object of type T to the byte buffer. @@ -51,9 +50,8 @@ public interface PacketHandler<T> { byte[] addrBuf = new byte[4]; buf.readBytes(addrBuf); - InetAddress address = InetAddress.getByAddress(addrBuf); int port = buf.readShort(); - return new InetAddressAndPort(address, port); + return new InetAddressAndPort(addrBuf, port); } /** diff --git a/crow.game.codec.api/src/main/java/crow/game/codec/PacketReader.java b/crow.game.codec.api/src/main/java/crow/game/codec/PacketReader.java new file mode 100644 index 0000000..49a401d --- /dev/null +++ b/crow.game.codec.api/src/main/java/crow/game/codec/PacketReader.java @@ -0,0 +1,58 @@ +package crow.game.codec; + +import java.net.UnknownHostException; + +/** + * Interface for decoding packets into data. + * + * <p>used to abstract out the implementation details of netty. + */ +public interface PacketReader { + /** + * Reads the next N bytes as a string. + * + * <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 length the size of the string, with zero padding. + * @return The string object. + */ + String readString(int length); + + /** + * Reads the address and port pair from the kro server byte stream, which is a 6-byte series. + * + * @return The IP address and port pair + * @see InetAddressAndPort + * @throws UnknownHostException + */ + InetAddressAndPort readInetAddressAndPort() throws UnknownHostException; + + /** + * Skip N bytes + * + * @param length number of bytes to skip + */ + void skip(int length); + + /** + * @return the next 4-byte short in the stream + */ + short readShortLE(); + + /** + * @return the next 4-byte integer in the stream + */ + int readIntLE(); + + /** + * @return the next byte in the stream + */ + byte readByte(); + + /** + * @return The underlying object that backs this packet reader, usually a byte buffer. + * @param <T> The underlying object type. + */ + <T> T getUnderlyingObject(); +} 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 3a3caaa..3af7532 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 @@ -79,6 +79,10 @@ public record PacketRule( return new PacketRule(prefix, sizeTag, Type.DYNAMIC, clz); } + public static PacketRule Dynamic(int prefix, int sizeTag, Class<?> clz) { + return new PacketRule((short) prefix, sizeTag, Type.DYNAMIC, clz); + } + /** * Utility constructor for building a non-packet version specific static packet whose name is * derived from the given class. @@ -91,4 +95,8 @@ public record PacketRule( public static PacketRule Static(short prefix, int size, Class<?> clz) { return new PacketRule(prefix, size, Type.STATIC, clz); } + + public static PacketRule Static(int prefix, int size, Class<?> clz) { + return new PacketRule((short) 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 b4b4345..bce2498 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,11 +1,7 @@ /** - * 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. + * Package containing the base API objects for the protocol implementation. It requires netty + * bytebuf, but it's slowly being phased out for PacketReader/PacketWriter interfaces which only + * support the subset of needed methods. * * <p>There are multiple hooks for handling differing packet versions in this API: * @@ -18,12 +14,12 @@ * <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 + * "MyPacketHandle" that, when you receive 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>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/src/main/java/crow/game/codec/netty/ByteBufPacketReader.java b/crow.game.codec.netty/src/main/java/crow/game/codec/netty/ByteBufPacketReader.java new file mode 100644 index 0000000..5d2bbd7 --- /dev/null +++ b/crow.game.codec.netty/src/main/java/crow/game/codec/netty/ByteBufPacketReader.java @@ -0,0 +1,66 @@ +package crow.game.codec.netty; + +import java.net.UnknownHostException; + +import io.netty.buffer.ByteBuf; + +import crow.game.codec.InetAddressAndPort; +import crow.game.codec.PacketReader; + +/** Implementation of PacketReader that wraps a Netty {@link ByteBuf}. */ +public class ByteBufPacketReader implements PacketReader { + + private final ByteBuf buf; + + public ByteBufPacketReader(ByteBuf buf) { + this.buf = buf; + } + + @Override + public InetAddressAndPort readInetAddressAndPort() throws UnknownHostException { + byte[] a = new byte[4]; + buf.readBytes(a, 0, 4); + + int port = buf.readShort(); + return new InetAddressAndPort(a, port); + } + + @Override + public void skip(int length) { + buf.skipBytes(length); + } + + @Override + public String readString(int length) { + byte x = 0; + StringBuffer out = new StringBuffer(); + + do { + x = buf.readByte(); + if (x != 0) { + out.append((char) x); + } + } while (--length > 0); + return out.toString(); + } + + @Override + public int readIntLE() { + return buf.readIntLE(); + } + + @Override + public short readShortLE() { + return buf.readShortLE(); + } + + @Override + public byte readByte() { + return buf.readByte(); + } + + @Override + public <T> T getUnderlyingObject() { + return (T) buf; + } +} 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 670920a..5a18fd5 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 @@ -19,7 +19,9 @@ import crow.game.codec.PacketRule; /** * 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). + * it is (called the packet ID). Packets which are sized dynamically will have their lengths read + * and written here as well, so individual {@link PacketHandler} implementations do not need to + * worry about length. * * @see crow.game.codec.PacketRule * @see crow.game.codec.Packet @@ -27,9 +29,8 @@ import crow.game.codec.PacketRule; */ public class FrameCodec extends ByteToMessageCodec<Packet> { - static Logger logger = LoggerFactory.getLogger(FrameCodec.class); + private static final Logger logger = LoggerFactory.getLogger(FrameCodec.class); - // protected final Context context; public FrameCodec(Context context) { @@ -47,7 +48,7 @@ public class FrameCodec extends ByteToMessageCodec<Packet> { 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(ctx::matchesPacketVersion) .filter((PacketRule pr) -> packetRuleHasMoreData(in, pr)) .findFirst(); } @@ -103,4 +104,10 @@ public class FrameCodec extends ByteToMessageCodec<Packet> { out.add(new Packet(in.readBytes(size), rule.get())); } } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + logger.error("unhandled error: {}", cause.getMessage()); + ctx.close(); + } } 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 e8585ce..9fb138a 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 @@ -7,6 +7,9 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageCodec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import crow.game.codec.Context; import crow.game.codec.Packet; import crow.game.codec.PacketHandler; @@ -16,6 +19,7 @@ import crow.game.codec.PacketHandler; * business-logic specific POJOs. */ public class PacketCodec extends MessageToMessageCodec<Packet, Object> { + private static final Logger logger = LoggerFactory.getLogger(PacketCodec.class); protected final Context context; @@ -35,7 +39,7 @@ public class PacketCodec extends MessageToMessageCodec<Packet, Object> { .orElseThrow()); msg.buffer().readShortLE(); // drop the packet ID - out.add(h.fromPacket(wrCtx, msg.buffer())); + out.add(h.fromPacket(wrCtx, new ByteBufPacketReader(msg.buffer()))); } @Override @@ -57,4 +61,10 @@ public class PacketCodec extends MessageToMessageCodec<Packet, Object> { h.toPacket(wrCtx, directBuffer, msg); out.add(new Packet(directBuffer, h.rule(wrCtx))); } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + logger.error("unhandled error: {}", cause.getMessage()); + ctx.close(); + } } 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 3d13680..66c8989 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,2 +1,5 @@ -/** Package containing all the serialization and deserialization logic for the protocol. */ +/** + * Package containing all the netty-specific serialization and deserialization logic for the + * protocol. + */ package crow.game.codec.netty; 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 9141c1a..d583967 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 @@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf; import crow.game.codec.Context; import crow.game.codec.PacketHandler; +import crow.game.codec.PacketReader; import crow.game.codec.PacketRule; /** @@ -14,7 +15,7 @@ import crow.game.codec.PacketRule; public class PingPacketHandler implements PacketHandler<Ping> { @Override - public Ping fromPacket(Context ctx, ByteBuf buffer) throws Exception { + public Ping fromPacket(Context ctx, PacketReader buffer) throws Exception { return new Ping(); } 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 1d67072..214d4de 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 @@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf; import crow.game.codec.Context; import crow.game.codec.PacketHandler; +import crow.game.codec.PacketReader; import crow.game.codec.PacketRule; /** @@ -14,7 +15,7 @@ import crow.game.codec.PacketRule; public class PongPacketHandler implements PacketHandler<Pong> { @Override - public Pong fromPacket(Context ctx, ByteBuf buffer) { + public Pong fromPacket(Context ctx, PacketReader buffer) { return new Pong(); }