跳到主要内容

RhythMC-Reborn 数据包格式文档

目录

  1. 数据包概述
  2. HTTP 数据包
  3. WebSocket 数据包
  4. 基础数据结构
  5. 序列化规范

数据包概述

数据包分类

设计原则

原则描述
类型安全使用 sealed interface 限制实现类型
不可变使用 record 确保数据不可变
自描述包含 type 字段用于类型识别
JSON 友好字段命名使用下划线风格

HTTP 数据包

HttpPayload 接口

public sealed interface HttpPayload
permits GameplayRecord, StatusRecord {
String type();
}

GameplayRecord (游戏记录)

用途: 记录玩家单局游戏的详细数据

结构图:

JSON 示例:

{
"type": "gameplay",
"levelID": 1,
"acc": 98,
"isRival": true,
"isWin": true,
"rivalUID": 67890,
"rivalAcc": 95,
"tapStats": {
"perfect": 150,
"great": 12,
"miss": 3
},
"holdStats": {
"perfect": 80,
"great": 5,
"miss": 0
},
"dodgeStats": {
"perfect": 40,
"great": 2,
"miss": 1
},
"lookStats": {
"perfect": 30,
"great": 1,
"miss": 0
},
"maxCombo": 245,
"pass": true,
"fullCombo": false,
"allPerfect": false,
"isNewRecord": true,
"player_arena": "arena_01",
"time": 1700000000000
}

字段说明表:

字段类型范围描述
typeString"gameplay"类型标识
levelIDint> 0关卡唯一标识
accint0-100准确率百分比
isRivalboolean-是否为对战模式
isWinboolean-对战模式是否胜利
rivalUIDint> 0对手玩家UID
rivalAccint0-100对手准确率
tapStatsStatsRecord-TAP 音符判定统计
holdStatsStatsRecord-HOLD 音符判定统计
dodgeStatsStatsRecord-DODGE 音符判定统计
lookStatsStatsRecord-LOOK 音符判定统计
maxComboint>= 0本局最大连击数
passboolean-是否通关
fullComboboolean-是否全连(无MISS)
allPerfectboolean-是否全PERFECT
isNewRecordboolean-是否刷新个人纪录
player_arenaString-游戏场地标识
timelong-Unix时间戳(毫秒)

StatusRecord (状态记录)

用途: 记录服务器当前在线玩家数

结构图:

JSON 示例:

{
"type": "status",
"playerCount": 42
}

BaseHttpRecord (HTTP记录容器)

用途: 包装 HTTP 数据包,添加玩家和服务器信息

结构图:

JSON 示例:

{
"player": {
"playerUID": 12345,
"accessToken": "player-access-token",
"timestamp": 1700000000000
},
"server": {
"serverID": 100,
"serverName": "CN-East-01",
"country": "CN"
},
"payload": {
"type": "gameplay",
"levelID": 1,
"acc": 98
}
}

WebSocket 数据包

WsPayload 接口

public sealed interface WsPayload
permits RivalDisconnect, SyncRival, Ping, Pong {
String type();
}

类型层次

Ping (心跳请求)

方向: Server → Client

用途: 检测客户端连接状态

{
"type": "ping"
}

Pong (心跳响应)

方向: Client → Server

用途: 响应服务器心跳请求

{
"type": "pong"
}

SyncRival (对手数据同步)

方向: 双向

用途: 实时同步对手游戏数据

结构图:

JSON 示例:

{
"type": "sync_rival",
"playerId": 12345,
"data": {
"score": 98500,
"combo": 127,
"accuracy": 98.5,
"health": 85
}
}

RivalDisconnect (对手断开)

方向: Server → Client

用途: 通知客户端对手已断开连接

结构图:

JSON 示例:

{
"type": "rival_disconnect",
"playerId": 12345
}

基础数据结构

完整类图

BaseInfo (玩家基础信息)

