This XML document describes the KGS protocol. It is also used to automatically generate the perl parser for all the messages and structures in the protocol. Adapting it to other languages should be almost trivial.
Please note that wms has told me that he will change the protocol in response to my efforts. No problems with changing the protocol for good, but he does this just to make it more difficult to reverse-engineer it, since his changes are neither required, nor useful (they just make the protocol less robust, without adding added value). He doesn't even care that this locks out some users who cannot upgrade (cgoban2 isn't available for their platform anymore). It hurts to see such a waste of time and efforts that could have been used to fix bugs or improve the client...
If you feel you need to update the visual appearance of this document, feel free to look doc/doc2html.xsl and improve it.
The current version of this document can always be found at here, while the HTML version of it can be found here. (update: sourceforge is weeks behind on their anonymous cvs servers so the above might be out-of-date. sorry.)
"ORIGIN: CLIENT" means messages send from the client to the server, while "ORIGIN: SERVER" means messages send by the server to the client.
Everything on the wire is in little-endian format (what a shame).
Primitive types are mostly integers (signed
"I
<bits>", unsigned "U
<bits>"),
ascii strings ("username
"), or zero-terminated
UCS2-Strings ("STRING
"). Yes, I know java is supposed to
do UTF-16, but no implementation seems to care...
For the rest, go figure or bug me, Marc Lehmann <pcg@goof.com>
After connecting to the server, a handshake byte is sent. It's the major version number of the protocol the client expects to receive. Version 3 and 4 are mostly the same, except that Version 4 clients expect server messages to be compressed, version 3 clients not.
The server sends back his protocol number, which is always 3 in the current protocol. Most of the protocol variation is determined by the server using the client version that is used in the initial login message, not the initial handshake byte.
After the initial handshake, the client sends uncompressed messages, while the server sends back a zlib-compressed stream (rfc1950 and rfc1951).
All messages have the same header:
The length is the length of the full message including the header.
Beginning with version 2.5.x, a number is xored into the low
byte of the length in sent packages only, as given by the
following recurrence: rand[0] = 0; rand[i+1] = msg[i].length
+ (rand[i] * 0x04c2af9b + 0xfffffffb); xorbyte = rand >>
24
, all in 32 bit unsigned iso-c arithmetic.
If the type is >= 0x4000 this is a message for a specific channel. The channel number is always the next U16.
Beginning with version 2.5.x, a number is added on received messages only. The algorithm is as follows:
msglen < 44: type = typefield msglen > 44: type = (typefield + rand[i]) % 0x10000 rand[0] = 0 rand[i+1] = username[type % length username] + rand[i] * (type - 0x6cdd) where username is the user name of the logged-in user. coooool.
Apart from the basic types, I need to define some extra types to
deal with fixed-point values (based on integer types) or fixed-length
strings (either 7-bit-ascii or more limited (A
), or UCS-2
based (S
)).
The basic user or login name, used throughout the protocol as a handle to the user.
Many strings in the protocol are fixed-width for no good reason (maybe this is one reason for using compression in newer versions, as the packets itself are wasting lots of space.
Used in user_record.
A kind of locale specifier. It seems the general format seems to be lowercase language, underscore, uppercase location, e.g. en_US. More fancy specifications don't fit.
Just a simple boolean value. 0 means false, and 1 generally true, but I suggest accepting != 0 as true.
Komi values are multiplied by 2 to make them integer in the protocol. Well, *most* of the time at least...
The game result is also multiplied by two to give it higher resolution. There are also special values for wins by time etc., either in result or in the score* types, or both :)
A score value (used for displaying the score at the end of a game) are multiplied by four for a change (the 0.25 resolution is not used). In game structures it is encoded by dividing by two, though, so watch out! And in some others, it's encoded by multiplying by 1000... yuck!
Time values are multiplied by 1000, giving them millisecond accuracy.
64 bit timeval, milliseconds since posix epoch, e.g. my
($year, $month, $day) = (gmtime $date * 0.001)[5,4,3];
Password is a number calculated as follows (VERY insecure, basically
plaintext!): password = 0; for char in characters do password ←
password * 1055 + ascii_code (char)
Admins only(?)
Seems to be set on the english room. Or maybe not.
This room is private.
Special score values, in addition to numerical scores. Some are also used with their negative value.
Convinience constants used in several places.
Almost everywhere a user + flags is required, even used in some places where only a username is required. I see no general rule on when a complete user and when a partial user is required.
This structure is used for challanges as well as in the special TREE "subprotocol". It tightly encodes the game parameters.
Sent to login, usually the first message sent. The password needs to be set when the guest flag is true. Possible replies: . Followed by:
Request info about a certain user. Possible reply:
Update user info. Message structure is very similar to .
This message is sent to initiate or continue a private chat with a user. You'll always receive a copy of what you have sent back from the server (as usual).
Request user graph data, replied with .
Request a user picture from the server. Results in a or a timeout.
Send a global message. Maybe. Never tried, for obvious reasons :/. Results in a sent to all users.
Probably setting a notifier on a username, to get informed about changes using messages. sending your own username gives you a disconnect, so don't do that at home, kids!
Probably remove the notifier again.
List the rooms in a specific group/category. Results in a message.
Request a rooms update message for the given room.
Requests part of the users game record to be sent. Results in a or maybe a timeout.
Joins the given room. messages for yourself and all users in that room, as well as the initial gamelist, are send if the room exists. If not, timeout...
Unclear. Start a new game.
Clone: 00000: 20202020 20202020 20202020 20203238 28 00010: 9d000543 0d000000 00000000 00130201 ...C............ 00020: 0000ffff ffffffff ffffffff 00000000 ..ÿÿÿÿÿÿÿÿÿÿ....
The room where to start the new game
0x200 == global open game list
When cloning a game, the rules are set up like this: count => 65535, ruleset => 0, time => 4294967295, timesys => 0, interval => 4294967295. Cloning itself seems to be implemented solely in the client (somewhat sane, for a change).
Is send when a game is closed and should be saved on the gamerecord.
Sent by the client just before it logs out.
A single game record entry, as seen in .
0:3 == handicap, 4:7 == gametype:0:4
Owner (or empty)
The bits 16-24 of user1.flags and user2.flags give the high and low bits of a revision number in case there are multiple similar games.
0:11 == komi / 2, 12:14 == high 3 bits of gametype, 15: == sign bit
0:5 == boardsize; 6: == no idea; 7: == inplay?
Sent when no userinfo for the requested user could be found(?)
Game result record?
Adds or updates a global challenge (open game list).
The room this game is associated with.
Not all room messages are for rooms only, and rooms need to parse not only these messages. Orthogonality, what for?
See . In addition, flags the tree as being uploaded completely.
Notifies the client that a new game has been created. This message is sent long *after* upd_games and upd_observers etc. have been received. *sigh*
The newly created game.
The ID sent to the server in new_game.
Remove a game from the global challenge list (open game list).
The game id to remove.
Probably the same. I suggest to use this id for no good reason.