NON-PRODUCTION

Bandar QQ — tài liệu ghép client

Betting kiểu Baccarat nhưng lá là domino (bộ 28). Red vs Blue, mỗi bên 2 lá. Điểm = tổng pip mod 10. Có tip dealer. Envelope game_event + proto bandarqq_event.

1. Tổng quan

MụcGiá trị
ConnectGET /ws?access_token=<JWT> trên domain game
Lobbyinternal/proto/game-event · package game_event
Gameplaypackage bandarqq_event trong data_send.data
Card idhead * 7 + tail (head, tail ∈ 0…6)
TipC→S tip_dealer TagTipDealer=2 → user_id, amount, user_money
1 userMột connection. Duplicate → lobby 102

Side enum

Giá trịCửa
0 RedĐỏ thắng điểm
1 BlueXanh thắng điểm
2 DrawHòa điểm
3 QiuQiuCả hai = 9
4 TwinsCả 4 lá đều double (đầu=đuôi)
5 QiuRed / 6 QiuBlueBên đó 9 điểm
7 TwinRed / 8 TwinBlueBên đó 2 lá double

State: Pending · Countdown · DealTime · ShowdownTime · BetTime · PreShowdownTime. room_state.tip_dealer_amount = số chip mỗi lần tip.

2. Demo Red vs Blue

3
3
2
0
RED · 8
DRAW
3
1
3
4
BLUE · 1

Red 3-3 + 2-0 = 8; Blue 3-1 + 3-4 = 11 → 1. Red thắng. Twin: lá 3-3.

3. WS envelope (đầy đủ)

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

GET /ws?access_token=<JWT>     // domain game bandarqq
game_event.Request.data_send { room_id: 3  data: <bytes bandarqq_event.Request> }
game_event.Response.game_message { room_id: 3  data: <bytes bandarqq_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 bandarqq_event

C→S Request (oneof)

MessageFieldGhi chú
bet TagBet=1side, bet_amount, count[{bet,count}]Một cửa
multiple_betbets[]Nhiều cửa
tip_dealer Tag=2rỗngTrừ tip_dealer_amount
cheatred_cards[], blue_cards[]Non-prod
Request { bet { side: Red  bet_amount: 1000  count { bet: 1000 count: 1 } } }
Request { tip_dealer {} }

S→C Response (oneof)

MessageField
room_statestate, timers, bet, players[], red_cards[], blue_cards[], red_score, blue_score, side_list[], winning_sides[], logs[{red_score,blue_score,red_twin,blue_twin,red_qiu,blue_qiu,six_twins}], bets[], tip_dealer_amount, max_bet, max_bet_by_vip
start_betdelay_millis
bet / multiple_betuser_id, side, bet_amount, side_total_bet, seat, player_total_bet, count[]
tip_dealeruser_id, amount, user_money
dealrỗng
pre_showdownresult[{user_id,amount,user_money}], bet_sides[]
finishred_cards[], blue_cards[], win_sides[], red_score, blue_score, sides_win_amount[], players_sides_win_amount[]
errorcode + tag (1=bet, 2=tip)
Response { tip_dealer { user_id: 1001  amount: 1000  user_money: 499000 } }
Response { finish {
  red_cards: [24, 14]
  blue_cards: [22, 25]
  win_sides: [Red]
  red_score: 8
  blue_score: 1
  sides_win_amount { side: Red win_amount: 50000 }
  players_sides_win_amount {
    user_id: 1001  total_amount: 1000  user_money: 501000  vip: 1
    sides_win_amount { side: Red win_amount: 1000 }
  }
} }

room_state — restore

FieldClient
state + timersKhóa cược ngoài BetTime
players[]user_id, name, chip, avatar, seat, state, bet_sides[], vip, level
red_cards[], blue_cards[], red_score, blue_score4 lá nếu đang showdown
side_list[], winning_sides[], bets[]Paytable (Red/Blue 1:1, Draw 8, QiuQiu 90, Twins 300…)
logs[]red/blue score + twin/qiu/six_twins flags
tip_dealer_amount, max_bet, max_bet_by_vipGiá tip + trần cược
Response { bet {
  user_id: 1001  side: Red  bet_amount: 1000
  side_total_bet: 15000  seat: 1  player_total_bet: 1000
} }
Response { pre_showdown {
  result { user_id: 1001  amount: -1000  user_money: 498000 }
  bet_sides { side: Red bet_amount: 15000 }
} }
Decode lá: id 24 → head=3 tail=3 (3×7+3). id 14 → 2×7+0. Điểm = (pip1+pip2) mod 10. Luôn tin red_score/blue_score server. Twin = cả hai đầu lá bằng nhau.

Lobby payload mẫu

Request { room_config {} }
Request { join_any_room { bet: 1000  max_players: 0 } }
Request { data_send { room_id: 3  data: <bandarqq_event.Request tip_dealer> } }

Tip Tag=2 có thể gửi ngoài BetTime (tùy server ActionNotAllowed). amount trên ResponseTipDealer = số đã trừ, không phải số dư.

6. Luồng ghép

Connect /ws
room_config
join_room
start_bet
bet / tip_dealer
deal → finish
  1. Connect JWT. Close 1/2/4/5 → dừng.
  2. Join. Restore từ room_state (kể cả 4 lá nếu đang showdown).
  3. start_bet → cược Red/Blue/side. Tip bất kỳ lúc đủ chip (Tag=2).
  4. deal khóa cược. finish lật 2 cặp domino, highlight win_sides, cập nhật money.

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
Gameplay1 NotBetTime · 2 InvalidBet · 3 NotEnoughChip · 4 SideBetNotAllowed · 5 UserNotFound · 6 ActionNotAllowed · tag 1=bet · tag 2=tip

Khi nào chip đổi

EventChip
Tap betOptimistic trừ; confirm ở pre_showdown
tip_dealer−amount ngay, echo user_money
finish+total_amount; tin user_money

8. Checklist client