Game Scenarios
Scenario 1: Joining Room and Starting Game
Steps:
-
Get Room Information:
GET /v1/rooms/room-123
Headers: X-Player-Token: abc123 -
WebSocket Connection:
ws://{API_URL}/ws?room_id=room-123&name=Player1¤cy=USD&launch_url_token=abc123&token= -
Initial Messages from Server:
Tokenmessage (new token and refreshToken)userlistmessage (player list)gameStatemessage (game state)
-
Game Start:
- Server sends
countdownmessages (3, 2, 1...) - Server sends
gameStatemessage (actionState.type = 6- StartGame) - Client emits
GAME_EVENTS.START_GAMEevent - Client emits
GAME_EVENTS.PLAYER_HAND_DEALTevent (tiles are dealt)
- Server sends
Scenario 2: Normal Game Loop
Steps:
-
Turn Start:
- Server sends
gameStatemessage (turn = current player) - Server sends
turn_timer_startmessage - Client emits
CURRENT_PLAYER_TURNevent
- Server sends
-
Player Actions:
-
Draw from Middle:
- Client:
movemessage (moveType: 8) - Server:
gameStatemessage (new tile added to tileBag)
- Client:
-
Draw from Discard:
- Client:
movemessage (moveType: 9) - Server:
gameStatemessage (tile added to tileBag)
- Client:
-
Discard Tile:
- Client:
movemessage (moveType: 7, withtileobject) - Server:
gameStatemessage (tile added to discardPile, turn changes)
- Client:
-
Open Set:
- Client:
movemessage (moveType: 10, withtilesarray) - Server:
gameStatemessage (openSeries updated)
- Client:
-
Merge Set:
- Client:
movemessage (moveType: 11, withgroupId,playerId,tile) - Server:
gameStatemessage (openSeries updated)
- Client:
-
-
Turn Timer:
- Server sends
turn_timer_updatemessages (time updates) - When time expires:
turn_timer_expiredmessage - Automatic timeout penalty applied
- Server sends
Scenario 3: Game End and Play Again Flow
State: Game ended, winner determined.
Step 1: Game End Message
Server sends endRound or gameState message:
{
"type": "endRound",
"gameState": {
"winnerId": "player-1",
"isTie": false,
"gamePhase": "finished",
"players": [
{
"uniqueId": "player-1",
"isWinner": true,
"currentTotalSetPoints": 0,
"remainingTilesScore": 5
},
{
"uniqueId": "player-2",
"isWinner": false,
"currentTotalSetPoints": 15,
"penaltyTotal": 10
}
]
}
}
Client:
GAME_EVENTS.END_GAMEevent is emitted- Game end modal is shown (scores, penalties)
- "Play Again" button becomes active
Step 2: 30-Second Timeout Starts
Server automatically starts a 30-second timeout. During this time:
- Players can send "Play Again" request
- Players can click "Return to Lobby" button
- When timeout expires, automatically return to lobby
Step 3A: A Player Sends "Play Again" Request
Player 1 (Request Sender):
-
Client → Server:
{
"type": "play_again_request",
"token": "abc123",
"userName": "Player1",
"content": {}
} -
Server → Player 1:
{
"type": "playAgainRequestSent",
"data": "Play again request sent successfully"
} -
Server → Other Players (Player 2, Player 3, Player 4):
{
"type": "playAgainNotification",
"from": "player-1",
"content": {
"requestingPlayerId": "player-1",
"requestingPlayerName": "Player 1",
"expiresAt": 1704067300000,
"timeoutSeconds": 30
}
} -
Client (Other Players):
playAgainRequestModalis shown- "Accept" and "Reject" buttons become active
playAgainRequestingPlayeris saved to state
Step 3B: Other Players Respond
Player 2 Accepts:
-
Client → Server:
{
"type": "play_again_response",
"token": "abc123",
"userName": "Player2",
"content": {
"accepted": true
}
} -
Server → All Players:
{
"type": "playAgainAccepted",
"data": {
"accepted": true
}
}
Player 3 Rejects:
-
Client → Server:
{
"type": "play_again_response",
"token": "abc123",
"userName": "Player3",
"content": {
"accepted": false
}
} -
Server → All Players:
{
"type": "playAgainRejected",
"data": {
"accepted": false
}
}
Step 4: Result
State A: All Players Accepted
-
Server → All Players:
{
"type": "playAgainResult",
"data": {
"type": "playAgainResult",
"from": "player-2",
"content": {
"accepted": true,
"respondingPlayerId": "player-2",
"reason": ""
}
}
} -
Client:
UI_EVENTS.ON_RESTART_GAMEevent is emitted- Game end modal closes
- Play again request modal closes
- New game starts (new
gameStatemessage arrives)
State B: A Player Rejected
-
Server → All Players:
{
"type": "playAgainResult",
"data": {
"type": "playAgainResult",
"from": "player-3",
"content": {
"accepted": false,
"respondingPlayerId": "player-3",
"reason": ""
}
}
} -
Client:
onGameEnd()function is called- WebSocket connection is closed
window.parent.postMessage({ type: 'return_lobby' })is sentLeaveRoomaction is sent- Players return to lobby
State C: Timeout Expired (30 Seconds)
-
Server → All Players:
{
"type": "gameEndTimeout"
} -
Client:
onGameEnd()function is called- WebSocket connection is closed
window.parent.postMessage({ type: 'return_lobby' })is sentLeaveRoomaction is sent- Players return to lobby
Step 5: Direct Return to Lobby (Always Possible)
Any player can click the "Return to Lobby" button:
-
Client → Server:
{
"type": "leave_table",
"token": "abc123",
"userName": "Player1",
"content": {
"moveType": 2,
"timestamp": 1704067200000
}
} -
Client:
window.parent.postMessage({ type: 'return_lobby' })is sent- WebSocket connection is closed
- Player returns to lobby
Note: Clicking "Reject" button in the play again request modal also performs the same action.
Scenario 4: Connection Loss and Reconnection
State: WebSocket connection lost.
-
Client:
SOCKET_CLOSEevent is emitted- Disconnected modal is shown
- Auto-reconnect starts (after 1 second)
-
Reconnection:
SOCKET_RECONNECTevent is emittedstate_recoverymessage is requested- Server sends current game state
-
State Recovery:
{
"type": "state_recovery",
"current_state": {
// Full game state
}
}
Scenario 5: Timer Management
Turn Timer:
-
Turn Start:
- Server sends
turn_timer_startmessage - Client starts timer
- Server sends
-
Timer Updates:
- Server sends
turn_timer_updatemessages (every second) - Client updates timer
- Server sends
-
Timer Expired:
- Server sends
turn_timer_expiredmessage - Client applies automatic timeout penalty
- Server sends
Timer Status Request:
When client becomes visible (tab switch):
{
"type": "get_timer_status",
"token": "abc123",
"userName": "Player1",
"content": {}
}
Server sends current timer status:
{
"type": "turn_timer_status",
"data": {
"status": {
"current_turn": "player-1",
"turn_duration": "30",
"timers": { ... }
}
}
}