プロジェクト

全般

プロフィール

バグ #426

完了

IRに露出する totalDuration が実プレー時間でなく判定ズレ総和になっている

バグ #426: IRに露出する totalDuration が実プレー時間でなく判定ズレ総和になっている

エージェント ちゃん さんが10日前に追加. 10日前に更新.

ステータス:
完了
優先度:
通常
担当者:
対象バージョン:
開始日:
2026/06/26
期日:
進捗率:

100%

予定工数:

説明

背景

izu-ir #425「1曲しかプレーしてないのに本日のプレイ時間が『569時間3分』になる」の調査で、根本原因が beatoraja 側の totalDuration 計測にあると判明した。

#277IRScoreData に露出した totalDuration の仕様は「単一プレーの実経過時間(ミリ秒):完走なら曲丸々、途中終了ならそこまで、選曲画面等は除外」だが、実際にセットされている値はプレー時間ではない。

原因

bms.player.beatoraja.play.PlayScoreDataBuilder#setJudgeDuration が、avgjudge(平均判定ズレ)算出用の中間値 avgduration を、そのまま score.setTotalDuration(avgduration)流用している。

private static void setJudgeDuration(ScoreData score, BMSModel model) {
    long count = 0;
    long avgduration = 0;
    ...
        long time = n.getMicroPlayTime();
        avgduration += state >= 1 && state <= 4 ? Math.abs(time) : 1000000;
        count++;
    ...
    score.setTotalDuration(avgduration);     // ← バグ: 判定ズレ総和であって経過時間ではない
    score.setAvgjudge(avgduration / count);  // ← 本来の用途(平均判定ズレ)
}
  • avgduration は「全ノートの判定ズレ絶対値 Math.abs(n.getMicroPlayTime()) の総和(未判定ノートは 1,000,000 マイクロ秒のペナルティ)」で、avgjudge = avgduration / count(平均判定ズレ)を出すための分子。
  • n.getMicroPlayTime() の実体は JudgeManager#updateMicron.setMicroPlayTime(mfast)mfast = 判定の早遅ズレ・マイクロ秒・符号付き)としてセットされる値で、recentTimings.record(mfast) でも使われる判定精度の値。プレー経過時間ではない。
  • 結果、totalDuration はノート数に比例して肥大化し、単位もマイクロ秒。izu-ir 側はこれをミリ秒のプレー時間として解釈するため、約20億 → 569時間 に化ける。
    • 本番実測: chart_score_logs 1行で total_duration = 2,048,601,381(≒569時間)。

対応方針

setJudgeDuration 内で avgjudge 計算はそのまま維持し、totalDuration には「最後に判定されたノートの曲内時刻(ミリ秒)」を設定する。TimeLine#getMilliTime() がミリ秒の曲内時刻。

long playDurationMillis = 0;
...
    if (state >= 1 && state <= 4) {
        playDurationMillis = Math.max(playDurationMillis, tl.getMilliTime());
    }
...
score.setTotalDuration(playDurationMillis);
score.setAvgjudge(count == 0 ? 0 : avgduration / count);
  • 完走なら最終ノート ≒ 曲丸々、途中終了なら最後に叩いたノートまで、を満たし #277 の仕様に合致する。
  • メソッド名 setJudgeDuration の責務(avgjudge)に対する totalDuration 混入を解消する。
  • テスト追加: 判定済みノートの最大 milliTimetotalDuration に入ること、avgjudge が従来通りであること。

izu-ir 側

  • izu-ir 側のロジック変更は不要。修正版 beatoraja.jar 取り込み後、以降に送られる値が正しくなる。
  • #425 では本番の壊れた行(player_id='izu', total_duration=2048601381)を 0 に是正する。

関連

  • izu-ir #425(本件の発見元・569時間表示バグ)
  • #277(IRScoreData への totalDuration 露出)

関連するチケット 1 (0件未完了1件完了)

関連している beatoraja - バグ #427: ハード落ちなど途中終了した場合でも楽曲トータルのノーツ数がIRに送信されている完了いづ さん2026/06/26

操作

いづ さん さんが10日前に更新 操作 #2

  • 関連している バグ #427: ハード落ちなど途中終了した場合でも楽曲トータルのノーツ数がIRに送信されている を追加

エージェント ちゃん さんが10日前に更新 操作 #3

  • ステータス未着手 から 実装中 に変更
  • 進捗率0 から 10 に変更

実装に着手しました。

  • ブランチ: feature/426
  • 対応方針: PlayScoreDataBuilder#setJudgeDurationtotalDuration に判定ズレ総和(avgduration)を流用しているのを止め、判定済みノートの最大 TimeLine#getMilliTime()(ミリ秒の曲内時刻)を設定するよう修正。avgjudge の算出は従来どおり維持し、ゼロ除算ガードも追加。

エージェント ちゃん さんが10日前に更新 操作 #4

  • ステータス実装中 から 完了 に変更
  • 進捗率10 から 100 に変更

対応が完了しました。

概要

IR に露出する totalDuration が、実プレー時間ではなく判定ズレ総和(avgjudge 用の中間値)になっていたバグを修正。ノート数に比例して肥大化し、izu-ir 側で約569時間と表示される原因だった。

変更点

  • PlayScoreDataBuilder#setJudgeDurationtotalDurationavgduration(判定ズレ絶対値の総和・マイクロ秒)を流用していたのを廃止し、判定済みノート(state 1〜4)の TimeLine#getMilliTime()(ミリ秒の曲内時刻)の最大値を設定するよう修正(完走なら曲丈々、途中終了なら最後に叩いたノートまで、#277 の仕様に合致)。
  • avgjudge の算出は従来どおり維持し、count == 0 の除算ゼロガードを追加。
  • テストから呼べるようメソッドを private static → パッケージプライベート static に変更。
  • 変更ファイル: src/bms/player/beatoraja/play/PlayScoreDataBuilder.javasrc/test/java/bms/player/beatoraja/play/PlayScoreDataBuilderTest.java(新規・テスト3件)

テスト

  • .\gradlew.bat test : 成功(新規 PlayScoreDataBuilderTest 3件 + 回帰確認として play.* / ScoreDataTest / ir.* すべてグリーン)

関連 MR

  • !107 (feature/426 → develop)

他の形式にエクスポート: PDF Atom