PokerUnicorn Network Protocol

Written by Meowing Cat on 5/28/2024, 7:00:00 AM

Specification of the PokerUnicorn Network Protocol

Hello, this is the specification of the PokerUnicorn Network Protocol. An example for implementing complicated network protocols.

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.

TCP Server Protocol

The game server works on SSL and supports WebSocket too.

Supported Protocols

  • Pure TCP over TLS
  • WebSocket over TLS

When the game server receives HTTP header for WebSocket handshaking, it switches to WebSocket mode for that client.

TLS Support

SSL key and certificate files are passed with --ssl-key-file and --ssl-cert-file CLI arguments.

WebSocket Support

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.

Frames and Network Commands

Specifications of TCP protocol and server module usage:

  • Each scalar packet start with the header frame.
  • Protocol object layouts are not memory-padded and named as pkrsrv_server_packet_frame_[...]_t.
  • Server module passes network commands in pkrsrv_server_packet_[...]_t.

Note Frames are not memory-padded! They must be packed.

Packet Structure

| ----- | ---------------------------------- | ------------------------- |
|  Byte |              Data                  |         Description       |
| ----- | ---------------------------------- | ------------------------- |
|       |        (64-bit Header Frame)       |                           |
|    0. |              OPCODE                |                           |
|    1. |                                    |                           |
|    2. |                                    |                           |
|    3. |                                    |                           |
|    4. |              LENGTH                | Generic length for        |
|    5. |                                    | generic use cases         |
|    6. |                                    |                           |
|    7. |                                    |                           |
| ----- | ---------------------------------- | ------------------------- |
|    0. |           (Packet Frame)           | Packet frame that its     |
|    N. |                ...                 | length determined by      |
|       |                                    | the OPCODE                |
|       |                                    |                           |
|       |                                    | Packet frames contain     |
|       |                                    | Vector field lengths as   |
|       |                                    | scalar types              |
| ----- | ---------------------------------- | ------------------------- |
|    0. |             [Vector 0]             | Vector lengths are spec-  |
|    N. |                ...                 | ified by frame fields     |
| ----- | ---------------------------------- | ------------------------- |
|    0. |             [Vector N]             |                           |
|    N. |                ...                 |                           |
| ----- | ---------------------------------- | ------------------------- |

Header

FieldTypeSizeDescription
opcodeuint32_t4Specifies how to marshal & send and receive & parse packets
lengthuint32_t4Generic length for some generic packet implementations
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_header {
    uint32_t opcode;
    uint32_t length;
};
Opcodes
enum PKRSRV_SERVER_OPCODE {
    PKRSRV_SERVER_OPCODE_NOP = 0,
    PKRSRV_SERVER_OPCODE_MEOW,
    PKRSRV_SERVER_OPCODE_PING,
    PKRSRV_SERVER_OPCODE_PONG,
    PKRSRV_SERVER_OPCODE_LOGIN,
    PKRSRV_SERVER_OPCODE_LOGIN_RES,
    PKRSRV_SERVER_OPCODE_SIGNUP,
    PKRSRV_SERVER_OPCODE_SIGNUP_RES,
    PKRSRV_SERVER_OPCODE_GET_ACCOUNT,
    PKRSRV_SERVER_OPCODE_ACCOUNT,
    PKRSRV_SERVER_OPCODE_ENTER,
    PKRSRV_SERVER_OPCODE_ENTER_RES,
    PKRSRV_SERVER_OPCODE_LEAVE,
    PKRSRV_SERVER_OPCODE_LEAVE_RES,
    PKRSRV_SERVER_OPCODE_JOIN,
    PKRSRV_SERVER_OPCODE_JOIN_RES,
    PKRSRV_SERVER_OPCODE_UNJOIN,
    PKRSRV_SERVER_OPCODE_UNJOIN_RES,
    PKRSRV_SERVER_OPCODE_POKER_INFO,
    PKRSRV_SERVER_OPCODE_POKER_STATE,
    PKRSRV_SERVER_OPCODE_POKER_ACTION,
    PKRSRV_SERVER_OPCODE_POKER_ACTION_REFLECTION,
    PKRSRV_SERVER_OPCODE_POKER_END,
    PKRSRV_SERVER_OPCODE_POKER_RESTARTED,
    PKRSRV_SERVER_OPCODE_JSON,
    PKRSRV_SERVER_OPCODE_UNJOINED,
    PKRSRV_SERVER_OPCODE_GET_TABLES,
    PKRSRV_SERVER_OPCODE_TABLES,
    PKRSRV_SERVER_OPCODE_GET_SESSIONS,
    PKRSRV_SERVER_OPCODE_SESSIONS,
    PKRSRV_SERVER_OPCODE_UPDATE_ACCOUNT,
    PKRSRV_SERVER_OPCODE_UPDATE_ACCOUNT_RES,
    PKRSRV_SERVER_OPCODE_SERVER_INFO,
    PKRSRV_SERVER_OPCODE_OVER_CAPACITY,
    PKRSRV_SERVER_OPCODE_END // Opcodes terminator
};

Meow

Client must send MEOW packet after connection is established to start using the protocol.

Over Capacity

Sent when server is over capacity. Client must disconnect and try again later.

Signup

Attributes:

AttributeDescription
directionCLIENT_TO_SERVER
has_responsetrue

Fields:

FieldTypeSizeDescription
id_token_lengthuint16_t2Length of id_token NULL-terminated string (includes NULL-char too)
password_lengthuint16_t2Length of password NULL-terminated string (includes NULL-char too)
id_tokenchar*^(non-NULL-terminated) Being sent by the server (ptr)
passwordchar*^(non-NULL-terminated) Being sent by the server (ptr)
namechar*^(non-NULL-terminated) Being sent by the server (ptr)
avatarunsigned char*^Binary representation of avatar image
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_signup {
    uint16_t id_token_length;
    uint16_t password_length;
    uint16_t name_length;
    uint32_t avatar_length;
};

Next to send:

FieldTypeSizeDescription
id_tokenchar*^(non-NULL-terminated) Being sent by the server (ptr)
passwordchar*^(non-NULL-terminated) Being sent by the server (ptr)
namechar*^(non-NULL-terminated) Being sent by the server (ptr)
avatarunsigned char*^Binary representation of avatar image

Signup Response

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
is_okuint8_t1Is there any error?
is_logineduint8_t1Was logged in after sign up?
statusuint16_t (pkrsrv_server_packet_signup_res_status_t)2Status code

Status Codes:

typedef enum PKRSRV_SERVER_PACKET_SIGNUP_RES_STATUS {
    PKRSRV_SERVER_PACKET_SIGNUP_RES_STATUS_OK = 0,
    PKRSRV_SERVER_PACKET_SIGNUP_RES_STATUS_ERROR,
    PKRSRV_SERVER_PACKET_SIGNUP_RES_STATUS_ALREADY_EXISTS
} pkrsrv_server_packet_signup_res_status_t;
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_signup_res {
    unsigned char is_ok;
    unsigned char is_logined;
    uint16_t status;
};

Next to receive:

Next to receive if is_ok && is_logined:

struct __attribute__((__packed__))
pkrsrv_server_packet_frame_signup_res_account {
    uint16_t id_token_length;
    uint16_t name_length;
    uint32_t avatar_length;
    uint16_t xmr_deposit_address_length;
    uint64_t id;
    uint64_t balance;
};
FieldTypeSizeDescription
id_token_lengthuint16_t2Length of id_token
name_lengthuint16_t2Length of user name
avatar_lengthuint16_t2Length of avatar binary
xmr_deposit_address_lengthuint16_t2Length of XMR deposit address binary
iduint64_t8Account ID
`balanceuint64_t8Account balance

Next to receive:

VectorTypeSize
id_tokenunsigned char[id_token_length]id_token_length
nameunsigned char[name_length]name_length
avatarunsigned char[avatar_length]avatar_length
xmr_deposit_addressunsigned char[xmr_deposit_address_length]xmr_deposit_address_length

Login

Attributes:

AttributeDescription
directionCLIENT_TO_SERVER
has_responsetrue

Fields:

FieldTypeSizeDescription
id_token_lengthuint16_t2Length of id_token NULL-terminated string (includes NULL-char too)
password_lengthuint16_t2Length of password NULL-terminated string (includes NULL-char too)
id_tokenchar*^(non-NULL-terminated) Being sent by the server (ptr)
passwordchar*^(non-NULL-terminated) Being sent by the server (ptr)

Next to send:

FieldTypeSizeDescription
id_tokenchar*id_token_lengthID Token to login (non-NULL-terminated)
passwordchar*password_lengthPassword to login (non-NULL-terminated)
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_login {
    uint16_t id_token_length;
    uint16_t password_length;
    char* id_token;
    char* password;
};
Login Response

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
is_okuint8_t1Is there any error?
is_logineduint8_t1Was logging in successful?
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_login_res {
    unsigned char is_ok;
    unsigned char is_logined;
};

Next to receive if is_ok && is_logined:

struct __attribute__((__packed__))
pkrsrv_server_packet_frame_login_res_account {
    uint16_t xmr_deposit_address_length;
    uint16_t id_token_length;
    uint16_t name_length;
    uint32_t avatar_length;
    uint64_t id;
    uint64_t balance;
};
FieldTypeSizeDescription
xm_deposit_address_lengthuint16_t2Length of XMR deposit address binary
id_token_lengthuint16_t2Length of id_token
name_lengthuint16_t2Length of user name
avatar_lengthuint16_t2Length of avatar binary
balanceuint64_t8Account balance

Next to receive:

VectorTypeSize
xmr_deposit_addressunsigned char[xmr_deposit_address_length]xmr_deposit_address_length
id_tokenunsigned char[id_token_length]id_token_length
nameunsigned char[name_length]name_length
avatarunsigned char[avatar_length]avatar_length

Account

Sent to client when account is updated.

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
xm_deposit_address_lengthuint16_t2Length of XMR deposit address binary
id_token_lengthuint16_t2Length of id_token
name_lengthuint16_t2Length of user name
avatar_lengthuint16_t2Length of avatar binary
iduint64_t8Account ID
balanceuint64_t8Account balance
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_account {
    uint16_t xmr_deposit_address_length;
    uint16_t id_token_length;
    uint16_t name_length;
    uint32_t avatar_length;
    uint64_t id;
    uint64_t balance;
};

Update Account

Attributes:

AttributeDescription
directionCLIENT_TO_SERVER
has_responsetrue
FieldTypeSizeDescription
name_lengthuint16_t2Length of user name
avatar_lengthuint32_t4Length of avatar binary
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_update_account {
    uint16_t name_length;
    uint32_t avatar_length;
};

Next to send:

VectorTypeSize
nameunsigned char[name_length]name_length
avatarunsigned char[avatar_length]avatar_length
Update Account Response

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
is_okuint8_t1Is there any error?
is_avatar_too_biguint8_t1Was avatar binary too big?
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_login_res {
    unsigned char is_ok;
    unsigned char is_avatar_too_big;
};

Enter

Enters a table/session as a watcher.

Attributes:

AttributeDescription
directionCLIENT_TO_SERVER
has_responsetrue

Fields:

FieldTypeSizeDescription
table_iduint64_t8Table unique identifier
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_enter {
    uint64_t table_id;
};

Enter Res

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
table_iduint64_t8Determines which table the response was for
is_okuint8_t1Is there any error?
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_enter_res {
    pkrsrv_server_client_t* client;
    uint64_t table_id;
    unsigned char is_ok;
};

Leave

Attributes:

AttributeDescription
directionCLIENT_TO_SERVER

Fields:

FieldTypeSizeDescription
table_iduint64_t8Determines which table the response was for
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_enter_res {
    uint64_t table_id;
};

Leave Res

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
table_iduint64_t8Determines which table the response was for
is_okuint8_t1Is there any error?
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_leave_res {
    uint64_t table_id;
    unsigned char is_ok;
};

Join

Joins into the game (takes a seat), must be sent while player is a watcher.

Attributes:

AttributeDescription
directionCLIENT_TO_SERVER
has_responsetrue

Fields:

FieldTypeSizeDescription
table_iduint64_t8Table unique identifier
enterance_amountuint64_t4Enterance amount to join with
positionint32_t4Which seat player wanna sit
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_join {
    uint64_t table_id;
    uint32_t enterance_amount;
    int32_t position;
};
Join Response

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
table_iduint64_t8Determines which table the response was for
is_okuint8_t1Is there any error?
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_join_res {
    uint64_t table_id;
    unsigned char is_ok;
};

Unjoin

Attributes:

AttributeDescription
directionCLIENT_TO_SERVER
has_responsetrue

Fields:

FieldTypeSizeDescription
table_iduint64_t8Table unique identifier
enterance_amountuint64_t4Enterance amount to join with
positionint32_t4Which seat player wanna sit
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_unjoin {
    uint64_t table_id;
};
Unjoin Response

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
is_okuint8_t1Is there any error?
table_iduint64_t8Determines which table the response was for
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_unjoin_res {
    unsigned char is_ok;
    uint64_t table_id;
};

Poker Info

typedef struct pkrsrv_server_packet_frame_poker_info pkrsrv_server_packet_frame_poker_info_t;
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_poker_info {
    uint16_t name_length;
    uint16_t players_length;
    uint64_t table_id;
    uint16_t max_players;
    uint16_t action_timeout;
    uint64_t small_blind;
    uint64_t big_blind;
    uint64_t enterance_min;
    uint64_t enterance_max;
};

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
name_lengthuint16_t2Length of table name string (non-NULL-terminated)
players_lengthuint16_t2Length of players array
table_iduint64_t8Table id that specifies which session is the game info for
max_playeruint16_t2Number of max players for the table
action_timeoutuint16_t2Action timeout (0 for unlimited)
small_blinduint16_t2Small blind amount
big_blinduint16_t2Big blind amount
enterance_minuint64_t2Min amount of money player must have to join the table/session
enterance_maxuint64_t2Max amount of money player must have to join the table/session

Next to receive:

FieldTypeSizeDescription
namechar* to pkrsrv_string_t (ref-counted)name_lengthGame/table title (non-NULL-terminated)
pkrsrv_server_packet_frame_poker_info_player_t[]char* to pkrsrv_string_t (ref-counted)name_lengthArray of player frames
typedef struct pkrsrv_server_packet_frame_poker_info_player pkrsrv_server_packet_frame_poker_info_player_t;
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_poker_info_player {
    uint16_t name_length;
    uint32_t avatar_length;
    uint64_t id;
    uint8_t position;
    uint8_t is_playing;
    uint8_t is_dealt;
    uint8_t is_allin;
    uint8_t is_folded;
    uint8_t is_left;
    uint64_t balance;
};
FieldTypeSizeDescription
name_lengthuint16_t2Public name length of the player
avatar_lengthuint32_t4Avatar binary length of the player
iduint64_t8Account ID of the player
positionuint8_t1Player’s position
is_playinguint8_t1Is player playing or waiting for next hand?
is_dealtuint8_t1Are player’s hand cards dealt?
is_allinuint8_t1Had the player been done all-in?
is_foldeduint8_t1Is player folded?
is_leftuint8_t1Is player left? (Will be removed in next start)
balanceuint32_t4Player’s chips amaount

Next to receive for each pkrsrv_server_packet_frame_poker_info_player_t:

VectorTypeSize
id_tokenunsigned char[id_token_length]id_token_length
nameunsigned char[name_length]name_length
avatarunsigned char[avatar_length]avatar_length

Poker State

The game state packet

struct __attribute__((__packed__))
pkrsrv_server_packet_frame_poker_state {
    uint64_t table_id;
    uint16_t state;
    unsigned char is_playing;
    unsigned char cards[5];
    uint16_t players_length;
    unsigned char position;
    unsigned char playing_position;
    unsigned char is_dealt;
    unsigned char hand_cards[2];
    uint64_t balance;
    uint64_t bet;
    uint64_t current_raise;
    uint64_t current_bet;
    uint64_t pot_amount;
    uint64_t starting_after;
};

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
table_iduint64_t8Determines which table
stateuint16_t2Game state identifier
is_playinguint8_t1Is receiver player playing?
cardsunsigned char[5]5Cards array each byte has (4 bits card) + (4 bits kind)
players_lengthuint16_t2Length of players array
positionuint8_t1Receiver player’s position
playing_positionuint8_t1Playing player’s position
is_dealtuint8_t1Are hand cards dealt?
hand_cardsunsigned char[2]2Receiver player’s hand cards. Each byte has (4 bits value) + (4 bits kind)
balanceuint64_t8Receiver player’s current balance
betuint64_t8Receiver player’s current bet
current_raiseuint64_t8Current raise-by amount
current_betuint64_t8Current bet amount
pot_amountuint64_t8Total amount on pot

Next to receive:

Next to receive:

FieldTypeSizeDescription
namechar* to pkrsrv_string_t (ref-counted)name_lengthGame/table title (non-NULL-terminated)
pkrsrv_server_packet_frame_poker_info_player_t[]char* to pkrsrv_string_t (ref-counted)name_lengthArray of player frames
typedef struct pkrsrv_server_packet_frame_poker_state_player pkrsrv_server_packet_frame_poker_state_player_t;
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_poker_state_player {
    uint64_t id;
    uint16_t name_length;
    unsigned char position;
    unsigned char is_playing;
    unsigned char is_dealt;
    unsigned char is_allin;
    unsigned char is_folded;
    unsigned char is_left;
    uint64_t balance;
};
FieldTypeSizeDescription
name_lengthuint16_t2Public name length of the player
positionuint8_t1Player’s position
is_playinguint8_t1Is player playing or waiting for next hand?
is_dealtuint8_t1Are player’s hand cards dealt?
is_allinuint8_t1Had the player been done all-in?
is_foldeduint8_t1Is player folded?
is_leftuint8_t1Is player left? (Will be removed in next start)
balanceuint64_t8Player’s chips amaount

Next to receive for each pkrsrv_server_packet_frame_poker_info_player_t (Latest poker_info.players_length times):

VectorTypeSize
nameunsigned char[name_length]name_length

Poker End

The packet sent when game is ended

struct __attribute__((__packed__))
pkrsrv_server_packet_frame_poker_end {
    uint64_t table_id;
    uint64_t winner_account_id;
    uint64_t earned_amount;
};

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
table_iduint64_t8Determines which joined session
winner_account_iduint64_t8Winner account ID
earned_amountuint64_t8Pot amount that the winner earned
scores_lengthuint8_t1Determines the length of score objects of multiple winners

Next to receive for each pkrsrv_server_packet_frame_poker_end_score (scores_length times):

struct __attribute__((__packed__))
pkrsrv_server_packet_frame_poker_end_score {
    uint64_t account_id;
    uint64_t bet_diff;
};
FieldTypeSizeDescription
account_iduint64_t8Account ID of this score
bet_diffuint64_t8Diff between final bet and player’s bet

Poker Restarted

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
table_iduint64_t8Determines which joined session
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_poker_restarted {
    uint64_t table_id;
};

Poker Action

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.

typedef struct pkrsrv_server_packet_frame_poker_action pkrsrv_server_packet_frame_poker_action_t;
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_poker_action {
    uint64_t table_id;
    uint16_t action_kind;
    uint64_t amount;
};

Attributes:

AttributeDescription
directionCLIENT_TO_SERVER

Fields:

FieldTypeSizeDescription
table_iduint64_t8Table ID to identify which session
action_kinduint16_t of (pkrsrv_poker_actions_action_kind_t)2Determines hich poker action
amountuint64_t8Amount value for bets/raises

Poker Action Reflection

Poker action event that gets broadcasted by server to all clients in the session when a poker action is done.

struct __attribute__((__packed__))
pkrsrv_server_packet_frame_poker_action_reflection {
    uint64_t table_id;
    uint64_t account_id;
    uint16_t action_kind;
    uint64_t amount;
};

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
table_iduint64_t8Table ID to identify which session
account_iduint64_t8Account ID to identify which player
action_kinduint16_t of (pkrsrv_poker_actions_action_kind_t)2Determines hich poker action
amountuint64_t8Amount value for bets/raises

Unjoined

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
table_iduint64_t8Determines which joined session
table_reasonuint32_t4Unjoining reason
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_unjoined {
    uint64_t table_id;
    uint32_t reason;
};

Get Tables

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:

AttributeDescription
directionCLIENT_TO_SERVER

Fields:

FieldTypeSizeDescription
offsetuint16_t2Starting offset for table list
`table_lengthuint16_t2Number of tables to retrieve from the offset
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_get_tables {
    uint16_t offset;
    uint16_t length;
};

Tables

This packet is followed by length * pkrsrv_server_packet_frame_table_t packets that must get retrieved.

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT

Fields:

FieldTypeSizeDescription
offsetuint16_t2Starting offset for table list
`table_lengthuint16_t2Number of tables to retrieve from the offset
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_tables {
    uint16_t offset;
    uint16_t length;
};

Next to receive (length-times of pkrsrv_server_packet_frame_table_t…):

FieldTypeSizeDescription
iduint64_t8Table ID
small_blinduint32_t4Small-blind amount
big_blinduint32_t4Big-blind amount
max_playersuint16_t2Number of max players of the table
players_countuint16_t2Number of playing players
watchers_countuint16_t2Number of watchers
name_lengthuint16_t2Table name length to retrieve name bytes
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_table {
    uint64_t id;
    uint32_t small_blind;
    uint32_t big_blind;
    uint16_t max_players;
    uint16_t players_count;
    uint16_t watchers_count;
    uint16_t name_length;
};

Next to receive:

FieldTypeSizeDescription
namechar*name_lengthTable name (non-NULL-terminated)

Get Sessions

This packet is for clients to request a list of currently playing game sessions.

Attributes:

AttributeDescription
directionCLIENT_TO_SERVER
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_get_sessions {
    uint16_t offset;
    uint16_t length;
};

Sessions

When sessions are requested by client, server sends this packet.

Attributes:

AttributeDescription
directionSERVER_TO_CLIENT
struct __attribute__((__packed__))
pkrsrv_server_packet_frame_sessions {
    uint16_t offset;
    uint16_t length;
};

Next to receive:

pkrsrv_server_packet_frame_poker_info_t[sessions.length]

JSON

Bidirectional JSON packets for generic usage.

JSONs are sent with a header that has header.opcode = PKRSRV_SERVER_OPCODE_JSON and header.length.

(pkrsrv_server_packet_frame_header) {
    .opcode = PKRSRV_SERVER_OPCODE_JSON,
    .length = json_length
}

The following JSON_LENGTH bytes are supposed to be sent/received which is actually the serialized JSON string.

WebSocket Mode

The server switches to WebSocket mode if the client sends a WebSocket handshake request for that client.

WebSocket Handshake

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:

ssize_t result;
pkrsrv_server_packet_frame_header_t header;

RECEIVE_FIRST_FRAME:

READ_OR_CLOSE(client->ssl, &header, sizeof(pkrsrv_server_packet_frame_header_t));

char* header_str = (char *) (&header);

client->is_websocket = (header_str[0] == 'G') &&
                        (header_str[1] == 'E') &&
                        (header_str[2] == 'T') &&
                        (header_str[3] == ' ') &&
                        (header_str[4] == '/');

WebSocket Packet Structure

| ----- | ---------------------------------- | ---------------------------------- |
|  Byte |              Data                  |             Description            |
| ----- | ---------------------------------- | ---------------------------------- |
|                                Web Socket Header                                |
| ----- | ---------------------------------- | ---------------------------------- |
|   0   |              FIN                   | 1 bit                              |
|   0   |              RSV1                  | 1 bit                              |
|   0   |              RSV2                  | 1 bit                              |
|   0   |              RSV3                  | 1 bit                              |
|   0   |              OPCODE                | 4 bits                             |
|   1   |              MASK                  | 1 bit                              |
|   1   |              PAYLOAD LENGTH        | 7 bits, or 7+16 bits, or 7+64 bits |
|   2   |              PAYLOAD LENGTH        | (if payload length is 126)         |
|   3   |              PAYLOAD LENGTH        | (if payload length is 126)         |
|   2   |              PAYLOAD LENGTH        | (if payload length is 127)         |
|   3   |              PAYLOAD LENGTH        | (if payload length is 127)         |
|   4   |              MASKING KEY           | 4 bytes                            |
| ----- | ---------------------------------- | ---------------------------------- |
|                    Packet Header (Beginning of the actual packet)               |
| ----- | ---------------------------------- | ---------------------------------- |
|   5   |              OPCODE                | 4 bytes                            |
|   9   |              LENGTH                | 4 bytes                            |
| ----- | ---------------------------------- | ---------------------------------- |
|                               ... Packet Data ...                               |
| ----- | ---------------------------------- | ---------------------------------- |