![]() |
PokerUnicorn
|
Currently the game server has a TCP server that works on SSL and supports WebSocket too.
Important WebSocket mode works as a wrapper for the TCP protocol. The protocol flows in the same way in WebSocket mode too. In fact, WebSocket is a message-based protocol not a streaming protocol while TCP is a streaming protocol. A WS client that connects to the server must do buffered synchronous network reads on the protocol. I made a
SynchronousWebSocketStream
library for this purpose in the client side. The client uses that.
The game server works on SSL and supports WebSocket too.
When the game server receives HTTP header for WebSocket handshaking, it switches to WebSocket mode for that client.
SSL key and certificate files are passed with --ssl-key-file
and --ssl-cert-file
CLI arguments.
WebSocket module is pkrsrv_websocket_...
. The server understands and switches to WebSocket when client is a WebSocket client.
The protocol flows in the same way in WebSocket mode too.. WS client must do buffered synchronous network reads on the protocol.
Specifications of TCP protocol and server module usage:
pkrsrv_server_packet_frame_[...]_t
.pkrsrv_server_packet_[...]_t
.Note Frames are not memory-padded! They must be packed.
Field | Type | Size | Description |
---|---|---|---|
opcode | uint32_t | 4 | Specifies how to marshal & send and receive & parse packets |
length | uint32_t | 4 | Generic length for some generic packet implementations |
Client must send MEOW packet after connection is established to start using the protocol.
Sent when server is over capacity. Client must disconnect and try again later.
Attributes:
Attribute | Description |
---|---|
direction | CLIENT_TO_SERVER |
has_response | true |
Fields:
Field | Type | Size | Description |
---|---|---|---|
id_token_length | uint16_t | 2 | Length of id_token NULL-terminated string (includes NULL-char too) |
password_length | uint16_t | 2 | Length of password NULL-terminated string (includes NULL-char too) |
id_token | char* | (non-NULL-terminated) Being sent by the server (ptr) | |
password | char* | (non-NULL-terminated) Being sent by the server (ptr) | |
name | char* | (non-NULL-terminated) Being sent by the server (ptr) | |
avatar | unsigned char* | Binary representation of avatar image |
Next to send:
Field | Type | Size | Description |
---|---|---|---|
id_token | char* | (non-NULL-terminated) Being sent by the server (ptr) | |
password | char* | (non-NULL-terminated) Being sent by the server (ptr) | |
name | char* | (non-NULL-terminated) Being sent by the server (ptr) | |
avatar | unsigned char* | Binary representation of avatar image |
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
is_ok | uint8_t | 1 | Is there any error? |
is_logined | uint8_t | 1 | Was logged in after sign up? |
status | uint16_t (pkrsrv_server_packet_signup_res_status_t ) | 2 | Status code |
Status Codes:
Next to receive:
Next to receive if is_ok && is_logined
:
Field | Type | Size | Description |
---|---|---|---|
id_token_length | uint16_t | 2 | Length of id_token |
name_length | uint16_t | 2 | Length of user name |
avatar_length | uint16_t | 2 | Length of avatar binary |
xmr_deposit_address_length | uint16_t | 2 | Length of XMR deposit address binary |
id | uint64_t | 8 | Account ID |
balance \ilinebr </td> <td class="markdownTableBodyNone"> uint64_t` | 8 | Account balance |
Next to receive:
Vector | Type | Size |
---|---|---|
id_token | unsigned char[id_token_length] | id_token_length |
name | unsigned char[name_length] | name_length |
avatar | unsigned char[avatar_length] | avatar_length |
xmr_deposit_address | unsigned char[xmr_deposit_address_length] | xmr_deposit_address_length |
Attributes:
Attribute | Description |
---|---|
direction | CLIENT_TO_SERVER |
has_response | true |
Fields:
Field | Type | Size | Description |
---|---|---|---|
id_token_length | uint16_t | 2 | Length of id_token NULL-terminated string (includes NULL-char too) |
password_length | uint16_t | 2 | Length of password NULL-terminated string (includes NULL-char too) |
id_token | char* | (non-NULL-terminated) Being sent by the server (ptr) | |
password | char* | (non-NULL-terminated) Being sent by the server (ptr) |
Next to send:
Field | Type | Size | Description |
---|---|---|---|
id_token | char* | id_token_length | ID Token to login (non-NULL-terminated) |
password | char* | password_length | Password to login (non-NULL-terminated) |
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
is_ok | uint8_t | 1 | Is there any error? |
is_logined | uint8_t | 1 | Was logging in successful? |
Next to receive if is_ok && is_logined
:
Field | Type | Size | Description |
---|---|---|---|
xm_deposit_address_length | uint16_t | 2 | Length of XMR deposit address binary |
id_token_length | uint16_t | 2 | Length of id_token |
name_length | uint16_t | 2 | Length of user name |
avatar_length | uint16_t | 2 | Length of avatar binary |
balance | uint64_t | 8 | Account balance |
Next to receive:
Vector | Type | Size |
---|---|---|
xmr_deposit_address | unsigned char[xmr_deposit_address_length] | xmr_deposit_address_length |
id_token | unsigned char[id_token_length] | id_token_length |
name | unsigned char[name_length] | name_length |
avatar | unsigned char[avatar_length] | avatar_length |
Sent to client when account is updated.
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
xm_deposit_address_length | uint16_t | 2 | Length of XMR deposit address binary |
id_token_length | uint16_t | 2 | Length of id_token |
name_length | uint16_t | 2 | Length of user name |
avatar_length | uint16_t | 2 | Length of avatar binary |
id | uint64_t | 8 | Account ID |
balance | uint64_t | 8 | Account balance |
Attributes:
Attribute | Description |
---|---|
direction | CLIENT_TO_SERVER |
has_response | true |
Field | Type | Size | Description |
---|---|---|---|
name_length | uint16_t | 2 | Length of user name |
avatar_length | uint32_t | 4 | Length of avatar binary |
Next to send:
Vector | Type | Size |
---|---|---|
name | unsigned char[name_length] | name_length |
avatar | unsigned char[avatar_length] | avatar_length |
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
is_ok | uint8_t | 1 | Is there any error? |
is_avatar_too_big | uint8_t | 1 | Was avatar binary too big? |
Enters a table/session as a watcher.
Attributes:
Attribute | Description |
---|---|
direction | CLIENT_TO_SERVER |
has_response | true |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Table unique identifier |
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Determines which table the response was for |
is_ok | uint8_t | 1 | Is there any error? |
Attributes:
Attribute | Description |
---|---|
direction | CLIENT_TO_SERVER |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Determines which table the response was for |
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Determines which table the response was for |
is_ok | uint8_t | 1 | Is there any error? |
Joins into the game (takes a seat), must be sent while player is a watcher.
Attributes:
Attribute | Description |
---|---|
direction | CLIENT_TO_SERVER |
has_response | true |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Table unique identifier |
enterance_amount | uint64_t | 4 | Enterance amount to join with |
position | int32_t | 4 | Which seat player wanna sit |
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Determines which table the response was for |
is_ok | uint8_t | 1 | Is there any error? |
Attributes:
Attribute | Description |
---|---|
direction | CLIENT_TO_SERVER |
has_response | true |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Table unique identifier |
enterance_amount | uint64_t | 4 | Enterance amount to join with |
position | int32_t | 4 | Which seat player wanna sit |
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
is_ok | uint8_t | 1 | Is there any error? |
table_id | uint64_t | 8 | Determines which table the response was for |
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
name_length | uint16_t | 2 | Length of table name string (non-NULL-terminated) |
players_length | uint16_t | 2 | Length of players array |
table_id | uint64_t | 8 | Table id that specifies which session is the game info for |
max_player | uint16_t | 2 | Number of max players for the table |
action_timeout | uint16_t | 2 | Action timeout (0 for unlimited) |
small_blind | uint16_t | 2 | Small blind amount |
big_blind | uint16_t | 2 | Big blind amount |
enterance_min | uint64_t | 2 | Min amount of money player must have to join the table/session |
enterance_max | uint64_t | 2 | Max amount of money player must have to join the table/session |
Next to receive:
Field | Type | Size | Description |
---|---|---|---|
name | char* to pkrsrv_string_t (ref-counted) | name_length | Game/table title (non-NULL-terminated) |
pkrsrv_server_packet_frame_poker_info_player_t[] | char* to pkrsrv_string_t (ref-counted) | name_length | Array of player frames |
Field | Type | Size | Description |
---|---|---|---|
name_length | uint16_t | 2 | Public name length of the player |
avatar_length | uint32_t | 4 | Avatar binary length of the player |
id | uint64_t | 8 | Account ID of the player |
position | uint8_t | 1 | Player's position |
is_playing | uint8_t | 1 | Is player playing or waiting for next hand? |
is_dealt | uint8_t | 1 | Are player's hand cards dealt? |
is_allin | uint8_t | 1 | Had the player been done all-in? |
is_folded | uint8_t | 1 | Is player folded? |
is_left | uint8_t | 1 | Is player left? (Will be removed in next start) |
balance | uint32_t | 4 | Player's chips amaount |
Next to receive for each pkrsrv_server_packet_frame_poker_info_player_t
:
Vector | Type | Size |
---|---|---|
id_token | unsigned char[id_token_length] | id_token_length |
name | unsigned char[name_length] | name_length |
avatar | unsigned char[avatar_length] | avatar_length |
The game state packet
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Determines which table |
state | uint16_t | 2 | Game state identifier |
is_playing | uint8_t | 1 | Is receiver player playing? |
cards | unsigned char[5] | 5 | Cards array each byte has (4 bits card) + (4 bits kind) |
players_length | uint16_t | 2 | Length of players array |
position | uint8_t | 1 | Receiver player's position |
playing_position | uint8_t | 1 | Playing player's position |
is_dealt | uint8_t | 1 | Are hand cards dealt? |
hand_cards | unsigned char[2] | 2 | Receiver player's hand cards. Each byte has (4 bits value) + (4 bits kind) |
balance | uint64_t | 8 | Receiver player's current balance |
bet | uint64_t | 8 | Receiver player's current bet |
current_raise | uint64_t | 8 | Current raise-by amount |
current_bet | uint64_t | 8 | Current bet amount |
pot_amount | uint64_t | 8 | Total amount on pot |
Next to receive:
Next to receive:
Field | Type | Size | Description |
---|---|---|---|
name | char* to pkrsrv_string_t (ref-counted) | name_length | Game/table title (non-NULL-terminated) |
pkrsrv_server_packet_frame_poker_info_player_t[] | char* to pkrsrv_string_t (ref-counted) | name_length | Array of player frames |
Field | Type | Size | Description |
---|---|---|---|
name_length | uint16_t | 2 | Public name length of the player |
position | uint8_t | 1 | Player's position |
is_playing | uint8_t | 1 | Is player playing or waiting for next hand? |
is_dealt | uint8_t | 1 | Are player's hand cards dealt? |
is_allin | uint8_t | 1 | Had the player been done all-in? |
is_folded | uint8_t | 1 | Is player folded? |
is_left | uint8_t | 1 | Is player left? (Will be removed in next start) |
balance | uint64_t | 8 | Player's chips amaount |
Next to receive for each pkrsrv_server_packet_frame_poker_info_player_t
(Latest poker_info.players_length
times):
Vector | Type | Size |
---|---|---|
name | unsigned char[name_length] | name_length |
The packet sent when game is ended
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Determines which joined session |
winner_account_id | uint64_t | 8 | Winner account ID |
earned_amount | uint64_t | 8 | Pot amount that the winner earned |
scores_length | uint8_t | 1 | Determines the length of score objects of multiple winners |
Next to receive for each pkrsrv_server_packet_frame_poker_end_score
(scores_length
times):
Field | Type | Size | Description |
---|---|---|---|
account_id | uint64_t | 8 | Account ID of this score |
bet_diff | uint64_t | 8 | Diff between final bet and player's bet |
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Determines which joined session |
Poker action packet for doing actions from client to server.
Note This packet is for only doing actions from client to server. When a player does an action, an action reflection packet will be broadcasted to all clients:
pkrsrv_server_packet_frame_poker_action_reflection_t
/pkrsrv_server_packet_poker_action_reflection_t
.
Attributes:
Attribute | Description |
---|---|
direction | CLIENT_TO_SERVER |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Table ID to identify which session |
action_kind | uint16_t of (pkrsrv_poker_actions_action_kind_t ) | 2 | Determines hich poker action |
amount | uint64_t | 8 | Amount value for bets/raises |
Poker action event that gets broadcasted by server to all clients in the session when a poker action is done.
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Table ID to identify which session |
account_id | uint64_t | 8 | Account ID to identify which player |
action_kind | uint16_t of (pkrsrv_poker_actions_action_kind_t ) | 2 | Determines hich poker action |
amount | uint64_t | 8 | Amount value for bets/raises |
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
table_id | uint64_t | 8 | Determines which joined session |
table_reason \ilinebr </td> <td class="markdownTableBodyNone"> uint32_t` | 4 | Unjoining reason |
Clients request list of tables with this packet and server sends pkrsrv_server_packet_frame_tables_t
packet and following length * pkrsrv_server_packet_frame_table_t
packets to the client this after request.
Attributes:
Attribute | Description |
---|---|
direction | CLIENT_TO_SERVER |
Fields:
Field | Type | Size | Description |
---|---|---|---|
offset | uint16_t | 2 | Starting offset for table list |
table_length \ilinebr </td> <td class="markdownTableBodyNone"> uint16_t\ilinebr </td> <td class="markdownTableBodyNone"> 2 \ilinebr </td> <td class="markdownTableBodyNone"> Number of tables to retrieve from the offset` |
This packet is followed by length * pkrsrv_server_packet_frame_table_t
packets that must get retrieved.
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Fields:
Field | Type | Size | Description |
---|---|---|---|
offset | uint16_t | 2 | Starting offset for table list |
table_length \ilinebr </td> <td class="markdownTableBodyNone"> uint16_t\ilinebr </td> <td class="markdownTableBodyNone"> 2 \ilinebr </td> <td class="markdownTableBodyNone"> Number of tables to retrieve from the offset` |
Next to receive (length
-times of pkrsrv_server_packet_frame_table_t
...):
Field | Type | Size | Description |
---|---|---|---|
id | uint64_t | 8 | Table ID |
small_blind | uint32_t | 4 | Small-blind amount |
big_blind | uint32_t | 4 | Big-blind amount |
max_players | uint16_t | 2 | Number of max players of the table |
players_count | uint16_t | 2 | Number of playing players |
watchers_count | uint16_t | 2 | Number of watchers |
name_length | uint16_t | 2 | Table name length to retrieve name bytes |
Next to receive:
Field | Type | Size | Description |
---|---|---|---|
name | char* | name_length | Table name (non-NULL-terminated) |
This packet is for clients to request a list of currently playing game sessions.
Attributes:
Attribute | Description |
---|---|
direction | CLIENT_TO_SERVER |
When sessions are requested by client, server sends this packet.
Attributes:
Attribute | Description |
---|---|
direction | SERVER_TO_CLIENT |
Next to receive:
Bidirectional JSON packets for generic usage.
JSONs are sent with a header that has header.opcode = PKRSRV_SERVER_OPCODE_JSON
and header.length
.
The following JSON_LENGTH
bytes are supposed to be sent/received which is actually the serialized JSON string.
The server switches to WebSocket mode if the client sends a WebSocket handshake request for that client.
The server checks if the client sends a HTTP GET request to understand if the client wants to switch to WebSocket mode.
The protocol's packet header (pkrsrv_server_packet_frame_header_t
) occupies 8 bytes in total: 4 bytes for opcode and 4 bytes for length; to understand if the client wants to switch to WebSocket mode, the server interprets the first packet header as a vector and checks it if it starts with "GET /"
.
Something like this: