INTEGRATION GUIDE
Game Service Integration Guide
Complete integration documentation for engineering teams: client loading, JS Bridge communication, server deployment, signed APIs, and callbacks.
1. Game Integration Modes
Gamefans supports two integration modes on the game side: the game-owned lobby mode and the business-side matchmaking mode. The business workflow and the APIs that need to be invoked differ between the two modes.
1.1 Game-Owned Lobby Mode
The sequence diagram for this mode is shown below. In this mode, the game itself maintains the seat state: when a player joins or leaves the game, the game actively calls the relevant server-side APIs. The business side can also use the related APIs to seat/unseat players or add bots.
1.2 Business-Side Matchmaking Mode
In this mode, the business side maintains the game player state and, when a game needs to start, calls the game service APIs to inform the game of the player list and to start the game. The sequence diagram after integrating this mode is shown below.
2. Client Integration
The game package is delivered as static files, deployed as a web page, and loaded via a URL. The interaction between the game and the business-side client is performed through a JS Bridge. The URL parameters control the game's behavior; the JS Bridge methods and events enable interaction between the game and the business-side client. The game ships with a set of predefined events and methods, and additional events or methods can be added per business-side requirements.
2.1 Client Deployment
The delivered game client consists of static files. It must be deployed as a web page to the appropriate server, object storage, or other location, with CDN enabled as required.
2.2 Client Loading
Once the game is deployed, it can be loaded via a URL. The following parameters are required to load the game:
| Parameter | Type | Description |
|---|---|---|
| app_id | string | APP ID |
| room_id | string | Room ID |
| token | string | User identifier |
| language | string | Game language code |
| extra | JSON | Other information |
The extra field contains the following parameters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| http_url | string | Yes | -- | URL of the HTTP API called by the game |
| ws_url | string | Yes | -- | URL of the WebSocket API called by the game |
| top | int | No | 0 | Safe area top boundary |
| bottom | int | No | 0 | Safe area bottom boundary |
| left | int | No | 0 | Safe area left boundary |
| right | int | No | 0 | Safe area right boundary |
| width | int | No | -- | Game width |
| height | int | No | -- | Game height |
| auto_scale | int | No | 0 | Whether to auto-scale: 0 disables, 1 enables |
Taking the following parameter values as an example:
| Parameter | Value |
|---|---|
| app_id | 5147182126419264 |
| room_id | 89945 |
| token | HWUEv2uXigqYEG0t |
| language | zh-CN |
| extra | {"http_url":"https://example.com/api","ws_url":"wss://example.com/websocket-endpoint","top":100,"bottom":300,"auto_scale":0} |
The final constructed game URL is as follows:
https://example.com/ludo?app_id=5147182126419264&room_id=89945&token=HWUEv2uXigqYEG0t&language=zh-CN&extra=%7B%22http_url%22%3A%22https%3A%2F%2Fexample.com%2Fapi%22%2C%22ws_url%22%3A%22wss%3A%2F%2Fexample.com%2Fwebsocket-endpoint%22%2C%22top%22%3A100%2C%22bottom%22%3A300%2C%22auto_scale%22%3A0%7DThe extra parameter is a url-encoded JSON string, which after decoding is:
{"http_url":"https://example.com/api","ws_url":"wss://example.com/websocket-endpoint","top":100,"bottom":300,"auto_scale":0}2.3 Client APIs
The game and the business side communicate through a JS Bridge. The business-side APP needs to register a JS Bridge to implement the relevant functions. The following files contain the Android and iOS integration code samples:
2.3.1 Android Integration
1. Add JS Interface
mViewModel = new GameViewModel(mWebView);
mWebView.addJavascriptInterface(mViewModel, "GameJSBridgeAndroid");2. GameViewModel.java
Communication is centralized in this file and can be reused directly
1. Send message to the game
String state = "app_common_android";
String json = "{\"key\":\"value\"}";
mViewModel.sendMessageToGame(state, json, new GameViewModel.GameMessageCallback() {
@Override
public void onCallbackMessage(String json) {
LogUtils.d("收到了游戏的消息回调:" + json);
}
});2. Set up a listener to receive data from the game
mViewModel.setOnGameMessageListener(new GameViewModel.OnGameMessageListener() {
@Override
public void onMessage(String state, String json, GameViewModel.GameMessageHandler handler) {
LogUtils.d("收到游戏发送过来的消息:" + state + " json:" + json);
// 给游戏回调应答消息,必须要调用,格式和内容请查看文档定义
String backJson = "{\"msg\":\"Android收到消息了\"}";
handler.completed(backJson);
}
});2.3.2 iOS Integration
1. Add JS Message Handlers
// 添加消息处理,其中onMessageFromGame和onCallbackMessage是必须的
userContentController.add(context.coordinator, name: "onMessageFromGame")
userContentController.add(context.coordinator, name: "onCallbackMessage")
// 在Coordinator的userContentController方法中,将消息传递给GameViewModel
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
parent.viewModel.userContentController(name: message.name,body: "\(message.body)")
}2. GameViewModel.swift
Communication is centralized in this file and can be reused directly
1. Send message to the game
let state = "app_common_ios"
let json = "{\"testKey\":\"haha\"}"
viewModel.sendMessageToGame(state:state,json:json){ json in
print("count:\(count) 游戏给的回调数据为:\(json ?? "")")
}2. Set up a listener to receive data from the game
// 设置游戏消息监听
viewModel.setOnGameMessageListener(listener: MyGameMessageListener())
class MyGameMessageListener: OnGameMessageListener {
func onMessage(state: String, json: String, handler: (String) -> Void) {
print("iOS收到游戏发来的消息,state:\(state) json:\(json)")
// 给游戏回调应答消息,必须要调用,格式和内容请查看文档定义
let backJson = "{\"msg\":\"iOS收到消息了\"}"
handler(backJson)
}
}2.4 Client Events
When the game and the APP communicate bidirectionally through the JS Bridge, both sides need to pass the state and data parameters, together with callback as the response handler.
Using sendMsgToApp as an example:
bridge.sendMsgToApp = function (state, data, callback) { ... }| Parameter | Type | Description |
|---|---|---|
| state | string | Command identifier — the event carried by the communication |
| data | JSON string | Data carried by the communication, formatted as a valid JSON string |
| callback | callable | Response callback |
2.4.1 Events Sent from the Game to the APP
1. Game Load Completed
mg_common_notify_load_completed notifies the app that the game has finished loading
| Parameter | Content | Description |
|---|---|---|
| state | mg_common_notify_load_completed | This message is sent when the game finishes loading |
| json | --- | |
| callback | --- |
2. Game Settlement
mg_common_game_settlement notifies the app of the game-end data
| Parameter | Content | Description |
|---|---|---|
| state | mg_common_game_settlement | Sent when the game settlement screen appears |
| json | Structure as follows | |
| callback | --- |
The data field structure is as follows:
{
"results": IResult[], // 玩家信息列表
"reason": number, // 游戏结束原因 0: 正常结束 3: 提前结束
"roundId": string, // 局id
"chessNum": number, // 当局玩法棋子个数 4 或者 2
"item": number, // 当局游戏是否有道具 0:无 1:有
"model": number // 当局游戏是快速模式还是经典模式 0:快速 1:经典
}IResult interface definition:
interface IResult{
uid: string,
appId: string,
userId: string, // 对应app侧的玩家id
name: string,
avatar: string,
gender: string,
score: number, // 得分
rank: number,
isEscaped: number,
isAI: number,
isManaged: number, // 是否托管完成游戏 1:托管完成 0:不是托管完成
extras: string,
}
3. Other Events
The game can send relevant events during the game flow according to the business-side requirements.
2.4.2 Events Sent from the APP to the Game
These events can be flexibly defined according to business-side requirements, for example operating the game's background music or sound effects through the native UI, or showing relevant game UI. After registering the JS Bridge, the following call can be used to send events to the game:
bridge.sendMsgToGame = function (message) {}3. Server Deployment
The server side is delivered as compiled binaries. The business side must provide the required APIs according to the documentation to complete the server configuration and deployment.
3.1 Server Resources
The server deployment must meet the following minimum server requirements:
| No. | Spec | Count | Purpose |
|---|---|---|---|
| 1 | 1 core / 2 GB | 1 unit | etcd service |
| 2 | 2 cores / 4 GB | depends on request volume | Game service |
If a single-machine deployment is required, the etcd service can be deployed together with the game service on the same server with a 2-core/4GB configuration; however, for environment management and scalability, it is recommended to keep them on separate servers.
In addition to the server resources above, the business side also needs to prepare a redis service.
3.2 Deployment Process
- File preparation: the delivered game server binary package gfs.zip
- API preparation: the get-user-info API and the game-event notification callback API required by the game server; refer to the next section for details
- Deploy the etcd service according to the server requirements in the table above
- Prepare the redis service
- Upload gfs.zip to the game deployment server and unzip it
- Modify etcd settings in httpgate/config/config.yaml, return to the httpgate directory, and run bash service.sh start to launch the service
- Modify etcd and redis settings in tcpgate/config/config.yaml, return to the tcpgate directory, and run bash service.sh start to launch the service
- Modify etcd and redis settings in timer/config/config.yaml, return to the timer directory, and run bash service.sh start to launch the service
- Modify etcd and redis settings in {{game_name}}/config/config.yaml, fill in get_user_info_url and notify_url, return to the ludo directory, and run bash service.sh start to launch the service
- If distributed deployment is required, repeat steps 3 through 7
Notes:
- Use bash to execute the service.sh script
Configuration items that need to be modified include:
- etcd_urls: etcd service address
- redis: redis service configuration
- app_info.app_id
- app_info.secret
- app_info.get_user_info_url: get-user-info endpoint URL the game server calls
- app_info.notify_url: event-notification endpoint URL the game server calls
- Check the executable permission of the service.sh script before starting the service; if it lacks execute permission, add it via chmod +x service.sh
4. Server Integration
All server APIs follow the rules below:
- All APIs exchange data using UTF-8 encoded JSON strings
- The basic format of the API response is as follows. The callback APIs provided by the business side must also follow this format. Unless otherwise specified, the rest of this document assumes this response format and does not list it separately.
{
"ret_code": 0, // 接口响应码,用来判断本次请求是否成功
"ret_msg": "ok", // 接口响应信息,可提供简单的
"data": [] // 响应数据,由具体业务确定其类型
}4.1 Game Callback APIs Provided by the Business Side
All APIs in this section must be provided by the business side. The game server calls them at appropriate moments during the game's runtime to obtain the relevant information and complete the corresponding functions.
4.1.1 Get User Info
The game server uses this API to retrieve the basic information of in-game users, such as username and avatar.
- Request Definition
POST 业务侧提供URL
Accept: application/json
Content-Type: application/json
{
"token": "{{token}}",
"app_id": "{{app_id}}"
} | Parameter | Type | Description |
|---|---|---|
| token | string | Unique user identifier |
| app_id | string | APP unique identifier |
- Response Definition
{
"ret_code": 0,
"ret_msg": "",
"data": {
"user_id": "123456",
"name": "Username",
"avatar": "https://example.com/avatar.png",
"gender": "male",
"is_ai": 0,
"extras": "{\"skin_type\": 0}"
}
}| Field | Type | Description |
|---|---|---|
| user_id | string | Unique user identifier |
| name | string | Display name |
| avatar | string | Avatar URL |
| gender | string | Gender |
| is_ai | int | Bot: 0 = real player, 1-3 = bot level |
| extras | string | JSON-string extension field |
`extras` field definition: this field has type `string`, which is an escaped `JSON` string. The specific data interface needs to be defined according to the concrete business requirements.
4.1.2 Game Event Callback
When the game state changes, the game server synchronizes the game state by calling this API.
Common API Information
All game callback APIs include the parameters specified here. The data field is determined by each individual event — refer to the corresponding request definition for the specific event.
- Request Definition
POST 业务侧提供URL
Accept: application/json
Content-Type: application/json
{
"event": "{{game_event}}",
"notify_id": "{{notify_id}}",
"game_name": "{{game_name}}",
"app_id": "{{app_id}}",
"room_id": "{{room_id}}",
"timestamp":"{{timestamp}}",
"data": {}
}| Parameter | Type | Description |
|---|---|---|
| event | string | Game event type |
| notify_id | string | Unique identifier of the event message |
| game_name | string | Game name |
| app_id | string | APP unique identifier |
| room_id | string | Room unique identifier |
| timestamp | string | Millisecond timestamp string |
| data | object | Other related information; refer to the request definition of the specific event |
- Response Definition
{
"ret_code": 0,
"ret_msg": "",
"data": null
}Game start event: game_start
When the game officially starts, the server calls this API to notify the game_start event.
- Request Definition
{
// 通用字段
"event": "game_start",
...
"data": {
"mode": 1,
"round_id": "{{round_id}}",
"start_time": 11230182038080,
"players": [{
"user_id": "{{user_id}}",
"seat_index": 0,
"status": "IDLE",
"is_ai": 0
}],
"extras": "",
"app_extras": ""
}
}| Parameter | Type | Description |
|---|---|---|
| mode | int | Mode |
| round_id | string | Round ID |
| start_time | int | Game start timestamp |
| players | PlayerInfo[] | List of players participating in the game |
| extras | string | Extension field |
| app_extras | string | Business-side pass-through field |
| Parameter | Type | Description |
|---|---|---|
| user_id | string | User ID |
| seat_index | int | Seat index |
| status | string | Status: IDLE = idle, READY = ready |
| is_ai | int | Whether the player is a bot |
Game end event: game_end
When the game ends, the server calls this API to notify the game_end event.
- Request Definition
{
// 通用字段
"event": "game_end",
...
"data": {
"mode": 1,
"round_id": "{{round_id}}",
"start_time": 11230182038080,
"end_time": 11230182038080,
"duration": 5000,
"results": [{
"user_id": "{{user_id}}",
"is_ai": 0,
"rank": 1,
"score": 10,
"is_win": 1,
"is_escaped": 0,
"is_managed": 0,
"extras": ""
}],
"extras": "",
"app_extras": ""
}
}| Parameter | Type | Description |
|---|---|---|
| mode | int | Mode |
| round_id | string | Round ID |
| start_time | int | Game start timestamp |
| end_time | int | Game end timestamp |
| duration | int | Game duration in milliseconds |
| results | Result[] | List of players |
| extras | string | Extension field |
| app_extras | string | Business-side pass-through field |
| Parameter | Type | Description |
|---|---|---|
| user_id | string | User ID |
| is_ai | int | Whether the player is a bot |
| rank | int | Rank |
| score | int | Score |
| is_win | int | Result: 1 = loss, 2 = win, 3 = draw |
| is_escaped | int | Whether escaped: 0 = no, 1 = yes |
| is_managed | int | Whether managed (auto-play): 0 = no, 1 = yes |
| extras | string | Extension field |
4.2 Calling the Game Server APIs
4.2.1 API Signature Mechanism
Calling the game server APIs requires signature verification. This section describes the parameters and the signature method required for API signing. Unless otherwise specified, signature verification is required for all APIs.
1. Signature Parameters
| Parameter | Type | Description |
|---|---|---|
| auth_type | string | Authentication type: gfs |
| app_id | string | APP unique identifier |
| secret | string | APP key |
| timestamp | string | Request timestamp |
| nonce | string | Random string |
| body | json string | Request body in JSON format |
2. Signature Steps
Concatenate the parameters in the following format: four lines, each ending with \n (including the last line).
{{app_id}}\n
{{timestamp}}\n
{{nonce}}\n
{{body}}\nUse `secret` as the key to apply `HmacSHA1` encryption on the string from the previous step, producing the request signature.
sign = hmac_sha1(origin, secret)Add the `Authorization` request header to the request. The content must be written on a single line.
POST url
Authorization: {{auth_type}} app_id="{{app_id}}",timestamp={{timestamp}},nonce={{nonce}},signature={{sign}}3. Signature Example
Use the following data for the signature demonstration
| Parameter | Sample value |
|---|---|
| auth_type | gfs |
| app_id | 123456 |
| secret | abcdef |
| timestamp | 1718777147021 |
| nonce | wHwZk4veHDFSpiw5 |
| body | -- |
Build the signature string `origin`
123456
1718777147021
wHwZk4veHDFSpiw5
{"event":"user_enter","app_id":"123456","room_id":"200071","timestamp":"1718777146975","data":{"user_info":{"user_id":"200001","avatar":"https://gfs-static.cyouth.cn/upload/avatar/133482529220956160.png","name":"CYouth","gender":"0","is_ai":0}}}
# 最后一行也有换行符\nCompute the signature
sign = hmac_sha1(origin, secret)
# secret = abcdef
# sign = 24e819bb9a6bac0c02f141e0ff41ed1bf342db79Add the request header
Authorization: gfs app_id="123456",timestamp="1718777147021",nonce="wHwZk4veHDFSpiw5",signature="24e819bb9a6bac0c02f141e0ff41ed1bf342db79"Send the request
4.2.2 API Response Codes
| Response code | Description |
|---|---|
| 0 | Success |
| 100000 | Generic error |
| 100001 | code creation failed |
| 100002 | code verification failed |
| 100003 | code parsing failed |
| 100004 | code invalid |
| 100005 | code expired |
| 100006 | get_user_info request failed |
| 100007 | get_user_info data parsing failed |
| 100008 | get_user_info integration error; HTTP status code is not 200 |
| 100009 | http missing code parameter |
| 100010 | http missing appId parameter |
| 100101 | Login error |
| 100102 | Join error |
| 100103 | Cannot join a seat in an ongoing game |
| 100104 | Room is full |
| 100105 | Duplicate join |
| 100106 | Seat is occupied |
| 100107 | A bot (AI) cannot be the team leader |
| 100108 | Exit error |
| 100109 | Not on a game seat |
| 100110 | Cannot leave while not idle |
| 100111 | Ready error |
| 100112 | Cancel-ready error |
| 100113 | Start error |
| 100114 | Game already started |
| 100115 | Only the team leader can start the game |
| 100116 | Someone is not ready |
| 100117 | Not enough players to start the game |
| 100118 | Kick error |
| 100119 | Only the team leader can kick |
| 100120 | Cannot kick in an ongoing game |
| 100121 | Cannot kick yourself |
| 100122 | Change-leader error |
| 100123 | Escape error |
| 100124 | Game already ended when escaping |
| 100125 | Player already left the game when escaping |
| 100126 | Dissolve error |
| 100127 | Game already ended when dissolving |
| 100128 | Only the team leader can dissolve |
4.2.3 Game Server APIs
1. Common API Information
This section provides the common information for calling the game server APIs. Unless otherwise specified, all APIs must be called using the method described here and carry the data fields defined here.
- Common Request Definition
POST {{baseURL}}/{game}/app_event
Accept: application/json
Content-Type: application/json
{
"event": "user_enter",
"app_id": "app_id_1",
"room_id": "room_id_1",
"timestamp": "1657770493152",
"data": {}
}| Field | Location | Type | Description |
|---|---|---|---|
| game | URL | string | Game name |
| event | body | string | Event type |
| app_id | body | string | APP unique identifier |
| room_id | body | string | Room unique identifier |
| timestamp | body | string | Millisecond timestamp |
| data | body | object | Data required by the API; varies per API |
- Common Response Definition
| Field | Type | Description |
|---|---|---|
| ret_code | int | Response code |
| ret_msg | string | Response message |
| data | -- | -- |
2. Player Seat-Up API
This API is used to add a specified player to a game seat and requires the joining user's information.
- Request Definition
{
"event": "user_enter",
"app_id": "{{app_id}}",
"room_id": "{{room_id}}",
"timestamp": "{{timestamp}}",
"data": {
"user_info": {
"user_id": "",
"avatar": "",
"name": "",
"gender": "",
"extra": ""
}
}
}- `UserInfo` Definition
| Field | Type | Description |
|---|---|---|
| user_id | string | Unique user identifier |
| avatar | string | User avatar URL |
| name | string | User display name |
| gender | string | Gender |
| extras | JSON string | Extension field |
- `UserInfo.extras` Definition
| Field | Type | Description |
|---|---|---|
| skin_type | int | Skin type; valid range: 1-4 |
3. End Game or User Escape
This API is used to end a round of the game or to mark a user as having escaped.
- Request Definition
{
// 通用字段
"event": "game_end",
"app_id": "{{app_id}}",
"room_id": "{{room_id}}",
"timestamp": "{{timestamp}}",
"data": {
"user_id": ""
}
}| Field | Type | Description |
|---|---|---|
| user_id | string | User ID; included when a specific user is escaping. Defaults to empty, which means the game ends early |
4. Add Bot
This API is used to add a bot player to the room. A bot cannot act as the team leader, so a player must already be in the game before adding a bot.
- Request Definition
{
// 通用字段
"event": "ai_add",
"app_id": "{{app_id}}",
"room_id": "{{room_id}}",
"timestamp": "{{timestamp}}",
"data": {
"user_infos": [
{
"user_id": "",
"avatar": "",
"name": "",
"gender": "",
"extra": ""
}
],
"is_ready": true
}
}| Field | Type | Description |
|---|---|---|
| user_infos | userInfo[] | Information for one or more bot players to add |
| is_ready | boolean | Whether the bot players are in the ready state |
- Response Definition
When the request succeeds (i.e., `ret_code` is 0), the data has the following structure:
`data` Type Definition| Field | Type | Description |
|---|---|---|
| user_ids | string[] | Array of bot uids successfully added |
5. FAQ
The game uses safe-area configuration to avoid UI occlusion, so that space is reserved for the business-side client UI even when the game is full screen. Simply provide the safe-area parameters (top, bottom, left, right) in the game URL; the parameter values are the size of the area to reserve.