Updated $Date: 2005/05/06 21:10:11 $ by $Author: tchize $: Outline: This section contains a brief outline of the document. Background: List some of the background for client/server, and current state of affairs. General Socket Notes: How sockets are presently used. Protocol: Commands that are sent back and forth. Example Session: A brief example of what protocol commands would be sent back and forth. Programming notes: A few notes that can be useful for people writing clients are extending the server. Todo: Things to do in the future. Note: each section is seperated by a line of dashes. This should make finding a specific section easier. In order to make things a little for people doing porting, I have added SUMMARY comments in some of the sections. These contain a very brief summary of some aspect that is needed for writing the code. A more detailed explanation of the SUMMARY can be determined by reading the section. ------------------------------------------------------------------------------ Background: Originally, the communications plan was set to be a text based system. These messages are what is originally detailed below (now removed). It was up to the server and client to parse these messages and determine what to do. These messages were assumed to be 1 line per message. At a reasonably early stage of developement, Eric Anderson wrote a fairly (but not totally) complete client/server that used his eutl package. This package pretty much set up packets with subpackets - these subpackets would have a tag for the data type, then the data itself. Thus, you could any many types, and after transmission, the other end could decode these commands. This works fairly well, but I think the creation of numerous sub packets has some performance hit. Also, the eutl was not especially well documented, so writing a client for a different platform became more difficult (you needed to first port over eutl.) An example such of this is the Java client currently in production. Also, Eric left to work on other products shortly after writing his client, which didn't really leave anyone with a full understanding. I have decided to remove the eutl dependancy. At least one advantage is that having this network related code directly in the client and server makes error handling a bit easier/cleaner. However, instead of a straight text method, the outside packet method is: The is the size of the data packet, the 2 byte size for the size information is not included here. Eutl originally used 4 bytes for the size - to me, 2 bytes seems plenty (gives a maximum packet of 32767 - I can't see ever going beyond a few thousand, simply because in a fast action game, transmission size of such a packet would probably not make things playable.) While saving 2 bytes might not be much, it makes a least some sense. The actual data is something of the nature of the commands listed below. It is a text command, followed by possible other data. The remaining data can be binary - it is up to the client and server to decode what it sent. The commands as described below is just the data portion of the packet. If writing a new client, remember that you must take into account the size of the packet. there is no termination of packets, other than knowing how long it should be. For now, most everything that is sent is text. This is more or less how things worked under eutl, except it packed the ints into 4 bytes in a known order. In some cases, we handle ints as strings, in others, they are sent as binary information. How any command handles it is detailed below in the command description. The S and C represent the direction of the data (S->C represents something the server sends to the client, C->S represents something the client sends to the server.) In terms of all binary values, we use MSB order (same as eutl used). This includes the initial length information, as well as any ints or shorts that get sent inside the packets. All packets are defined to have at least one word of text, followed by a space, then followed by optional data (which can be binary.) Side note: Generally, the data the client sends to the server is text, but a fair amount of data the server sends to the client is binary. This has somewhat to do with who wrote what code, and also has to do that the S->C bandwidth is going to more the more serious limitation - the client generally won't be sending so much data that the its flow is much problem. Note that all the commands as detailed below are up to date descriptions I removed a lot of the old notes on this file, because they were out of date, and while might be good ideas, were not all that relevent to how things currently work. Summary: Packets sent back and forth have a 2 byte header (MSB order) which contains the length of the rest of the packet. ------------------------------------------------------------------------------ General socket notes: We are using a TCP/IP socket. Other methods could be used, but the present protocol does not make very good provisions for missing data, so it needs to be something that corrects errors/does resends automatically (or just doesn't get errors in the first place.) For now, we set non blocking output on the server side. This means we don't have to worry about internal buffering. If the connection is lost (which will also happen if the output buffer overflowing), it looks like we just terminate the connection without saving (meaning last save takes effect.) This can certainly be abused the same way that currently killing the server (ie, go to treasure chamber, get all the treasure, kill server, wait for map to reset, play again, with you starting in the treasure chamber.) I don't know if there is a really good way to handle it. The other method would be to save the player back in town. But this then gets the situation of 'oops, I'm trapped', lose connection, start back in town. This is probably preferable - all you really gained there is a word of recall spell/scroll. Also, it doesn't really hurt the honest players who lost their connection for various reasons (and in fact, could be a disadvantage if they can't connect again until after the map resets, and they lost all the loot they were working on..) The server only reads data from the socket if the player has an action. This isn't really good, since many of the commands below might not be actual commands for the player. The alternative is to look at the data, and if it is a player command and there isn't time, store it away to be processed later. But this increases complexity, in that the server must start buffering the commands. Fortunately, for now, there are few such client commands. If it becomes a case where the client is requesting images/sounds, dual channels could probably be used, since requesting that data is not related to the actual playing of the game (or a special daemon that serves those requests could also be done.) SUMMARY: TCP/IP sockets are used for exchange data. Server uses non blocking i/o when writing to the socket, and the server only reads from the socket when the player actually has time for an action. ------------------------------------------------------------------------------ Protocol: Object tags: Many of the commands below refer to 'object tags'. Whenever the server creates an object, it creates a unique tag for that object (starting at 1 when the server is first run, and ever increasing.) Tags are unique, but are not consistent between runs. Thus, the client can not store tags when it exits and hope to re-use them when it joins the server at a later time - tags are only valid for the current connection. I have decided to break the protocol into various sections which based somewhat on what the commands are for (ie, item related commands, map commands, image commands, etc.) ****************************************************************************** COMMANDS RELATING TO ESTABLISHING THE INITIAL CONNECTION AND CLOSING THE CONNECTION C->S: version [scval [vinfo]] S->C: version [scval [vinfo]] Through the version command, the client and server exchange what version of the protocol they understand. Neither send this in response to the other - they should both send this shortly after a connection is established. csval is the version level of C->S communications. scval is the version level of S->C communications. vinfo is a string that is purely for informative that general client/server info (ie, javaclient, x11client, winclient, sinix server, etc). It is purely of interest of server admins who can see what type of clients people are using. If a new command is added to the protocol in the C->S direction, then the version number in csval will get increased. Likewise, the same is true for the scval. As far as the client is concerned, its scval must be at least equal to the server, and its csval should not be newer than the server. The server does not care about the version command it receives right now - all it currently does is log mismatches. In theory, the server should keep track of what the client has, and adjust the commands it sends respectively in the S->C direction. The server is resilant enough that it won't crash with a version mistmach (however, client may end up sending commands that the server just ignores). It is really up to the client to enforce versioning and quit if the versions don't match. scval and vinfo was added starting in 1020. Before that version, there was only one version sent in the version command. The version are currently integers, in the form ABCD. A = 1, and will likely for quite a while. This will only really change if needed from rollover of B. B represents major protocol changes - if B mismatches, the clients will be totally unusable. Such an example would be change of map or item sending commands (either new commands or new format.) C represents more minor but still significant changes - clients might still work together, but some features that used to work may now fail due to the mismatch. An example may be a change in the meaning of some field in some command - providing the field is the same size, it still should be decoded properly, but the meaning won't be processed properly. D represents very minor changes or new commands. Things should work no worse if D does not match, however if they do match, some new features might be included. An example of the would be the C->S mark command to mark items. Server not understanding this just means that the server can not process it, and will ignore it. Note: Since all 'packets' have the length as the first 2 bytes, all that either the client or server needs to be able to do is look at the first string and see if it understands it. If not, it knows how many bytes it can skip. As such, exact version matches should not be necessary for proper operation - however, both the client and server needs to be coded to handle such cases. Note 2: For the most part, this has been obsoleted by the setup command which always return status and whether it understood the command or not. However there are still some cases where using this versioning is useful - an example it the addition of the requestinfo/replyinfo commands - the client wants to wait for acknowledge of all the replyinfo commands it has issued before sending the addme command. However, if the server doesn't understand these options, the client will never get a response. With the versioning, the client can look at the version and know if it should wait for a response or if the server will never send back. C->S: addme Tells the server that it should add me (the client) to the game. Generally, the client will always send this command, but I suppose there can be actions the client wants to do before being added. S->C: addme_failed S->C: addme_success This are responses to the last addme command. I really think these should be discontinued, as they are one of the few messages which is just a confirmation of a previous messsage. The addme_failed should really be replaced with a terminate type of of message (player quits game, server could inform us nicely and exit out). addme_success is really of no use - client just throws it away. S->C: goodbye (Added in SC protocol version 1022) Informs the client that the server has finished transmitting data to the client. This is a bit cleaner than the client detecting a read error. In theory, a C->S of the same type could be done, but I don't think it would make a big difference for the server (is going to do the same thing regardless of a clean conection drop or a bad one). Also see the setfacemode command below. ****************************************************************************** COMMANDS RELATING TO PLAYER INPUT AND OUTPUT IN THE INFO WINDOW C->S: command [count] command is the protocol command. It is the segment which actually includes the command (ie, north, fire, etc.) There should not be any binary data in the segment. count is an optional value for the number of objects/repeat count (ie, typically used for dropping, but count can be used in other cases) Client sends a command to the server. Ordinary commands (ie, north, west, apply, maps, etc), might be sent, commands with options may be sent (ie, 'invoke create food of booze', 'cast medium fireball'). There are a few special commands that can also be sent. 'fire' command is a special case. The server will handle repeat firing. 'fire_stop' will be sent to inform the server to stop firing. A different command name has been chosen to make things easier on the server ('fire_stop' should be a 0 time command, with the rest of the fire commands actually taking some time.) In some cases, 'fire_stop' may be sent almost immediately after the first fire (in cases where the player only wants to fire once). C->S: ncom (ncom = new command) This is a replacement for the 'command' above. packet is a 16 bit value which represents what command this is (used is command below). At current time, only the lowest 8 bits should be used (255 should be a plenty large window) - the other 8 bits are reserved for future flags repeat is a 32 bit value and is the repeat value. command is the actual command data (north, whatever). Notes under command above also apply here. S->C: comc