RankHub
各游戏

API 目前处于 Early Preview 阶段, 可能存在诸多不完善与错误之处,欢迎加群交流反馈:加入官方交流群(1073434398)

Notanote

notanote 的 Nrk 评分体系、Best 31 桶规则,以及 profile / score 的统一字段与原生字段(native)。

notanote 的原生评分叫 Nrk:单曲值由准确率而非分数决定,总评是 Best 31 的加权平均。 公式由官方公布,但总评的分母存在口径分歧,本页把事实写清、不下结论。

机器可读版本

本页字段表与服务端 GET /v1/games/notanote/format 一一对应(profile.identityFields / profile.fields / profile.nativeFields、score.keyFields / score.compareField / score.ratingField / score.fields / score.nativeFields / score.buckets)。文档可能滞后,以端点返回为准。

Profile 格式

一张 notanote 档案 = 一个 RankHub 用户在 notanote 上的一张卡。对外主键是 profileId(uuid),不是 notanote 的 uid。

身份字段(建档用)

POST /v1/games/notanote/profiles 的 player 里必须带这个字段:

字段类型必填说明示例
notanoteUidstring是游戏侧账号 id。纯数字串(^\d{1,32}$)、且须是 > 0 的安全整数;对外按字符串透传,不做 Number 化"1008642"
  • 缺失/形状不对 ⇒ 400(文案见写入格式 §4);服务端只落库,不校验上游存在性;
  • player.name 也是必填(展示名),但它属于下面的统一字段,不是身份字段。

统一字段(player)

字段类型说明
namestring | null显示名,建档必填(可来自原生 PlayerName)
ratingnumber | null总评 Nrk,取档案汇总里服务端算好的值,四舍五入到 3 位小数

player.rating 是「算出来的」

notanote 的原生存档只有成绩,没有 Nrk 字段;player.rating 是服务端派生值,分母口径见评分语义与量纲。

原生字段(player.native)

挑选透传(不是整份原生对象),键名是 wire 上的实际键名:

字段类型说明
uidnumber | null玩家数字 id
avatarIdstring | null当前选中头像 id(原生 avatar_id)
candystring | null糖果。原生是字符串,存档里的 Candy 是数字,别混用
playedTimenumber | null累计游玩时长(summary.played_time);单位未确认

没透出的原生字段:avatar_url、types、is_banned。

Score 格式

成绩键与比较值

项值
成绩键 keyFieldssongId + difficulty(wire 顶层只有这两个)
完整写侧键还含 score.native.source(common / fanmade)与 score.native.color
比较字段 compareFieldscore(写侧比较列是服务端按判定重算的 calculated_score)
单曲评分字段 ratingFieldrating(原生列 nrk)
tie-break无(单个比较值)
  • 唯一键含来源与颜色:同一首歌同一难度,在 common(官方谱)/ fanmade(自制谱)之间, 或不同轨道颜色(Red/White/Blue/Green/Yellow/Black)之间,是不同的成绩, 不要只按 songId + difficulty 合并;
  • 这两个字段不在 wire 顶层,在 score.native 里;只按顶层字段拼 key 会把来源与颜色合并掉。

比较值不在响应里

compareField 写的是 score,而写侧实际比较的是存储列 calculated_score; wire 上的 score 投影的是上游 highest_score。判定分布不完美时两者并不相等 —— 要复算就按下面的公式算,或读 score.native 里的判定计数。

统一字段

字段类型说明
scoreIdstring成绩行 id(稳定排序 tie-break)
songIdstring曲目 id(可含空格/中文/下划线)
songNamestring | null曲名;曲库认不出时为 null
difficultystring | null难度:TL / SY / NT(没有数字下标,也没有 13+ 这类标签)
achievementsnumber | null准确率,量纲 0~1(不是 0~100)
scorenumber | null上游最高分,0 ~ 1,000,000
ratingnumber | null单曲 Nrk(原生列 nrk)
nativeobject | null原生字段,见下表

恒为 null(原生没有或无映射):type、levelIndex、level、rank、dxScore、dxStar、fc、fs、playTime。

