API 目前处于 Early Preview 阶段, 可能存在诸多不完善与错误之处,欢迎加群交流反馈:加入官方交流群(1073434398)
Phigros
phigros 的 Profile 与 Score 格式:objectId 身份、acc/rks 的量纲、score.native 只有 constant/artist、以及 phi + Best 30 双桶与「分母恒 30」的评分口径。
本页是 phigros 在 RankHub 上的字段级格式说明。phigros 有一条区别于其他游戏的硬约定:
rating(单曲 rks)由调用方给——服务端只做上界校验与「只升不降」合并,不代算。
从 phi-plugin 过来?
存档结构、单曲 rks、(φ3+best27)/30 与推送字段的对应关系见
如果你熟悉使用 phi-plugin 进行开发;本页只列字段定义。
机器可读版本
GET /v1/games/phigros/format 返回与本页一致的格式数据(同一份字段表)。
文档可能滞后,以端点返回为准。
acc 是 0\~100,不是 0\~1
phigros 的 achievements 装的是 acc(百分数 0~100);单曲 rks 只吃 acc、不吃 score。
把 acc 当 0~1 会让 rks 计算与展示全部错位。
Profile 格式
身份字段(建档用)
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
objectId | string | 是 | 玩家唯一 id(云存档账号维度) | "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d" |
shortId | string | 否 | 短 id(展示/检索用) | "P123456" |
- 建档走
POST /v1/games/phigros/profiles:只落库。 - 同一
objectId已有未解绑档案 ⇒409(… is already linked)。 - 字段式建档不写
session_token_ciphertext⇒syncCapable: false;只有用户在 RankHub 取值以字段为准。 name也是建档必填项,但它是统一字段(player.name)而不是身份键,所以不在上表;10 个游戏一致。
统一字段(player)
| 字段 | 类型 | 可空 | 单位 | 说明 |
|---|---|---|---|---|
name | string | 可空 | — | 显示名(优先 nickname,回落 username) |
rating | number | 可空 | rks(小数 2 位,展示四舍五入) | 取档案 summary.rks:导入路径下是游戏自己算的 rks;每次推成绩后会被服务端按 Best 30 口径重算的值覆盖 |
GET .../bests里的两个 totals 键都源自summary.rks:totals.officialRks读的是存下来的那一份,totals.rks是每次请求现算的(calculated rks)。所以「official」指的是「已落库的值」, 不是「永远等于游戏内显示」——导入时两者可能不等,推过成绩后通常收敛。
原生字段(player.native)
| 原生字段(wire 键) | 类型 | 单位 / 取值 | 语义 | 可空 |
|---|---|---|---|---|
objectId | string | — | 玩家唯一 id(原生 object_id) | 可空 |
shortId | string | — | 短 id(原生 short_id) | 可空 |
challengeModeRank | number | 整数 | 课题模式(Challenge Mode)RANK | 可空 |
gameVersion | string | — | 快照时的游戏版本号 | 可空 |
档案里还有 avatar / avatarName / backgroundSongId 等展示字段,不在 player.native 里;
summary 里的 levels(各难度通关/FC/ϕ 计数)、save_version、level_records 也不透传到 wire。
Score 格式
成绩键与比较值
| 项 | 值 | 说明 |
|---|---|---|
keyFields | songId / difficulty | 一条成绩 = 曲目 + 难度;difficulty 是 EZ / HD / IN / AT / Legacy 字符串 |
compareField | score | 「只升不降」按得分比较(0~1,000,000) |
ratingField | rating | 单曲评分字段是统一 rating(原生字段 rks)。值由调用方给,服务端不代算 |
difficulty是 5 值枚举,不是数字;推成绩时写"IN"这类字符串,或写数字下标 (EZ=0、HD=1、IN=2、AT=3、Legacy=4),认不出来直接400。- 比较值用
score(不是acc):同样acc可以对应不同score(连击分不同)。
本页
keyFields/compareField/ratingField里的名字都是 v1 wire 字段名(GameScoreDto的顶层字段);游戏原生列名写在说明列。
统一字段
phigros 会填的统一字段:
| 字段 | 类型 | 单位 / 量纲 | 可空 | 说明 |
|---|---|---|---|---|
scoreId | string | — | 否 | 成绩行 id |
songId | string | — | 否 | 曲目 id(存档 rawId 归一化 .0 后缀后的 id) |
songName | string | — | 可空 | 曲名(来自曲库) |
difficulty | string | EZ / HD / IN / AT / Legacy | 可空 | 难度(键之一) |
levelIndex | number | 0~4 | 可空 | 难度的数字形式(与 difficulty 同源) |
achievements | number | 百分数 0~100 | 可空 | 准确率 acc(不是 0~1) |
score | number | 0~1,000,000 | 可空 | 得分(比较值) |
rating | number | 小数,单曲 rks | 可空 | 单曲 rks(调用方给 / 本地算) |
rank | string | ϕ / V / S / A / B / C / F | 可空 | 评级(原生字段名就叫 rating) |
fc | string | 只有 "fc" 一个取值 | 可空 | 全连标记:fc 为真时给 "fc",否则 null |
playTime | string | — | 恒 null | 存档不带游玩时间 |
恒为 null 的字段:type(非 maimai)、level、fs、dxScore / dxStar。
原生字段(score.native)
phigros 的 score.native 只有两个键(其余原生字段都进了统一字段):
| 原生字段(wire 键) | 类型 | 单位 / 取值 | 语义 | 可空 |
|---|---|---|---|---|
constant | number | 1.0~16.x | 谱面定数(来自曲库,不在存档里);单曲 rks 的乘数 | 可空 |
artist | string | — | 曲师 | 可空 |
原生成绩里还有 rks、acc、fc、rating(评级字母)、chart.level(定数)等,
它们分别落到 rating / achievements / fc / rank / native.constant;song_name 落到 songName。
评分语义与量纲
单曲 rks 由「acc + 谱面定数」唯一决定:
single_rks =
acc < 70 -> 0
acc >= 70 -> ((acc - 55) / 45) ^ 2 × constant| 量 | 量纲 | 说明 |
|---|---|---|
acc | 百分数 0~100(2 位小数) | acc = 100 ⇒ single_rks = constant;acc = 70 ⇒ 约 0.111 × constant |
constant | 1.0~16.x | 谱面定数(游戏内谱面卡片直接显示的那个一位小数) |
single_rks | 小数 | 单曲值;SP / Legacy 谱面历史上统一按 0 处理(是否仍适用未见现行官方说明) |
player.rating(官方 rks) | 小数,展示 2 位(四舍五入) | (P1 + P2 + P3 + ΣB27) / 30:ϕ 曲中定数最高的 3 首 + 单曲 rks 前 27,分母恒 30 |
P1~P3与B27可以重叠(同一首既评 ϕ 又进前 27 会被算两次),所以「分母 30」不等于 「30 首不同曲子」;没有 ϕ 成绩时前三项记 0,仍除以 30。- 总分口径按 v3.11.0 起的 30 项平均(ϕ 曲定数前 3 + 单曲 rks 前 27);旧档案无法从字段上区分分母 20 的旧口径。
- 官方从未发布 rks 白皮书:上表是官方版本说明 + 社区复算的口径(证据等级
community)。
最佳成绩桶
GET /v1/games/phigros/profiles/{profileId}/bests 返回两个桶(可能重叠):
| key | label | 成员规则 | totals 键 |
|---|---|---|---|
phi | ϕ | 全部成绩中评级为 ϕ 的,按单曲 rks 降序取前 3 条(最多 3 条) | rks / officialRks |
best30 | Best 30 | 全部成绩按单曲 rks 降序取前 27 条(桶名叫 best30,实际只有 27 条) | 同上 |
totals.rks= 服务端按上面的 30 项平均重算的 rks(round3)。totals.officialRks= 档案里游戏自己算的 rks(player.rating同源)。buckets[].total= 桶内rating之和,不是 rks。- 两个桶可以重叠:同一首既是 ϕ 又在前 27 时会同时出现在两个桶里(这正是官方口径)。
写入(第三方)
写入只有两种形状:RankHub 格式(上文两张统一字段表里的字段)与游戏原生格式(native)。
共享字段、批量上限与失败语义见写入格式;本节只列 phigros 真正会读的字段。
建档(POST /v1/games/phigros/profiles)
player 用上文的身份字段(objectId 必填,shortId 可选)+ name。可以同时带 scores[],
返回的 player.rating 以落库后的汇总(rks)为准。身份重复 ⇒ 409。
推成绩(POST .../profiles/{profileId}/scores)
| 字段 | 类型 | 必给 | 说明 |
|---|---|---|---|
songId | string | 支持 | 曲目 id(曲名式字符串,如 "Introduction") |
difficulty | string | integer | 支持 | EZ / HD / IN / AT / Legacy(大小写不敏感),或下标 0~4;非法 ⇒ 400 |
score | number | 支持 | 0~1,000,000;比较值,缺省 0 |
accuracy | number | 支持 | 百分数 0~100(缺省回落 achievements,再缺省 0) |
rating | number | 支持 | 单曲 rks,调用方给;缺省落 0,服务端不复算(phigros 不属于服务端算分族) |
constant | number | 可选 | 谱面定数,0~100,缺省 0 |
artist | string | 可选 | 曲师(展示用) |
rank | string | 可选 | 评级字母 ϕ / V / S / A / B / C / F |
fullCombo | boolean | 可选 | 全连标记;也可改用 fc(存在性即全连) |
curl -sS -X POST https://api.rankhub.cc/v1/games/phigros/profiles/$PROFILE_ID/scores \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"scores": [
{
"songId": "Introduction",
"difficulty": "IN",
"score": 1000000,
"accuracy": 100,
"rating": 15.3,
"constant": 15.3,
"artist": "Sample Artist",
"rank": "ϕ",
"fullCombo": true
}
]
}'以上为示意数据:字段真实,数值虚构。
原生数据(native)
player.native / scores[].native 是游戏原生格式:与顶层同名字段同时出现时 native 优先(浅合并)。
只给 native 时,顶层的 name / objectId 可以省略。
建档(player.native)
native 键 | 类型 | 必填 | 换算 |
|---|---|---|---|
object_id | string(非空) | 支持 | → objectId |
nickname / username | string | 二者至少一个 | → name(优先 nickname,再 username) |
short_id | string | 可选 | → shortId |
推成绩(scores[].native)
native 键 | 类型 | 必填 | 换算 |
|---|---|---|---|
song_id | string(非空) | 支持 | → songId |
difficulty | string:EZ / HD / IN / AT / Legacy | 支持 | → difficulty(大小写敏感,按原值比对) |
score | integer 0~1,000,000 | 支持 | → score(比较值) |
acc | number 0~100(百分数) | 支持 | → accuracy 和 achievements |
constant | number 0~100 | 可选 | → constant |
rks | number 0~100 | 可选 | → rating(不给不代算:继续用顶层 rating) |
fc | boolean(也收 1 / "1" / "true") | 可选 | true ⇒ fullCombo + fc: "fc" |
rating | string:ϕ / V / S / A / B / C / F | 可选 | → rank(评级字母,不是分数) |
artist | string | 可选 | → artist |
可写字段 / 不可改
| 位置 | 可写 |
|---|---|
player.native(profile) | name、challenge_mode_rank、game_version(PATCH .../profiles/{profileId} 的 native 同样生效) |
scores[].native(score,除唯一键) | score、acc、constant、rks、fc、rating(→ rank)、artist |
- 不可改:身份字段
object_id/short_id(与现值不同 ⇒400 common.invalidArgument); 成绩唯一键song_id+difficulty(native里必须给,换了就是另一条成绩)。
本游戏的写入规则
- 必给:
songId+difficulty+score+accuracy+rating;只有difficulty缺失 / 非法会直接400。 - 比较值是
score:新值不高于现值 ⇒ 不覆盖,回improved: false+ignored(notImproved)。 - 谁算分:单曲 rks 由调用方给(
rating,服务端不复算、不与acc × constant对账);总分由服务端重算。 difficulty是 5 值枚举,accuracy是百分数0~100(不是 0~1)—— 量纲写错会直接算错 rks。
消费者注意事项
achievements是acc,量纲0~100(百分数)。它不是 0~1,也不是评级。rank装的是评级字母(ϕ/V/S/A/B/C/F),不是分数档位字符串; 最低档的实现值是F(社区文本常写False),跨源比对时要归一。ϕ的码位不统一:实现里用ϕ(U+03D5),社区文本常用φ(U+03C6)/Φ(U+03A6)。 严格字符串比较会漏掉全部 ϕ,导致「前三」恒为 0。rating由调用方给:推成绩时scores[].rating会原样入库并参与总分, 服务端不复算、也不与acc × constant对账。错的值会一路算进totals.rks。phi桶的成员是按单曲 rks 排序取的,而官方口径是按定数取前三;定数高但acc略低的 ϕ 曲可能落在不同位置。best30只有 27 条:不要按 30 硬编码长度;phi最多 3 条。- 两个 rks 并存:
player.rating与totals.officialRks读同一份summary.rks(导入时是游戏算的,推成绩后被服务端重算值覆盖),totals.rks是请求时现算的。 两者不等是正常现象,展示时选一个并标注用的是哪一个。 playTime恒null:phigros 的存档不带游玩时间。