本 API 为一套极简 HTTP REST 接口,用于多名玩家通过脚本控制贪吃蛇进行在线对战。
- 通信协议:HTTP / JSON
- 接口风格:REST
- 返回格式统一为
{ code, msg, data } - API 版本:v1.0.0
Base URL: http://your-server-address:port
请求头要求:
Content-Type: application/json
认证方式:
- 登录后获取
key(账号级别凭证) - 加入游戏后获取
token(游戏会话凭证) - 在请求体中携带对应凭证即可,无需复杂的 Header 认证
- 玩家脚本向服务器注册账号并获取身份凭证
- 玩家加入当前对局
- 周期性获取地图状态
- 计算并提交移动方向
- 服务器按回合推进游戏逻辑
- 重复步骤 3~5
- 地图大小固定,为
width × height的矩形区域 - 坐标系:左上角为
(0, 0),向右为 x 轴正方向,向下为 y 轴正方向 - 有效坐标范围:
0 <= x < width,0 <= y < height
- 玩家初始长度为 3,初始位置随机,保证头部周围一定范围内安全,初始时有一定回合无敌状态,不会被淘汰也不会淘汰其他玩家
- 蛇由头部
head和若干身体块blocks组成 blocks[0]即为头部位置
- 每回合玩家可选择一个移动方向:
UP/DOWN/LEFT/RIGHT - 若本回合未发送指令,则沿用上一回合方向
- 不允许反向移动(例如当前向右移动时,不能直接选择向左)
玩家发生以下情况将被淘汰,需要重新加入:
- 撞墙(头部移出地图边界)
- 撞到自身身体
- 撞到其他玩家身体
- 每回合按一定密度生成食物
food - 头部进入食物位置时,长度 +1,食物消失
- 蛇的长度会影响得分
- 单回合时长为
round_time毫秒(默认 1000ms) - 服务器按固定时间推进回合,所有玩家同步更新
为保证服务器稳定运行和游戏公平性,API 实施以下速率限制:
| 端点 | 限制 | 说明 |
|---|---|---|
/api/game/login |
10 次 / 小时 / IP | |
/api/game/join |
5 次 / 分钟 / key | |
/api/game/move |
每回合 1 次 / token | 每回合只能提交一次移动指令 |
/api/game/map |
无限制 | 无需token,公开访问,建议按回合轮询 |
/api/game/map/delta |
无限制 | 推荐,无需token,获取增量地图,流量节省80%+ |
/api/status |
60 次 / 分钟 / IP | 服务器状态查询 |
/api/metrics |
无限制 | 性能监控指标导出 |
{
"code": 429,
"msg": "too many requests, please retry after 10 seconds",
"data": {
"retry_after": 10
}
}- 移动指令:每回合只发送一次,建议在获取地图状态后立即计算并发送
- 地图轮询:按
round_time间隔轮询,不要过于频繁 - 错误重试:遇到 429 错误时,按
retry_after等待后重试
{
"code": 0,
"msg": "success",
"data": {}
}| code | 含义 |
|---|---|
| 0 | 成功 |
| 400 | 请求参数错误 |
| 401 | 未认证 / key 或 token 无效 |
| 403 | 无权限操作 |
| 404 | 资源不存在 |
| 409 | 状态冲突(重复加入、非法操作等) |
| 429 | 请求过于频繁 |
| 500 | 服务器内部错误 |
| 503 | 服务器维护中 |
异常响应示例:
{
"code": 401,
"msg": "invalid token",
"data": null
}GET /api/status
请求参数: 无
说明: 获取服务器运行状态和游戏基础配置,无需认证。
{
"code": 0,
"msg": "success",
"data": {
"status": "running",
"player_count": 10,
"map_size": {
"width": 50,
"height": 50
},
"round_time": 1000
}
}| code | msg |
|---|---|
| 503 | server maintenance |
POST /api/game/login
说明: 通过洛谷 UID 和剪贴板后缀验证身份,获取账号级别的 key。
请求体
{
"uid": "123456",
"paste": "luogu剪贴板后缀"
}参数说明:
uid(string, 必需): 洛谷用户 IDpaste(string, 必需): 洛谷剪贴板后缀,用于身份验证
成功响应
{
"code": 0,
"msg": "success",
"data": {
"key": "玩家唯一身份令牌"
}
}可能异常
| code | msg |
|---|---|
| 400 | invalid uid or paste |
| 403 | authentication failed |
| 500 | internal error |
POST /api/game/join
说明: 使用 key 加入当前游戏对局,获取游戏会话 token。
请求体
{
"key": "玩家唯一身份令牌",
"name": "玩家自定义名称",
"color": "#FF0000"
}参数说明:
key(string, 必需): 登录后获得的身份令牌name(string, 必需): 玩家显示名称,长度 1-20 字符color(string, 可选): 蛇的颜色,十六进制格式,默认随机分配
成功响应
{
"code": 0,
"msg": "success",
"data": {
"token": "游戏唯一标识符",
"id": "玩家在本局中的唯一ID",
"initial_direction": "right",
"map_state": {}
}
}字段说明:
token: 游戏会话令牌,用于后续API调用id: 玩家在本局中的唯一IDinitial_direction: 玩家蛇的初始移动方向(UP/DOWN/LEFT/RIGHT)map_state: 完整的初始地图状态
可能异常
| code | msg |
|---|---|
| 401 | invalid key |
| 409 | player already in game |
| 503 | server maintenance |
{
"players": [
{
"name": "nalong",
"id": "114514",
"color": "#FF0000",
"head": { "x": 10, "y": 15 },
"blocks": [
{ "x": 10, "y": 15 },
{ "x": 10, "y": 16 },
{ "x": 10, "y": 17 }
],
"length": 3,
"invincible_rounds": 2 //剩余无敌回合数
}
],
"foods": [
{ "x": 5, "y": 5 },
{ "x": 20, "y": 25 }
],
"round": 42,
"timestamp": 1706342400000,
"next_round_timestamp": 1706342401000
}字段说明:
players: 所有在线玩家列表name: 玩家名称id: 玩家 IDcolor: 蛇的颜色head: 蛇头坐标blocks: 蛇身所有块的坐标数组,blocks[0]为头部length: 蛇的当前长度
foods: 当前地图上所有食物的坐标round: 当前回合数timestamp: 服务器时间戳(毫秒)next_round_timestamp: 下一回合开始的时间戳(毫秒),客户端可据此计算下一回合到来的时间
GET /api/game/map
说明: 获取当前游戏地图的完整状态。每次都返回所有玩家和食物的完整信息。无需token验证,公开访问。
请求参数: 无
示例: GET /api/game/map
响应
{
"code": 0,
"msg": "success",
"data": {
"map_state": {
"players": [...],
"foods": [...],
"round": 42,
"timestamp": 1706342400000,
"next_round_timestamp": 1706342401000
}
}
}可能异常
| code | msg |
|---|---|
| 500 | internal error |
GET /api/game/map/delta
说明: 获取当前回合相对于上一回合的增量变化。
请求参数: 无
示例: GET /api/game/map/delta
增量数据结构说明:
增量数据只包含变化的部分,客户端需要自行维护完整状态:
- 所有玩家的简化信息 - 每回合都变化,但只发送关键字段(不含完整blocks数组)
- 新加入的玩家 - 包含完整信息,客户端需要添加到本地玩家列表
- 死亡的玩家ID - 客户端需要从本地玩家列表中移除
- 新增的食物 - 客户端需要添加到本地食物列表
- 移除的食物 - 客户端需要从本地食物列表中移除
响应格式
{
"code": 0,
"msg": "success",
"data": {
"delta_state": {
"round": 42,
"timestamp": 1706342400000,
"next_round_timestamp": 1706342401000,
"players": [
{
"id": "player1",
"head": { "x": 10, "y": 15 },
"direction": "UP",
"length": 5,
"invincible_rounds": 0
}
],
"joined_players": [
{
"id": "player2",
"name": "新玩家",
"color": "#FF0000",
"head": { "x": 20, "y": 20 },
"blocks": [
{ "x": 20, "y": 20 },
{ "x": 20, "y": 21 },
{ "x": 20, "y": 22 }
],
"length": 3,
"invincible_rounds": 5
}
],
"died_players": ["player3"],
"added_foods": [
{ "x": 5, "y": 5 },
{ "x": 8, "y": 12 }
],
"removed_foods": [
{ "x": 10, "y": 10 }
]
}
}
}字段说明:
round: 当前回合数timestamp: 服务器时间戳(毫秒)next_round_timestamp: 下一回合开始的时间戳(毫秒)players: 所有在线玩家的简化信息数组id: 玩家IDhead: 蛇头当前坐标direction: 当前移动方向 (UP/down/left/right)length: 蛇的当前长度invincible_rounds: 剩余无敌回合数
joined_players: 本回合新加入的玩家完整信息数组- 包含完整的玩家信息(name, color, blocks等)
- 客户端需要将这些玩家添加到本地状态
died_players: 本回合死亡的玩家ID数组- 客户端需要从本地玩家列表中移除这些玩家
added_foods: 本回合新增的食物坐标数组- 客户端需要将这些食物添加到本地食物列表
removed_foods: 本回合移除的食物坐标数组- 客户端需要从本地食物列表中移除这些食物
客户端状态维护逻辑:
# 伪代码示例
def update_with_delta(local_state, delta):
# 1. 更新回合数和时间戳
local_state.round = delta.round
local_state.timestamp = delta.timestamp
# 2. 处理死亡玩家
for player_id in delta.died_players:
local_state.players.remove(player_id)
# 3. 添加新加入的玩家
for player in delta.joined_players:
local_state.players[player.id] = player
# 4. 更新所有玩家的简化信息
for player_update in delta.players:
if player_update.id in local_state.players:
local_state.players[player_update.id].head = player_update.head
local_state.players[player_update.id].direction = player_update.direction
local_state.players[player_update.id].length = player_update.length
local_state.players[player_update.id].invincible_rounds = player_update.invincible_rounds
# 5. 移除食物
for food_pos in delta.removed_foods:
local_state.foods.remove(food_pos)
# 6. 添加食物
for food_pos in delta.added_foods:
local_state.foods.add(food_pos)使用建议:
- 首次拉取:使用
/api/game/map获取完整地图状态 - 后续轮询:使用
/api/game/map/delta获取增量更新 - 定期刷新:每隔一定回合(如50回合)重新获取完整地图,避免累积误差
- 异常恢复:如果增量更新出现问题,重新获取完整地图
可能异常
| code | msg |
|---|---|
| 500 | internal error |
POST /api/game/move
说明: 提交本回合的移动方向指令。每回合只能提交一次。
请求体
{
"token": "游戏唯一标识符",
"direction": "up"
}参数说明:
token(string, 必需): 加入游戏后获得的会话令牌direction(string, 必需): 移动方向,可选值:up/down/left/right
成功响应
{
"code": 0,
"msg": "success"
}可能异常
| code | msg |
|---|---|
| 400 | invalid direction |
| 401 | invalid token |
| 409 | move not allowed in current round |
| 429 | too many requests |
| 404 | player not in game |
注意: 玩家被淘汰后会返回 404 错误,需要重新调用 /api/game/join 加入游戏。
GET /api/leaderboard
说明: 获取排行榜数据,支持分页与时间范围过滤。
请求参数 (Query):
type(string, 可选): 排行榜类型,kills/max_length,默认killslimit(int, 可选): 每页数量,默认 50,受服务端max_entries限制offset(int, 可选): 偏移量,默认 0start_time(int64, 可选): 起始时间戳(毫秒)end_time(int64, 可选): 结束时间戳(毫秒)
成功响应
{
"code": 0,
"msg": "success",
"data": {
"type": "kills",
"limit": 50,
"offset": 0,
"start_time": 0,
"end_time": 0,
"refresh_interval_rounds": 5,
"cache_ttl_seconds": 5,
"entries": [
{
"uid": "123456",
"name": "player1",
"season_id": "all_time",
"now_length": 6,
"max_length": 8,
"kills": 3,
"deaths": 1,
"games_played": 5,
"total_food": 25,
"last_round": 180,
"timestamp": 1706342400000,
"rank": 1
}
]
}
}可能异常
| code | msg |
|---|---|
| 400 | invalid parameter |
| 500 | internal error |
GET /api/metrics
说明: 获取服务器性能指标,支持 JSON 和 Prometheus 格式。
请求参数 (Query):
format(string, 可选):json/prometheus,默认json
示例:
GET /api/metricsGET /api/metrics?format=prometheus
JSON 响应
{
"code": 0,
"msg": "success",
"data": {
"metrics": {
"enabled": true,
"timestamp_ms": 1706342400000,
"config": {
"window_seconds": 60,
"sample_rate": 0.2,
"max_samples": 2000
},
"qps": {
"overall": 12.5,
"per_endpoint": {
"status": 1.2,
"map": 6.3
}
},
"requests_total": 10240,
"requests_total_per_endpoint": {
"status": 120,
"map": 640
},
"latency_ms": {
"overall": {
"p95": 8.2,
"p99": 15.6,
"sample_count": 500
},
"per_endpoint": {
"map": {
"p95": 6.9,
"p99": 12.3,
"sample_count": 200
}
}
},
"round_ms": {
"last": 7.4,
"p95": 9.1,
"p99": 12.8,
"sample_count": 120
},
"locks": {
"GameManager.state": {
"count": 1200,
"avg_ms": 0.12,
"max_ms": 2.3,
"last_ms": 0.05
}
},
"gauges": {
"moves_current_size": 4,
"moves_pending_size": 8
},
"memory": {
"rss_bytes": 73400320
}
}
}
}Prometheus 响应
# HELP snake_qps Overall QPS in the configured window
# TYPE snake_qps gauge
snake_qps 12.5
# HELP snake_requests_total Total HTTP requests
# TYPE snake_requests_total counter
snake_requests_total 10240
可能异常
| code | msg |
|---|---|
| 503 | metrics disabled |