Giống Tiến Lên, thêm hệ số sao (1–4). Server random sao lúc chia bài và gửi luôn trong gói deal. Không còn bước user chọn sao. DealTime dài hơn (~10s) để client diễn animation chia bài + random sao.
GET /tienlenstar/tienlenstar_event.proto
GET /tienlenstar/game_event.proto
# Host = domain DOCUMENT (trang docs này), không phải domain GAME.
3. Demo bàn (CSS)
DealTime: chia bài + random sao. Client quay ★ rồi dừng đúng deal.star (ví dụ 3). User không bấm chọn.
Tây
13
Bắc
12
Đông
11
3♠
3♣
★ 1
★ 2
★ 3 deal.star
★ 4
Animation random trong DealTime (~10s) rồi khóa vào giá trị server gửi. Không gửi request nào lúc này.
4. Kết nối & envelope
Mọi frame WebSocket là binary protobuf — không JSON. Một user chỉ được một connection; login trùng → lobby error 102.
GET /ws?access_token=<JWT>
# Host = domain GAME (không phải docs). Frame = bytes proto, không text JSON.
Handshake close
Ý nghĩa — client phải làm
0
Closed — đóng bình thường, có thể reconnect
1
Other device — đang chơi máy khác, hiện toast, đừng spam reconnect
2
Too many — quá nhiều connection, đợi rồi thử lại
4
Expired — JWT hết hạn, lấy token mới rồi connect lại
5
Banned — tài khoản bị cấm, dừng retry
Envelope lobby package game_event (internal/proto/game-event). Payload bàn nằm trong data_send.data (C→S) và game_message.data (S→C) — bytes của proto game.
C→S game_event.Request
data_send.data = bytes game Request
S→C game_event.Response
game_message.data = bytes game Response
Unmarshal 2 lớp: (1) lobby Request/Response. (2) Nếu oneof là data thì unmarshal tiếp proto game. room_id trên envelope phải khớp bàn đang ngồi. Cheat chỉ non-prod.
5. Lobby C→S (game_event.Request)
oneof
Field
Mô tả
room_config
(empty)
Lấy config sảnh ngay sau handshake
get_room_list
bet int64
List bàn theo đúng mức cược
create_room
bet int64, password string, max_players int32, name string
Tạo bàn. password rỗng = public
join_room
room_id int32, password string, is_viewer bool
Vào bàn chỉ định. Viewer tạm để false
join_any_room
bet int64, max_players uint32
Matchmaking. max_players=0 = mọi size
leave_room
room_id int32
Rời bàn ngay
find_room
room_id int32
Tra cứu trước khi join (private?)
invite
room_id, password, user_id
Mời user rảnh
free_users_list
bet int64
User rảnh cùng mức cược, không đang bàn khác
send_chat_message
room_id, message, to[] int32, is_private_chat
Chat bàn; whisper khi is_private_chat + to
data_send
room_id + oneof data bytes | auto_leave
Mọi action gameplay. auto_leave = đăng ký out cuối ván
Ngồi bàn: nhận user_joined (kể cả mình), có thể room_state trong game_message.data.
Đủ người: start_countdown rồi start_game. Từ đây chỉ gửi action qua data_send.data.
Chat: send_chat_message → chat_message. Mời: free_users_list → invite → đối phương nhận someone_invited_you.
Hết ván: reset_game. Rời: leave_room hoặc data_send.auto_leave (out sau ván).
Reconnect giữa ván: giữ cùng JWT, join_room lại (cùng room_id). Server trả game_message.data = room_state proto game. Đừng tạo phòng mới. Một user một socket — socket cũ bị đóng với handshake 1 hoặc lobby 102.
Cấm
Lý do
JSON trên WS
Server chỉ unmarshal protobuf binary
Hai tab cùng account
102 duplicated login / handshake 1
data_send khi chưa join
118 not in room / 103 not found
create_room bet lạ
115 bet not found
Lỗi lobby: 100 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. Bảng đầy đủ mục Errors.
7. Gameplay C→S (proto tienlenstar_event)
Marshal message dưới thành bytes, gửi trong data_send.data. Chỉ play trong ván thường — không có chose_star.
oneof
Field
Tag / ghi chú
play
cards[], combination
TagPlay=1 — đánh bài, giống tienlen. Pass: combination=8, cards=[]
play { cards: [4], combination: Card } // TagPlay=1
play { cards: [], combination: Pass }
Gửi play trong DealTime → error 1 WrongState. Đợi hết animation deal/sao, nhận player_turn rồi mới đánh.
8. Gameplay S→C
Nằm trong game_message.data. Reconnect: unmarshal room_state trước, rồi sync UI.
oneof
Field
Khi nào
deal
cards[], combination (InstantWin), show_card_count, star (1–4)
Chia 13 lá + sao. Diễn animation chia bài và random sao trong DealTime (~10s). star là kết quả cuối, không phải gợi ý.