Thursday, July 01, 2010

Protocol

The whole point of this project, as I may have mentioned before, is to develop the servers, the backend, for the roleplaying framework, not the client. Of course, developing one or more clients is to be expected as I work on the project, because much like you'd expect from a blind painter, it helps to have a visual verification that the result is at all accurate; I could work on the server-side code all I want and HOPE it works, but I'd really want to SEE that it works eventually.

Knowing that, I knew that some day I'd be working on a client -- textbased, 2d, 3d, whatever -- but I never thought it'd be so soon. In fact, I'm actually writing a text-based console client before I get any of the "useful" part of MMORF written: even the server-side code that I'm writing to test the client is just login server stuff right now. Where's the good stuff??


Of course, writing the client means that I've had to address the issue of the protocol between the client and the server, which I've briefly discussed before, but have spent a bit of time thinking about already. Two things are important in a protocol, when all things are said and done: it's efficient, and it's debuggable. The second part can always be done regardless of how the protocol is implemented, but its readability corresponds to how easily it is debugged. Sure, you can write loggers and decoders to translate some binary stream to meaningful words for the developer, but when first developing the system, you're better off making it human-readable to start, and worry about the efficiency later, provided you coded the networking subsystems to handle a complete change to the protocol. This means that all communication is handled through functions that generate the protocol packets via a drop-in system, one which can be replaced without anyone being the wiser. In fact, having the client being able to support every drop-in system you've devised based on its ability to detect what's coming down the pipe means no rewriting later in the development cycle.


When I talk about a human-readable protocol, I typically mean an interchange format such as XML, JSON, YAML or even Metaplace's MetaMarkup. It's something that balances readability by humans and parseability by computers. Myself, I tend to lean towards XML, because it handles hierarchical data very well (both structurally and visually), it copes with escaping characters used by the protocol WITHIN the protocol (something which was a recurring theme with MetaMarkup), and because I've had some experience with it as a network protocol.

The only unfun part of developing a protocol is ... developing a protocol. When you get to the binary level and are trying to squeeze as much data through as possible, THEN it might actually be fun, but at the XML level, it can be tedious coming up with the schema for all of the different packets that are going to fly by.

That's why I was quite pleased to find out that the serialization of C# objects results in: XML! Instead of having to come up with an XML protocol schema, all I have to do is come up with an object schema based on all of the communication needed between client-and-server and server-and-server, then pass these objects through the network and let them pop out as objects that need to be handled. Even better, handlers for these objects can actually be part of the object itself.

It's a pretty slick system. It took a bit of designing to come up with a general server framework, allowing the client to handle various login servers (local (object-based) and remote) and client servers (local and remote), but this is done and mostly implemented. I now have a console client that launches its own local login and client servers (internal objects, not even using a loopback network connection) and can send login packets (or rather, serialized login objects). I had to design none of the protocol, instead letting the .NET Serialization system do it for me, and I had to neither write out nor read in the stream of data, deconstituting or reconstituting the meaning. Instead, I create a Login object, call my LoginServerConnection's Send() function with it, and on the other side the LoginServer sees a Login object pop out, which it may deal with as it pleases.


Just think: I'm almost at the point where I can actually design some of the game server and see it working. Amazing.

No comments: