講完封包的標頭格式,再來就是遊戲的關鍵資料結構-- Protobuf 命令訊息,也是跟 JSON 的最大差別--強制型態確認,這個至關重要,後續所有伺服器跟客戶端的互動都是基於這些命令定義,也是開發自動測試腳本的主要關注點,今天會把遊戲用到的幾個命令訊息一一拆解說明。
雖然是兩天前的內容,不過為了快速查閱還是放一份在這,遊戲流程從高階的觀點可以這樣看:
實際上,稍微把上述階段跟實際命令展開,可以得到以下彙整表:
階段 | 客戶端請求 | 服務端回應 |
---|---|---|
登入 | LoginRequest | LoginResponse |
進房間 | RoomJoinRequest | RoomJoinResponse |
取得快照 | SnapshotRequest | SnapshotResponse |
下注 | BetPlacementRequest | BetPlacementResponse |
下注結束 | BetFinishedRequest | BetFinishedResponse |
開獎與派彩結果 | ReckonResultRequest | ReckonResultResponse |
稍微解釋一下快照,完成登入跟進入房間後,需要取得目前遊戲的狀態,在業界術語就是快照,這是初始化過程或者遊戲狀態同步很重要的一個命令訊息
接下來,就會一一拆解命令訊息
// game_messages.proto
syntax = "proto3";
package gaming;
第一行:syntax = "proto3";作用
指定 Protobuf 語法版本:告訴編譯器使用 Protocol Buffers 的第3版語法
必須放在文件最開頭(除了註釋)
第二行:package gaming;
定義命名空間:避免不同文件中的消息類型名稱衝突
組織代碼結構:將相關的消息類型分組
// Authentication Messages
message LoginRequest {
string username = 1;
string password = 2;
}
message LoginResponse {
bool success = 1;
string message = 2;
string session_token = 3;
int64 user_id = 4;
int64 balance = 5;
}
玩家端使用 LoginRequest 登入時,要帶入兩個參數,帳號跟密碼,這個應該不需多做解釋
服務端會套用 LoginResponse 回應,內容包括:
// Room Management Messages
message RoomJoinRequest {
int32 room_id = 1;
}
message RoomJoinResponse {
bool success = 1;
string message = 2;
int32 room_id = 3;
int32 player_count = 4;
int64 jackpot_pool = 5;
}
登入遊戲完成認證後,下一步就是進入指定的遊戲房間,RoomJoinRequest 輸入的參數也很簡單,就是哪一間房間
RoomJoinResponse 出參為求擬真,會回傳以下內容:
// Game State Messages
message SnapshotRequest {
// Empty message
}
message SnapshotResponse {
int64 user_balance = 1;
repeated Bet active_bets = 2;
int32 current_room = 3;
int64 jackpot_pool = 4;
GameRoundStatus round_status = 5;
}
message Bet {
int32 dice_face = 1;
int64 amount = 2;
string bet_id = 3;
string round_id = 4;
}
enum GameRoundStatus {
NO_ACTIVE_ROUND = 0;
BETTING_PHASE = 1;
WAITING_RESULTS = 2;
}
SnapshotRequest 輸入參數很簡單,就是不需要參數
SnapshotResponse 取得快照的返回值這邊又稍微複雜一點,可以看到應用了 Protobuf 的引用跟陣列性質
// Betting Messages
message BetPlacementRequest {
int32 dice_face = 1; // 1-6
int64 amount = 2; // Bet amount
string round_id = 3; // Game round identifier
}
message BetPlacementResponse {
bool success = 1;
string message = 2;
string bet_id = 3;
int64 remaining_balance = 4;
string round_id = 5;
}
message BetFinishedRequest {
string round_id = 1; // Game round identifier
}
message BetFinishedResponse {
bool success = 1;
string message = 2;
string round_id = 3;
}
下注階段可細分兩個: 下注中跟結束下注
BetPlacementRequest 下注中的輸入參數就是:
BetPlacementResponse 回傳值有:
可以發現上述下注動作一次只能押注一個數字,不過結束前可以重複執行達到多數字押注的目標
下注完畢後,使用 BetFinishedRequest 通知服務端下注完成,參數只有一個--局號
BetFinishedResponse 回傳值一樣只有一個參數--局號
// Result Messages
message ReckonResultRequest {
string round_id = 1; // Game round identifier
}
message ReckonResultResponse {
int32 dice_result = 1; // 1-6
repeated BetResult bet_results = 2;
int64 total_winnings = 3;
int64 new_balance = 4;
int64 updated_jackpot_pool = 5;
string round_id = 6;
}
message BetResult {
string bet_id = 1;
int32 dice_face = 2;
int64 bet_amount = 3;
bool won = 4;
int64 payout = 5;
string round_id = 6;
}
通知下注結束後,再來就是等待開獎
首先用 ReckonResultRequest 請求開獎結果,,參數只有一個--局號
ReckonResultResponse 會回應以下內容:
至此,有命令訊息定義,玩家跟服務端雙方就能按此規格互相通訊了,理論定義花了不少篇幅,不過相信對於讀者的整體理解是有幫助的
下一章結就是開始 Python 端的實作,逐步按照規劃把服務器端做出來、一一測試然後包裝成 Robot Framework 的結構