谱面身份由 songId + difficulty 加上 score.native 里的 source / color 共同决定; 只按顶层两个字段做去重会把不同来源/颜色的成绩并成一条。

原生字段(score.native)

字段类型说明
chartKeystring谱面键:{songId}_{color}_{difficulty},如 The Fire_White_TL
colorstring | null轨道颜色(唯一键的一段)
sourcestring | nullcommon(官方谱)/ fanmade(自制谱)(唯一键的一段)
constantnumber | null谱面定数(来自曲库;认不出曲目时为 null)
noteCountnumber | null物量
xperfectCountnumber | nullXPerfect 判定数(官方分数/准确率公式里没有这一档)
perfectCount / goodCount / badCount / missCountnumber | null各判定数
combonumber | null本次最大连击

评分语义与量纲

单曲 Nrk(官方公布):

单曲 Rank =
  (e^(Acc − 0.5) − 1) / (e^0.5 − 1) × (定数 + 5)     , Acc ∈ [0.5, 1]
  0                                                   , Acc < 0.5
符号含义量纲
Acc本局准确率0~1
定数谱面 chart constant(曲库给出的字符串数字,如 "15.6")小数
结果单曲 Rank无上限概念;0 ~ 定数 + 5
  • Acc = 1 ⇒ 单曲 Rank = 定数 + 5(上限);Acc < 0.5 ⇒ 0;
  • 准确率决定 Nrk,分数不决定:Good 判定只按 40%~90% 计入 Acc,但按满值计入分数;
  • 同一张谱面反复提交时,只有 calculated_score 更高才会覆盖当前成绩。

分数(官方公布):分数 = 1,000,000 × (Perfect数 + Good数) / 物量 —— Bad / Miss 不扣分、只扣 Acc,所以「分数高」与「Nrk 高」是两件事。

总评(官方公布):

Nrk = Σ(Rᵢ × ωᵢ) / Σωᵢ        , i = 1..31
ωᵢ = 1 − 0.02 × (i − 1)
  • Rᵢ = 单曲 Rank 降序第 i 位;同一首歌多个难度只保留最高那一条;
  • 分母分歧(只记事实):官方写 Σωᵢ,31 条齐全时为 21.7;社区日文 wiki 写 /22, 本服务实现也硬编码 /22。满 31 条时官方口径高约 1.38%,不足 31 条时差距更大;
  • 精度:三个公式官方都没有舍入说明;本服务总评四舍五入到 3 位小数,单曲值原样保留浮点;
  • 桶内准入:只统计 source === 'common' 且曲库能认出的谱面。官方只写了「同曲多难度取最高」, 没有写排除自制谱 —— 这条差异会影响 Nrk 高低。
  • 同一首歌的多个难度:单曲 Rank 按难度各自计算,进总评时同曲只取最高的那一条(不重复计入)。

最佳成绩桶

GET /v1/games/notanote/profiles/{profileId}/bests 固定返回一个桶:

keylabel成员规则totalsKeys
best31Best 31按曲目去重(同曲多难度只留最高单曲 Nrk,只统计官方谱)→ 降序取前 31 → 按 ωᵢ = 1 − 0.02(i−1) 加权平均nrk、bestCount
  • totals.nrk:总评(四舍五入 3 位);totals.bestCount:前 31 里实际有几条(可能 < 31);
  • buckets[0].total:桶内各条 rating 的和,不是总评 Nrk(总评是加权平均)。

写入(第三方)

请求体形状、idempotencyKey、批量上限、428/403 等通用规则见写入格式; 这里只写 notanote 真正特殊的部分。

两种写形状

  • RankHub 格式:直接给统一字段(songId / difficulty / accuracy / rating …);
  • 游戏原生格式(native):player.native / scores[].native 里放原生形状,服务端按唯一一套换算归一, 与顶层同名字段同时出现时 native 优先;不合法 ⇒ 400 common.invalidArgument(message 指明键)。

