NON-PRODUCTION

Dragon Tiger — tài liệu ghép client

Cược Dragon / Tiger / Tie (+ Big/Small từng cửa). Mỗi bên 1 lá. Envelope lobby game_event; payload bàn proto dragontiger_event. Frame binary protobuf.

1. Tổng quan

MụcGiá trị
ConnectGET /ws?access_token=<JWT> trên domain game
Lobbyinternal/proto/game-event · package game_event
Gameplaypackage dragontiger_event trong data_send.data
Card idrank * 4 + suit. So sánh rank; hòa → cửa Tie
1 userMột connection. Duplicate → lobby 102

Side enum

Giá trịCửa
0 DragonLá rồng cao hơn
1 TigerLá hổ cao hơn
2 TieHòa rank
3 DragonBigDragon rank lớn (side bet)
4 DragonSmallDragon rank nhỏ
5 TigerBigTiger rank lớn
6 TigerSmallTiger rank nhỏ

State: Pending · Countdown · DealTime · ShowdownTime · BetTime · PreShowdownTime. Tỷ lệ lấy room_state.side_list (vd Tie 10:1).

Cược chỉ trong BetTime sau start_bet. multiple_bet để đặt Dragon + Tie một lúc.

2. Demo Dragon vs Tiger

K♥
DRAGON
score 13
TIE
7♠
TIGER
score 7

Viền vàng = win_sides chứa Dragon. Lật 2 lá theo finish.dragon_card / tiger_card. Rank K > 7 → Dragon thắng; Tie nếu hai rank bằng nhau.

Card id

id = rank * 4 + suit. Rank 0=A … 12=K (tuỳ enum gamecard). Suit 0–3. Ví dụ K♥ ≈ 44, 7♠ ≈ 20 — lấy đúng field int32, client map sprite.

Không dùng công thức điểm Baccarat (mod 10). Cửa chính so rank; Big/Small là side bet theo ngưỡng rank trong side_list.

3. WS envelope (đầy đủ)

Frame binary protobuf, không JSON. Handshake: 0 closed · 1 other device playing · 2 too many conn · 4 token expired · 5 banned.

GET /ws?access_token=<JWT>     // domain game dragontiger
game_event.Request.data_send { room_id: 11  data: <bytes dragontiger_event.Request> }
game_event.Response.game_message { room_id: 11  data: <bytes dragontiger_event.Response> }

Ngoài data: user_joined / user_left / start_countdown / start_game / reset_game / auto_leave.

4. Lobby C→S / S→C

C→SField
room_config→ room_types, play_now, best_choice, disable_*
get_room_listbet
create_roombet, password, max_players, name
join_roomroom_id, password, is_viewer
join_any_roombet, max_players
leave_roomroom_id
find_roomroom_id
inviteroom_id, password, user_id
free_users_listbet
send_chat_messageroom_id, message, to[], is_private_chat
data_sendbytes game hoặc auto_leave
S→CField
room_config / room_listsảnh / bàn
game_messagepayload bàn
errorcode + tag
chat_messagechat
invite / someone_invited_youmời

5. Gameplay proto dragontiger_event

C→S Request (oneof)

MessageFieldGhi chú
bet TagBet=1side, bet_amount, count[{bet, count}]Một cửa + chip animate
multiple_betbets[]Nhiều cửa
cheatdragon_card, tiger_cardNon-prod
Request { bet { side: Dragon  bet_amount: 1000  count { bet: 1000 count: 1 } } }
Request { multiple_bet { bets { side: Dragon bet_amount: 500 } bets { side: Tie bet_amount: 100 } } }

S→C Response (oneof)

MessageField
room_statestate, timers, bet, players[], dragon_card, tiger_card, dragon_score, tiger_score, side_list[], winning_sides[], bets[], history[{sides, dragon_card, tiger_card}], max_bet, max_bet_by_vip, is_registered_leave
start_betdelay_millis
bet / multiple_betuser_id, side, bet_amount, side_total_bet, seat, player_total_bet, count[]
dealrỗng
pre_showdownresult[{user_id, amount, user_money}], bet_sides[] — sync chip đã trừ
finishdragon_card, tiger_card, win_sides[], sides_win_amount[], players_sides_win_amount[], dragon_score, tiger_score
errorcode + tag=1 RequestBet
Response { start_bet { delay_millis: 30000 } }
Response { finish {
  dragon_card: 44
  tiger_card: 20
  win_sides: [Dragon, DragonBig]
  dragon_score: 13
  tiger_score: 7
  sides_win_amount { side: Dragon  win_amount: 80000 }
  players_sides_win_amount {
    user_id: 1001  total_amount: 1000  user_money: 501000
    sides_win_amount { side: Dragon win_amount: 1000 }
  }
} }

room_state — restore

FieldClient
state, count_down_millis, state_start_time, state_end_timeĐồng hồ
players[]user_id, name, chip, avatar, seat, disconnected, state, bet_sides[], level, vip
dragon_card, tiger_card, dragon_score, tiger_scoreCó khi đang Deal/Showdown
side_list[], bets[], winning_sides[]Paytable + chip + cửa thắng
history[]sides[], dragon_card, tiger_card — bead road
max_bet, max_bet_by_vip, is_registered_leaveChặn cược / tự kick
Response { bet {
  user_id: 1001  side: Dragon  bet_amount: 1000
  side_total_bet: 22000  seat: 0  player_total_bet: 1000
  count { bet: 1000 count: 1 }
} }
Response { pre_showdown {
  result { user_id: 1001  amount: -1000  user_money: 499000 }
  bet_sides { side: Dragon  bet_amount: 22000 }
} }
Optimistic chip lúc tap. pre_showdown ghi đè nếu lệch. Win lấy players_sides_win_amount.user_money. Score là rank lá (không mod 10 như baccarat).

Lobby payload mẫu

Request { room_config {} }
Request { join_room { room_id: 11  password: ""  is_viewer: false } }
Request { data_send { room_id: 11  data: <dragontiger_event.Request bet> } }

Big/Small là side bet theo rank lá (không phải tổng điểm). Multiplier mặc định Tie 10:1; đọc side_list chứ đừng hardcode. Cheat dragon_card/tiger_card chỉ non-prod.

6. Luồng ghép

Connect /ws
room_config
join_room
start_bet
bet / multiple_bet
deal → pre_showdown → finish
  1. Connect JWT. Close 1/2/4/5 → dừng.
  2. room_config rồi join. Restore felt từ room_state.
  3. start_bet mở cửa. Gửi bet Tag=1 (hoặc multiple_bet).
  4. deal khóa. pre_showdown sync money. finish lật 2 lá, highlight win_sides.

7. Error codes

Handshake0 closed · 1 other device playing · 2 too many conn · 4 token expired · 5 banned
Lobby100 missing payload · 102 duplicated login · 103 room not found · 108 bad password · 113 room full · 114 in other room · 115 bet not found · 120 not enough money
Gameplay tag=11 NotBetTime · 2 InvalidBet · 3 NotEnoughChip · 4 SideBetNotAllowed · 5 UserNotFound · 6 ActionNotAllowed

Khi nào chip đổi

EventChip
Tap bet (optimistic)Client trừ tạm; server chưa confirm
pre_showdownGhi đè result[].user_money
finish+total_amount từng user; source of truth = user_money

8. Checklist client