I want to implement an existing protocol (server side only) in Java. The client is an old game client that I would like to write a server for.
The protocol is structured like this:
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+----//----+--------+--------+
| size0 | size1 | seq0 | seq1 | sessID0| sessID1| param0 | param1 | id0 | id1 | payload | trash | trash |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+----//----+--------+--------+
Now the protocol, being pretty old, defines many versions, where the header (up until including id1
) stays the same (as do the last two padding bytes).
The payload
varies from version to version, so it needs to be decoded differently. See simplified example below (pl#
for payload byte number):
Version 1.00
...+--------+--------+--------+--------+--------+--------/--------+--------+...
(HEAD) | pl0 | pl1 | pl2 | pl3 | pl4 | pl5 ... pl22 | pl23 |
...+--------+--------+--------+--------+--------+--------/--------+--------+...
| clientVersion (little endian) | userName (fixed String l=20) |
...+--------+--------+--------+--------+--------+--------/--------+--------+
Version 1.10
...+--------+--------+--------+--------+--------+--------/--------+--------+...
(HEAD) | pl0 | pl1 | pl2 | pl3 | pl4 | pl5 ... plN-1 | plN |
...+--------+--------+--------+--------+--------+--------/--------+--------+...
| clientVersion (big endian) | userName (pascal String) |
...+--------+--------+--------+--------+--------+--------/--------+--------+
The question for me now is two-fold.
First and foremost, how do you implement such a protocol without getting into switch()
hell? I have read that a lot of people recommend a FSM (or maybe State-pattern) for that, but I didn't get how this should work. I have also been looking into (and playing with) Google's ProtoBuf, which sadly does not support any int16 / uint16 data types.
Secondly, how to implement this, so that I end up with a nice, clean PacketHandler
-interface that may look like this:
public interface PacketHandler {
void onLoginRequest(Client client, LoginRequest request);
void onHeartbeatRequest(Client client, HeartbeatRequest request);
...
}
The purpose of that Handler would be that the classes LoginRequest
and HeartbeatRequest
are the same, no matter the client version.
On the outgoing side of things I want to be able to write these kind of POJOs as well, for example:
public class PacketHandlerImpl implements PacketHandler {
void onLoginRequest(Client client, LoginRequest request) {
if(success)
client.send(new LoginGrantedResponse(...));
else
client.send(new LoginDeniedResponse(...));
}
...
}
Thanks in advance for your help!