public record BaseInfo(
int playerUID, // 玩家唯一ID
String accessToken, // 玩家短期 access token
long timestamp // 时间戳
) {
public static BaseInfo build(RhyPlayer player) {
return new BaseInfo(
player.getUid(),
player.getSessionToken(),
System.currentTimeMillis()
);
}
}

JSON 示例:

{
"playerUID": 12345,
"accessToken": "player-access-token",
"timestamp": 1700000000000
}

ServerInfo (服务器信息)

public record ServerInfo(
int serverID, // 服务器ID
String serverName, // 服务器名称
String country // 所在国家/地区
) {
public static ServerInfo build(int serverID, String serverName, String country) {
return new ServerInfo(serverID, serverName, country);
}
}

JSON 示例:

{
"serverID": 100,
"serverName": "CN-East-01",
"country": "CN"
}

StatsRecord (判定统计)

用途: 统计各类型音符的判定结果

public record StatsRecord(
int perfect, // PERFECT 判定数
int great, // GREAT 判定数
int miss // MISS 判定数
) {}

JSON 示例:

{
"perfect": 150,
"great": 12,
"miss": 3
}

DownloadInfo (下载信息)

用途: 资源下载任务信息

public record DownloadInfo(
String path, // 文件相对路径
String url, // 下载URL
long bytes, // 文件大小(字节)
String sha1 // SHA1 校验值
) {}

JSON 示例:

{
"path": "songs/song1.rsm",
"url": "https://cdn.example.com/songs/song1.rsm",
"bytes": 1048576,
"sha1": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
}

序列化规范

JSON 序列化配置

使用 Alibaba FastJSON2 进行序列化:

// 序列化为 JSON 字符串
String json = JSON.toJSONString(object);

// 从 JSON 字符串解析
T object = JSON.parseObject(json, T.class);

类型识别机制

类型路由代码

// WebSocket 消息反序列化
public static WsPayload fromWsJson(String json) {
JSONObject obj = JSON.parseObject(json);
String type = obj.getString("type");

return switch (type) {
case "ping" -> JSON.parseObject(json, Ping.class);
case "pong" -> JSON.parseObject(json, Pong.class);
case "sync_rival" -> JSON.parseObject(json, SyncRival.class);
case "rival_disconnect" -> JSON.parseObject(json, RivalDisconnect.class);
default -> throw new IllegalArgumentException("Unknown type: " + type);
};
}

// HTTP 数据包反序列化
public static HttpPayload fromHttpPayloadJson(String json) {
JSONObject obj = JSON.parseObject(json);
String type = obj.getString("type");

return switch (type) {
case "gameplay" -> JSON.parseObject(json, GameplayRecord.class);
case "status" -> JSON.parseObject(json, StatusRecord.class);
default -> throw new IllegalArgumentException("Unknown type: " + type);
};
}

泛型反序列化

// 使用 TypeReference 处理泛型
public static BaseHttpRecord<?> fromBaseHttpRecordJson(String json) {
JSONObject obj = JSON.parseObject(json);
JSONObject payload = obj.getJSONObject("payload");
String type = payload.getString("type");

return switch (type) {
case "gameplay" -> JSON.parseObject(json,
new TypeReference<BaseHttpRecord<GameplayRecord>>() {});
case "status" -> JSON.parseObject(json,
new TypeReference<BaseHttpRecord<StatusRecord>>() {});
default -> throw new IllegalArgumentException("Unknown type: " + type);
};
}

数据包类型速查表

HTTP 数据包

类型标识类名方向用途
gameplayGameplayRecordC → S上传游戏记录
statusStatusRecordC → S上传服务器状态

WebSocket 数据包

类型标识类名方向用途
pingPingS → C心跳请求
pongPongC → S心跳响应
sync_rivalSyncRival双向对手数据同步
rival_disconnectRivalDisconnectS → C对手断开通知

数据结构

名称用途
BaseInfo玩家基础信息
ServerInfo服务器信息
StatsRecord判定统计
DownloadInfo下载信息
BaseHttpRecordHTTP 数据容器