The paradigm is the "intelligent terminal" paradigm: the clients know relatively little about the game itself, only how to display it. The table has 19 places tiles can be: - the deck, "k" All tiles start out here; it corresponds to no particular display position. All tiles are moved to the various other places as part of startup initialization. - the wall, "wNNN" NNN is the wall position's number, in decimal, 0-padded to three digits. Wall tiles are numbered 0 through 135, where 0 is the top tile at East's portion's right end and numbers increase in draw order: 1 is under 0, 2 is just to the left of 0, etc. - the replacement wall, "rNN" Numbered 00 through 11, where 00 is the first replacement tile and numbers increase in draw order - the dora wall, "aN" Numbered 0 through 9, where 0 is first dora, 1 is first reverse dora, 2 is second dora, etc. - 4 x player's hand, "hX" where X is hand's player ID No numbering; display details are up to clients. - 4 x player's discards, "dX" where X is hand's player ID No numbering; display details are up to clients. Only the most recent discard is ever moved. - 4 x player's flowers/seasons, "fX" where X is hand's player ID No numbering; display details are up to clients. - 4 x player's melds, "mXN" where X is hand's player ID and N is meld number from 0 to 3. No numbering within melds. protocol data types: "..." literal strings C: counted string, represented as N:DATA where N is length in decimal and DATA is contents, N bytes long; N itself must be no more than 8 characters long W: wind letter, e, s, w, or n. T: tile, represented as: suits: suit (B, C, or D, for bamboo, characters, or dots) followed by number (1-9) winds: W followed by direction (e, s, w, or n) dragons: D followed by colour (r, g, or w) haku: H flowers: F1, F2, F3, F4 seasons: S1, S2, S3, S4 face not shown to the receiving player: ? P: place, one of the above I: player ID - a digit 1-4, corresponding to PLAYER1 through PLAYER4 in the "game" command N: integer - from 1 to 16 decimal digits giving an integer, terminated with a dot. Whitespace is ignored except within the above. Alphabetic case is ignored except within counted strings. (Note that dragons versus dots is ambiguous on the first character, but the second disambiguates.) When a client operation "can be rejected", this means that the server may accept or reject it for reasons it may be difficult for clients to detect - typically, exactly what variant of the game is being played. Rejection of an operation means that the server responds to the client message with a "reject" message and perform no server-side state change; the client is expected to try again. The "reject" message: "reject" C:EXPL The operation was rejected; EXPL is a short free-form text string explaining briefly what was wrong with it. The client is not required to display EXPL to the user, but unless the client is intelligent enough about the game that rejects should never happen, it is likely a good idea to. The maximum length of an EXPL string is 512 octets, though most clients will have a substantially lower effective maximum. on connection Client sends protocol version string and gives player's name: "mmj1c" C:NAME NAME is player's name; player names may be no longer than 256 characters Server sends protocol version string: "mmj1s" game setup "game" I:YOURID C:PLAYER1 C:PLAYER2 C:PLAYER3 C:PLAYER4 PLAYER1, PLAYER2, PLAYER3, PLAYER4 are the players' names YOURID is the player ID for the receiving Player player names are limited to 64 bytes long. hand init "newhand" W:HANDWIND W:P1WIND W:P2WIND W:P3WIND W:P4WIND HANDWIND is the hand's prevailing wind PxWIND is PLAYERx's wind (x in 1..4) The client needs to reset its state (and display, if appropriate) to all tiles in the deck and nothing in any of the other 18 places tiles can be. wall break - maybe allow East-rolled 2d6? "break" N M N and M are single digits giving the two d6 rolls; if client cares about break location, it must compute it itself. when a tile moves: "move" T:tile P:src P:dst tile is tile doing the moving, as seen by this player src is the place it's moving from dst is the place it's moving to When creating a concealed kong meld, most players see two open and two concealed moves; the melding player sees four open moves followed by one concealed move from the meld to itself. Clients are expected to detect these patterns if they wish to display concealed kong melds specially. "riichi" next tile move will be from a hand to a discard, and the discarding player is declaring riichi. This occurs only if the game variant being played has riichi, of course. when a player must act: "play" receiving player has a hand of 14 and must act. "claim" a tile has just been moved to someone's discard pile; this player must claim it or pass on it. when a hand ends: "draw" The hand is drawn. "win" I:winner scoring The hand was won by winner. scoring is a breakdown of the scoring; it consists of one or more repetitions of C:what N:points where what is the score description (eg, "Big Three Winds") and points is the number of points it brings (eg, 12). This is terminated with a similar entry with a zero-length what, whose point value is the total value for the hand. draw and win are followed by hand-to-itself moves which reveal all the tiles in all players' hands. Then "ack" The client is expected to send back "ack" when it is ready to proceed to the next hand. Once all clients have acked, the server advances the hand wind, updates seat winds, and starts over with another newhand. at any time after server sends "game" "chat" - supports text comm between players client -> server "chat" DESTS "." C:TEXT TEXT is the text to send; DESTS is one or more player ID characters saying whom the text is to go to. server -> client "chat" SRC OTHERDESTS "." C:TEXT TEXT is the text sent; SRC is the player ID of the person who sent this; OTHERDESTS is zero or more player IDs indicating what other players this message went to. The maximum length of a chat text is 1024 octets. player action on "play" "discard" T:tile Discard tile. Can be rejected. "riichi" T:tile Discard tile and declare riichi. Can be rejected. "out" Go out. Can be rejected. "ckong" T:tile `Meld' a concealed kong of tile. Can be rejected. "pkong" T:tile Upgrade a melded pung of tile to a kong. Can be rejected. on "claim" "claim" T:tile1 T:tile2 T:tile3 Claim the tile, giving the tiles from the hand which go with it (using ? for tile3 for pung/chow). Can be rejected. "swap" T:tile Swap the discarded tile for the specified tile from the player's hand. Can be rejected. If the swap is accepted, a new "claim" is issued to all clients after the swap completes. "ron" Claim the tile as a winning tile. Can be rejected. "pass" Let the tile go. Can be rejected, but only in conditions such as trying to pass when it's not claim phase. Eg, a game may begin thus (S is server, 1-4 are players): Inform the players who's playing (connection 1 is Jean, 2 is Chris, 3 is Pat, and 4 is Mouse) S->1: game 1 4:Jean 5:Chris 3:Pat 5:Mouse S->2: game 2 4:Jean 5:Chris 3:Pat 5:Mouse S->3: game 3 4:Jean 5:Chris 3:Pat 5:Mouse S->4: game 4 4:Jean 5:Chris 3:Pat 5:Mouse Tell the players where they are for this hand (East hand, E Jean, S Chris, W Pat, N Mouse): S->all: newhand e e s w n East rolls 4 and 6, thus breaking 10 tiles into South's portion of the wall, ie, dealer's first tiles are wall tiles (17+10-1)*2=52 through 55, with the dealing continuing up through wall tile 106, which is East's draw tile. S->all: break 4 6 S->1: move c6 w052 h1 move d6 w053 h1 move b6 w054 h1 move d2 w055 h1 move ? w056 h2 move ? w057 h2 move ? w058 h2 move ? w059 h2 move ? w060 h3 move ? w061 h3 move ? w062 h3 move ? w063 h3 move ? w064 h4 move ? w065 h4 move ? w066 h4 move ? w067 h4 .... S->2: move ? w052 h1 move ? w053 h1 move ? w054 h1 move ? w055 h1 move dr w056 h2 move d8 w057 h2 move dw w058 h2 move 9d w059 h2 move ? w060 h3 move ? w061 h3 move ? w062 h3 move ? w063 h3 move ? w064 h4 move ? w065 h4 move ? w066 h4 move ? w067 h4 ... S->3: move ? w052 h1 move ? w053 h1 move ? w054 h1 move ? w055 h1 move ? w056 h2 move ? w057 h2 move ? w058 h2 move ? w059 h2 move d7 w060 h3 move d8 w061 h3 move b1 w062 h3 move b9 w063 h3 move ? w064 h4 move ? w065 h4 move ? w066 h4 move ? w067 h4 ... S->4: move ? w052 h1 move ? w053 h1 move ? w054 h1 move ? w055 h1 move ? w056 h2 move ? w057 h2 move ? w058 h2 move ? w059 h2 move ? w060 h3 move ? w061 h3 move ? w062 h3 move ? w063 h3 move b5 w064 h4 move d8 w065 h4 move d3 w066 h4 move b8 w067 h4 ... The first dora tile is turned up; it's a green dragon: S->all: move dg r10 r10 As it happens, in this case, East gets S2 and S3 and North gets F2, so immediately following the deal, replacements are drawn: the nine of dots and one of characters for East and the five of characters for North: S->all: move s2 h1 f1 S->1: move d9 r00 h1 S->2,3,4: move ? r00 h1 S->all: move s3 h1 f1 S->1: move c1 r01 h1 S->2,3,4: move ? r01 h1 S->all: move f2 h4 f4 S->4: move c5 r02 h4 S->1,2,3: move ? r02 h4 At this point it is East's play: S->1: play East decides to discard the six of dots: 1->S: discard d6 S->all: move d6 h1 d1 S->2,3,4: claim North pungs it; South and West pass: 2->S: pass 3->S: pass 4->S: claim d6 d6 ? S->all: move d6 d1 m40 move d6 h4 m40 move d6 h4 m40 It's now North's turn to discard: S->4: play North discards the green dragon: 4->S: discard dg S->all: move dg h4 d4 S->1,2,3: claim The other three pass (nobody can claim it): 1->S: pass 2->S: pass 3->S: pass This means East draws the next tile, which is a four of characters: S->1: move c4 w105 h1 S->2,3,4: move ? w105 h1 S->1: play