服務端介面
你的服務端用 App Token 管理遊戲房間:查大區、建房、簽發加入票據、開局、結束、移出玩家、查詢房間和成員。玩家加入房間和連線遊戲節點由客戶端 SDK 完成,不在這裡。下文的介面省略了應用字首,完整地址為 $IM_API/$ORG/$APP 加上表中的路徑,鑑權見鑑權與 App Token。
最常用的三步——建房、籤票據、結束:
curl -X PUT "$IM_API/$ORG/$APP/game/rooms/rank-8812" \
-H "Authorization: Bearer $APP_TOKEN" -H "Content-Type: application/json" \
-d '{"max_players":4,"start_mode":"all_ready","ext":{"match_id":"m-8812"}}'
curl -X POST "$IM_API/$ORG/$APP/game/rooms/rank-8812/tickets" \
-H "Authorization: Bearer $APP_TOKEN" -H "Content-Type: application/json" \
-d '{"users":[{"username":"alice"},{"username":"bob"},{"username":"carol"},{"username":"dave"}]}'
curl -X POST "$IM_API/$ORG/$APP/game/rooms/rank-8812/close" \
-H "Authorization: Bearer $APP_TOKEN" -H "Content-Type: application/json" \
-d '{"result":{"winner":"alice"}}'介面一覽
| 要做什麼 | 方法和路徑 | 能否重試 |
|---|---|---|
| 查可用大區 | GET /game/regions | 可以 |
| 建立房間 | PUT /game/rooms/{room_id} | 可以,同參數返回同一房間 |
| 簽發加入票據 | POST /game/rooms/{room_id}/tickets | 不自動重試;超時後直接重新簽發 |
| 開局 | POST /game/rooms/{room_id}/start | 可以 |
| 結束房間 | POST /game/rooms/{room_id}/close | 可以,只有第一次生效 |
| 移出玩家 | POST /game/rooms/{room_id}/members/{username}/remove | 可以,用同一個 operation_id |
| 查詢房間 | GET /game/rooms/{room_id} | 可以 |
| 列出房間 | GET /game/rooms | 可以 |
| 列出成員 | GET /game/rooms/{room_id}/members | 可以 |
服務端 SDK
四種服務端 SDK 都有 game 模組,方法和介面一一對應,自動換取 App Token、重試可以重試的請求:
| 介面 | Go | Node.js | Python | Java |
|---|---|---|---|---|
| 查大區 | client.Game.ListRegions | client.game.listRegions | client.game.list_regions | im.game().listRegions |
| 建立房間 | Game.CreateRoom | game.createRoom | game.create_room | game().createRoom |
| 簽發票據 | Game.IssueTickets | game.issueTickets | game.issue_tickets | game().issueTickets |
| 開局 | Game.StartRoom | game.startRoom | game.start_room | game().startRoom |
| 結束房間 | Game.CloseRoom | game.closeRoom | game.close_room | game().closeRoom |
| 移出玩家 | Game.RemoveMember | game.removeMember | game.remove_member | game().removeMember |
| 查詢房間 | Game.GetRoom | game.getRoom | game.get_room | game().getRoom |
| 列出房間 | Game.ListRooms、AllRooms | game.listRooms、iterRooms | game.list_rooms、iter_rooms | game().listRooms |
| 列出成員 | Game.ListMembers | game.listMembers | game.list_members | game().listMembers |
Node.js 示例——匹配成功後建房、籤票據,對局結束時結算:
import { DRClient, isDRError } from '@deeprespond/im-server-sdk';
const im = new DRClient({ baseUrl: process.env.IM_API!, orgName: 'demo-org', appName: 'demo', clientId: process.env.IM_CLIENT_ID!, clientSecret: process.env.IM_CLIENT_SECRET! });
export async function startMatch(matchId: string, players: string[]) {
try {
// 房间 ID 用匹配 ID:超时重试也只会有一个房间
await im.game.createRoom(matchId, { max_players: players.length, start_mode: 'all_ready', ext: { match_id: matchId } });
} catch (err) {
if (isDRError(err) && err.reason === 'game_capacity_unavailable') throw new Error('游戏服务繁忙,请稍后重试');
throw err;
}
const tickets = await im.game.issueTickets(matchId, { users: players.map((username, seat) => ({ username, seat })) });
return Object.fromEntries(tickets.map((t) => [t.username, t.ticket])); // 分别交给每个玩家
}
export async function finishMatch(matchId: string, winner: string) {
const { accepted } = await im.game.closeRoom(matchId, { reason: '对局结束', result: { winner } });
return accepted; // false 表示房间之前已经在结束
}各語言的完整示例、方法簽名和錯誤處理:Go、Node.js、Python、Java;安裝和初始化見服務端 SDK。下面按介面說明請求和返回。
查大區
GET /game/regions
{"items": [{"region": "cn-east", "name": "华东", "default": true, "available": true}]}| 欄位 | 說明 |
|---|---|
region | 大區 ID,建房時用 |
default | 是否為應用的預設大區;建房不指定大區時用它 |
available | 當前是否有可排程的遊戲節點。為 false 時在這個大區建房會失敗 |
建立房間
PUT /game/rooms/{room_id},room_id 為 1–64 位字母、數字、_、-,區分大小寫。請求體的欄位都可以省略(省略時發 {}):
| 欄位 | 預設 | 說明 |
|---|---|---|
name | 空 | 房間名,最多 128 個字,只在控制台顯示 |
region | 應用的預設大區 | 大區 ID。與 regions 二選一 |
regions | — | 1–3 個候選大區,按順序嘗試,第一個有可用節點的生效。適合按玩家所在地區排序 |
max_players | 10 | 座位數,1–10 |
sync_mode | frame | frame 幀同步;relay 只轉發自定義訊息,見同步方式 |
tick_rate | 30 | 幀率:10、15、20、30、60。relay 時忽略。幀率越高延遲越低,流量越大 |
start_mode | auto | auto 第一個玩家連上就開局;all_ready 已連上的玩家都準備後開局;manual 由你呼叫開局 |
access_mode | ticket | ticket 憑你簽發的票據加入;open 本應用的任何登入使用者都能加入,不需要票據 |
max_duration_seconds | 3600 | 最長時長,至少 60,不超過平臺上限(控制台「遊戲房間」頁可以看到)。到時自動結束(expired) |
reconnect_window_seconds | 60 | 斷線後保留座位的時間,0–300;0 表示斷線即離開 |
snapshot | 關閉 | {"enabled": true, "interval_seconds": 30}:幀同步房間的狀態快照,玩家斷線太久、缺的幀太多時用快照追上進度。間隔 10–120 秒。relay 不能開啟 |
ext | {} | 你的擴充套件欄位(JSON 物件),不超過 2KB,在回撥裡原樣帶回。適合放對局 ID、模式 |
返回:201 新建,200 同參數的房間已經存在(重複請求),兩種情況都返回 {"room": Room}。房間的欄位見房間。
房間 ID 的規則:同一個 ID、同樣的引數重複建立,返回同一個房間;引數不同,或者這個 ID 的房間已經結束,返回 409 conflict(room_id_used)。所以用對局 ID 作為房間 ID 時,匹配服務可以放心重試。
簽發加入票據
POST /game/rooms/{room_id}/tickets
{"users": [{"username": "alice", "seat": 0}, {"username": "bob"}], "ttl_seconds": 120}| 欄位 | 說明 |
|---|---|
users | 1–10 個使用者。seat 可選,指定座位(0 到 max_players-1);不指定時,玩家加入時分到最小的空座位 |
ttl_seconds | 有效期,30–600 秒,預設 120 |
返回 201:
{"tickets": [
{"username": "alice", "seat": 0, "ticket": "gtk_9f3KqZ1mXb2…", "expires_at": "2026-11-10T08:02:00.000Z"},
{"username": "bob", "seat": null, "ticket": "gtk_Hq7Lm0Pz4c…", "expires_at": "2026-11-10T08:02:00.000Z"}
]}ticket原文只在這裡返回一次。通過你的業務介面交給對應的玩家,不要寫進日誌。- 票據只能用一次,只對這個使用者和這個房間有效。
- 同一使用者再籤一次,他之前沒用的票據作廢。所以請求超時後直接重新簽發即可,服務端 SDK 也不會自動重試這個介面。
open房間不需要票據,簽發返回400。
開局
POST /game/rooms/{room_id}/start,沒有請求體。
只用於 start_mode 為 manual 的房間。遊戲節點開始推進幀後,房間狀態才變為 playing,所以返回時 room.status 可能還是 open;已經在對局中的直接返回。
結束房間
POST /game/rooms/{room_id}/close
{"reason": "对局结束", "result": {"winner": "alice", "scores": {"alice": 12, "bob": 9}}}| 欄位 | 說明 |
|---|---|
reason | 可選,最多 200 字,只寫操作日誌,玩家看不到 |
result | 可選,你的結算結果(JSON 物件),不超過 4KB。原樣發給房間裡的每個玩家(客戶端 onClose 的 result),並出現在 game_room.closed 回撥裡 |
返回:202 表示這次開始結束,房間進入 closing;遊戲節點處理完後變為 closed,併發出 game_room.closed。200 表示房間之前已經在結束或已經結束,返回當前的房間,不修改第一次的原因和結果。
不呼叫這個介面,房間也會在超時、所有人離開後自動結束,見房間的狀態。
移出玩家
POST /game/rooms/{room_id}/members/{username}/remove
{"operation_id": "kick-8812-bob-1", "reason": "作弊"}| 欄位 | 說明 |
|---|---|
operation_id | 冪等鍵,1–64 位字母、數字或 _ . : -。同一個 operation_id 重複請求返回第一次的結果。服務端 SDK 不傳時自動生成,重試時不變 |
reason | 可選,最多 200 字,寫操作日誌 |
返回:202 {"operation_id": "…", "member": Member} 表示已移出;200 {"operation_id": "…", "member": null} 表示這個使用者不在房間裡。
被移出的玩家立即斷開,客戶端 onClose 的 reason 為 kicked,SDK 不會自動重連;他沒用的票據也作廢。要讓他回來,重新簽發票據。
查詢房間
GET /game/rooms/{room_id},返回 {"room": Room}。
列出房間
GET /game/rooms?status=open,playing&search=rank-&limit=20&cursor=…
| 引數 | 說明 |
|---|---|
status | open、playing、closing、closed,多個用逗號分隔;不傳為全部 |
search | 房間 ID 字首 |
limit、cursor | 分頁,limit 1–100,預設 20;下一頁用返回的 next_cursor,為 null 時沒有更多 |
按建立時間從新到舊,返回 {"items": [Room], "next_cursor": "…"}。
列出成員
GET /game/rooms/{room_id}/members,返回 {"items": [Member]}。玩家每加入一次一項:同一座位換了人、同一個人離開後再加入,都會有多項,按加入順序。
房間
| 欄位 | 說明 |
|---|---|
room_id、name | 房間 ID 和名稱 |
status | open、playing、closing、closed |
region、region_name | 所在大區 |
max_players、player_count | 座位數、當前在座的人數 |
sync_mode、tick_rate、start_mode、access_mode | 建立時的設定;relay 房間的 tick_rate 為 0 |
max_duration_seconds、reconnect_window_seconds、snapshot | 建立時的設定 |
ext | 建立時的擴充套件欄位 |
version | 房間版本,每次狀態變化加一;用來判斷回撥的新舊 |
created_at、started_at、closed_at | 時間(ISO 8601,UTC);沒有開局或沒有結束時為 null |
close_reason | 結束原因,見房間的狀態;沒有結束時為空字串 |
result | 結束時的結算結果;沒有時為 null |
成員
| 欄位 | 說明 |
|---|---|
seat、username | 座位和使用者名稱 |
status | joined 已拿到座位還沒連上;connected 線上;disconnected 斷線中(重連視窗內);left 已離開 |
transport | 連線方式:udp 或 ws;沒連上過為空字串 |
rtt_ms | 往返延遲的中位數(毫秒),只在 connected 時有 |
connected_ms | 累計線上時長(毫秒),計費的依據 |
joined_at、left_at | 加入、離開的時間 |
leave_reason | leave 主動離開;timeout 斷線超過重連視窗;removed、kicked 被移出;connect_timeout 拿到座位後 60 秒內沒有連上;room_closed 房間結束 |
錯誤
錯誤的格式見錯誤碼。遊戲房間的具體原因在 details.reason:
| HTTP | code | details.reason | 原因和處理 |
|---|---|---|---|
| 403 | permission_denied | game_disabled | 平臺沒有開放遊戲房間,或單獨關閉了你的應用。聯絡我們 |
| 403 | permission_denied | plan_feature_disabled | 套餐不含遊戲房間。升級套餐 |
| 400 | invalid_argument | region_unavailable | 大區不存在或已停用。用查大區返回的大區 |
| 400 | invalid_argument | — | 引數不合法,message 說明是哪個欄位;簽發票據時使用者不存在或已停用(details.usernames)、同一批指定了重複座位(details.seat) |
| 404 | not_found | — | 房間不存在 |
| 409 | conflict | room_id_used | 房間 ID 已用於引數不同的房間,或這個房間已經結束。換一個 ID |
| 409 | conflict | room_not_open | 房間已在結束或已結束,不能再籤票據、開局 |
| 409 | conflict | operation_id_used | operation_id 已用於別的移出操作。換一個 |
| 409 | limit_exceeded | open_rooms | 同時進行的房間數達到上限。結束不用的房間,或聯絡我們提高上限 |
| 503 | unavailable | game_capacity_unavailable | 大區暫時沒有可用的遊戲節點。稍後重試,或用 regions 給出候選大區 |
| 503 | unavailable | game_node_unavailable | 房間所在的遊戲節點暫時不可用 |
