I have a Netty-based game server implementation that handles 40 or so distinct packets with their own serialization structure, for brevity I'll refer to them as FooPacket
, BarPacket
, ... These packet types have been assigned consecutive numerical "packet types", which are unsigned bytes. For example, FooPacket
is type 0x01, BarPacket
is type 0x02, etc...
All of these packets extend an abstract class, MyGameNetworkPacket
.
My current approach with Netty is to use an initial decoding layer to convert a TCP stream into a series of unparsed packet objects, which are defined as consisting of a packet type, length, and buffer containing the actual packet contents. (no byte stuffing is performed). I then feed this packet down the pipeline to one of a few "deserializing processors", each of which handles a set of packet types that are semantically related to each other (for example one such processor might handle connection state messages, while another may handle packets related to a certain aspect of the game itself). As per the design of Netty's MessageToMessageDecoder usage, my decoder is implemented as:
protected void decode (ChannelHandlerContext ctx, UnparsedPacket msg, List<Object> out)
throws Exception {
switch (msg.command) {
case FooPacket.COMMAND_ID:
FooPacket fooPacket = new FooPacket(msg.frame);
out.add(fooPacket);
break;
case BarPacket.COMMAND_ID:
BarPacket barPacket = new BarPacket(msg.frame);
out.add(barPacket);
break;
}
}
Once I get a structure decided upon I can easily create this with a code generator.
In addition, I override public boolean acceptInboundMessage(Object)
to specify if an unparsed packet should be handled by this decoder.
This is attaining a bit of what appears to be a code-smell for me, so I was looking into alternative solutions, such as:
- Having an array whose (integer keys) map to values of a lambda that takes a RawPacket and returns the parsed packet. This seems like a somewhat weird way to use an array and requires that I manually maintain the definition of that array so that its index-element mapping exactly matches the actual packet type to packet structure mapping already in use.
- Having a map structure that does the same thing as the array. Similar pitfalls, but somewhat more maintainable.
Are these any more novel ways to approach this that I haven't thought of yet?