本游戏的写入规则

  • 必给:songId + difficulty(缺失直接 400)+ accuracy + rating。 后两条缺失不报错,但会写出「准确率 0 / Nrk 0」的错误成绩。
  • accuracy 量纲是 0~1(写 >1 会被当成百分数自动除 100,最后夹到 0~1),写错量纲会直接算错 Nrk。
  • 比较值是 score,但写侧比较的是 calculated_score:推成绩时它由 accuracy × 1,000,000 得出, accuracy = 0 时才用你给的 score。所以想「变好」,本质是把 accuracy 抬高。
  • 谁算分:单曲 rating(Nrk)由调用方给(maimai / arcaea 之外的游戏都是调用方给,只有那两者服务端算); 总评 Nrk(Best 31)由服务端重算并回写档案。
  • 唯一键:来源 + songId + 颜色 + difficulty,见成绩键与比较值。
  • 可选字段:constant(曲库命中时以曲库为准)、color(缺省取曲库颜色,再退回 Unknown)、 chartKey、combo;playTime 不读取(原生没有时间字段)。
  • 409:身份已被别的 RankHub 用户绑定,或该用户已有另一张 notanote 档案(重复建档前请先查 GET .../profiles)。
  • 通用上界(写入守卫开启时生效,默认 off):score 越界 400,rating / constant / accuracy 越界或 playTime 晚于现在 1 小时 422。

原生数据(native)

建档:原生存档里只有玩家名 —— PlayerName → name,其余忽略。 ❗ 存档里没有 uid,notanoteUid 仍必须用顶层字段给。

成绩:原生成绩是 { "<songId>_<color>_<difficulty>": 原始值 } 的 map,单条推送要自带 key。

native 键类型换算
chartKeystring,必填形如 <songId>_<color>_<difficulty>(从右往左切三段)→ 谱面键 + songId / color / difficulty
HighestScore整数 0~1000000score
Combo整数 0~100000combo
sourcecommon / fanmade谱面来源(唯一键的一段)

可写字段(native,除身份与唯一键外全部):

位置可写键落库 / 读回
profile(建档或 PATCH /v1/games/notanote/profiles/{profileId} 的 native)PlayerName、avatar_id、candy、played_timename → player.name;其余读回 player.native.avatarId / candy / playedTime
score(scores[].native)HighestScore、Combo、source对应 score / combo / 来源

不可改:身份 notanoteUid(原生 uid,native 里没有这个键,PATCH 入参也没有身份字段 ⇒ 无入口); 成绩唯一键(chartKey 决定谱面)—— 顶层与 native 都给时必须一致,不一致以 native 为准,会写到另一张谱面上。

示例(示意数据) —— 一条推成绩请求(native 与顶层混用):

curl -sS -X POST https://api.rankhub.cc/v1/games/notanote/profiles/$PROFILE_ID/scores \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scores": [
      {
        "native": { "chartKey": "The Fire_White_TL", "HighestScore": 998000, "Combo": 842, "source": "common" },
        "accuracy": 0.9925,
        "rating": 18.34
      }
    ]
  }'

消费者注意事项

  1. achievements 是 0~1,不是 0~100、也不是 maimai 的 0~101。跨游戏渲染必须先按游戏分支。
  2. rating(单曲 Nrk)依赖曲库定数:曲库认不出该谱面时为 0;player.rating 是总评派生值。
  3. 比较值不回传:写侧比较 calculated_score,响应里的 score 是上游 highest_score,两者可能不同。
  4. 总评口径未统一:分母 21.7 与 22 的差异会让满 31 条时差约 1.38%,与其它站点对不上属预期。
  5. rank / fc / fs 恒为 null:官方有评级阈值表但未实现映射;连击要从 score.native.combo 与 noteCount 自己判断。
  6. playTime 恒为 null:原生没有游玩时间字段。
  7. playedTime 单位未确认:可能是秒也可能是分钟,展示前不要硬编码换算。

相关

本页内容

官方交流群二维码官方交流群问题反馈 · 版本资讯 · 成绩交流群号 1073434398