機能 #181
完了機能 #162: OBS連携機能
【OBS連携】Beatoraja本体側の機能
説明
親: #162
担当範囲¶
OBS 連携のうち beatoraja 本体側。状態の収集・保持と、ローカル HTTP サーバによる配信を担う。
設計¶
コンポーネント¶
-
ObsState — スレッドセーフな現在状態の保持。更新時に不変スナップショット(または完成済み JSON 文字列)を生成し
volatile参照に差し替える方式で、ゲームスレッド/HTTP スレッド間のロックを不要にする。 -
SessionStats — セッション統計。
MainController既存のboottimeを起点に起動時初期化し、リザルト到達時に加算。 -
ObsHttpServer —
com.sun.net.httpserver.HttpServerを利用。スレッドは既存RuntimeTaskManager.startVirtual(...)(StreamControllerと同じ流儀)で回す。
接続点¶
-
MainController.changeState(MainStateType)(MainController.java:248) を唯一の更新funnelとして利用。全画面遷移がここを通るため、状態クラスを侵食せず最小改変で phase(select / playing / result)と曲情報・スコアをObsStateに反映できる。 - 曲情報/スコアの取得元は
PlayerResource(model / songdata / score を保持)。
/state.json スキーマ(暫定)¶
判定済みの表記文字列で渡し、HTML 側は流し込むだけにする方針。
{
"phase": "select | playing | result",
"nowPlaying": {
"title": "...", "artist": "...", "genre": "...",
"level": "★12", "mode": "7K",
"bpm": "150-200", "notes": 1234,
"bestLamp": "HARD", "bestScore": "AAA 92.3%"
},
"lastResult": {
"title": "...", "lamp": "FULLCOMBO", "rank": "AAA",
"exscore": 2345, "diff": "+58", "updated": true
},
"session": {
"playCount": 14, "clearCount": 11,
"startedAt": "2026-06-12T20:00:00", "newRecords": 3
}
}
設定¶
-
ConfigにobsEnabled/obsPort(既定値の例: 7322)を追加。 - 設定画面への追加も実施。
- バインドは localhost 固定。
配置¶
- 新規パッケージ案:
bms.player.beatoraja.stream.obs(既存 stream パッケージと同系統)。 - 静的ファイル(#182 の HTML)は同サーバから配信する。
エージェント ちゃん さんが24日前に更新
- 説明 を更新 (差分)
エージェント ちゃん さんが23日前に更新
追加仕様 詳細設計(2026-06-13)本体側¶
親 #162 に対する追加表示要素の本体側設計。既存の「MainController.changeState() を唯一の更新funnelとする」方針を基本踏襲しつつ、GAS によるゲージ種別のプレイ中変化だけは追従用の副経路を追加する。
追加する表示要素と取得元¶
| 要素 | 取得元 | 更新タイミング |
|---|---|---|
| ランダムオプション(1P) |
BMSPlayer.getOptionInformation().randomoption を Random.getRandom(id, mode) 経由で名称化 |
changeState→PLAY(静的) |
| ランダムオプション(2P/DP) |
randomoption2(DP/バトル時のみ) |
同上 |
| ゲージオプション(現在値) |
BMSPlayer.getGauge().getType() を名称化 |
初期: changeState、以後: GAS変化時 |
| GAS On/Off・モード |
PlayerConfig.getGaugeAutoShift()(GAUGEAUTOSHIFT_NONE=Off) |
changeState(静的) |
| ランダムレーンパターン |
BMSPlayer.getOptionInformation().laneShufflePattern[0](RANDOM/R-RANDOM時のみ) |
changeState→PLAY(静的) |
| プレーヤー名 | PlayerConfig.getName() |
起動時/常時 |
| 取得段位 | 新規 PlayerConfig フィールド(手動設定文字列) |
起動時/常時 |
| 一言(目標など) | 新規 PlayerConfig フィールド |
起動時/常時 |
取得段位・一言はユーザー判断により「手動設定の文字列を
PlayerConfigに保持しランチャーUIで設定」とする。
重要:GAS のリアルタイム更新(既存funnelの限界)¶
GAS=on 時、ゲージ種別は BMSPlayer のプレイループ内(BMSPlayer.java:643-677 の GAS 処理ブロック)で gauge.setType(...) により切り替わる。これは changeState() を通らないため、既存の「changeState のみで ObsState を更新」方式では追従できない。
対応方針:
-
ObsStateに in-play 用の軽量更新 API(例:updatePlayGauge(int gaugeType))を追加。playingフェーズのスナップショットのうちゲージ表記だけを差し替え、volatile参照を入れ替える。 -
BMSPlayer側に直近反映済みのゲージ種別を保持し、GAS 処理直後に 種別が変化したときのみObsStateを更新(毎フレーム呼ばない/obsEnabledでガード)。 -
スレッド安全性は維持:
changeState()もBMSPlayer.update()も同じゲームスレッドから呼ばれるため単一ライタ。HTTP スレッドはvolatileスナップショットを読むだけでロック不要、という #181 の前提を崩さない。
レーンパターン(仕様C)は laneShufflePattern がプレイ開始時に確定し以後不変のため副経路は不要。changeState(PLAY) で BMSPlayer 生成(MainController.java:263)後にスナップショットへ格納する。
/state.json スキーマ拡張¶
profile(全phase共通・常時)を追加し、nowPlaying に playOption を追加する。表記済み文字列で渡し、HTML 側は流し込むだけ、の方針は維持。
{
"phase": "select | playing | result",
"profile": {
"name": "izu",
"dan": "発狂八段", // 手動設定 (PlayerConfig)
"comment": "目標:皆伝" // 手動設定 (PlayerConfig)
},
"nowPlaying": {
// ...既存フィールド...
"playOption": {
"random": "R-RANDOM", // 1P
"random2": null, // DP/バトル時のみ
"gauge": "HARD", // ★GASでライブ更新される現在のゲージ
"gaugeAutoShift": true, // On/Off
"gaugeAutoShiftMode": "BEST CLEAR",
"lane": "3 1 6 4 2 7 5" // RANDOM/R-RANDOM時のみ。他はnull
}
},
"lastResult": { /* 既存 */ },
"session": { /* 既存 */ }
}
表記の整形(本体側で確定)¶
既存配列・enum を流用する。
- ランダム名:
{OFF, MIRROR, RANDOM, R-RANDOM, S-RANDOM, SPIRAL, H-RANDOM, ALL-SCR, RANDOM-EX, S-RANDOM-EX}(PlayConfigurationViewのscoreOptionsと同一インデックス。R-RANDOM =Random.ROTATE)。PMS(9K) はOPTION_PMS準拠。 - ゲージ名:
{ASSIST EASY, EASY, NORMAL, HARD, EX-HARD, HAZARD, CLASS, EX-CLASS, EXHARD-CLASS}(GrooveGaugeの type 定数 0–8 に対応。コース/段位プレイの CLASS 系も含めて網羅)。 - GAS モード名:
{NONE, CONTINUE, SURVIVAL TO GROOVE, BEST CLEAR, SELECT TO UNDER}(GAUGEAUTOSHIFT_*0–4)。 - レーンパターン:
IntegerIndexPropertyTypes.getAssignedLane()(#180)と同等のロジックでlaneShufflePatternを整形(割当元レーン番号をスペース区切り)。#180 と重複するため共通ヘルパへの切り出しを推奨。RANDOM/R-RANDOM(=ROTATE) のみ対象(spec準拠。EX/CROSS 系は将来拡張余地)。
設定(Config / PlayerConfig)¶
-
PlayerConfigにString dan(取得段位)とString obsComment(一言)を追加。getter/setter とPlayerConfigPersistenceDtoへの追加、デフォルト空文字。 - ランチャー
PlayConfigurationView(プレイヤー設定タブ)にテキスト入力欄2つを追加。FXML・コントローラのバインド・リソースバンドルのキーを同期。 -
obsEnabled/obsPort(既定 7322)は本チケット既存設計のまま。
配置¶
- 既存設計どおり
bms.player.beatoraja.stream.obsにObsStateほかを配置。整形ヘルパ(ランダム名/ゲージ名/GAS名/レーンパターン)も同パッケージへ。
動作確認の想定¶
7K 譜面を RANDOM + GAS=BEST CLEAR でプレイし、http://localhost:7322/now-playing.html を開く。ランダム=RANDOM、レーンパターン=置換列、ゲージが初期 HARD から GAS 発動で NORMAL/EASY 等へ1秒ポーリングでライブに変化、GAS バッジ On、プロフィールに名前/段位/一言が表示されることを確認する。
本コメントは詳細設計であり実装着手前。ステータスは変更していない。
エージェント ちゃん さんが23日前に更新
補足(2026-06-13):HTML側の単一集約に伴う影響¶
#182 の方針を「用途別分割 → 単一 overlay.html に集約」へ変更したことに伴う本体側の確認事項。
-
/state.jsonスキーマ(profile/nowPlaying.playOption)は変更不要。元から全ブロックを1本で返す設計のため単一 HTML と整合する。 - HTTP サーバの静的配信は
overlay.html1枚(+CSS/JS)を配る構成になる。profile.htmlなど用途別テンプレートの配信は不要。 - ポーリングが3ソース→1ソースに集約されるため、
/state.json生成・配信の負荷面でもむしろ軽くなる。GAS のライブ更新経路(ObsState.updatePlayGauge)の設計は前コメントのまま。
エージェント ちゃん さんが23日前に更新
- ステータス を 未着手 から 実装中 に変更
- 進捗率 を 0 から 10 に変更
実装着手。作業ブランチ codex/162-obs-integration(develop 起点)にて本体側を実装する。コードベース調査で設計前提(changeState=MainController.java:248 / BMSPlayer 生成=263、RuntimeTaskManager.startVirtual、ReplayData.randomoption/randomoption2/laneShufflePattern、GrooveGauge 定数 0–8、GAS 処理=BMSPlayer.java:643-677 の gauge.setType、Config/PlayerConfig の Json DTO 永続化、PlayConfigurationView/FXML/UIResources.properties、IntegerIndexPropertyTypes.getAssignedLane=396-447)を実コードで確認済み。
エージェント ちゃん さんが23日前に更新
実装完了・MR !24 にて develop へ。https://gitlab.izu-san.com/games/beatoraja/-/merge_requests/24
本体側:新規 bms.player.beatoraja.stream.obs(ObsHttpServer / ObsState / SessionStats / ObsController / ObsFormat)。MainController.changeState() を funnel に select/nowPlaying/play/result/courseResult を反映、BMSPlayer の GAS 処理直後に種別変化時のみゲージをライブ更新。/state.json(phase/profile/nowPlaying(+playOption)/lastResult/session/history)、難易度表表記、選曲履歴(最大5件)。設定:Config.obsEnabled/obsPort、PlayerConfig.dan/obsComment+永続化、ランチャー Stream タブ UI。スレッド安全(単一ライタ+volatile)。xvfb-run -a ./gradlew test 成功。
エージェント ちゃん さんが23日前に更新
- ステータス を 実装中 から 完了 に変更
- 進捗率 を 10 から 100 に変更
MR !24 承認・ develop へマージ完了につき完了。
実装サマリ(本体側)¶
- 新規
bms.player.beatoraja.stream.obs:ObsHttpServer(localhost 固定・/state.json・./obs静的配信・パストラバーサル防止・仮想スレッド)、ObsState(volatile 不変スナップショット+キャッシュ済 JSON、ゲームスレッド単一ライタ)、SessionStats、ObsController、ObsFormat。 -
MainController.changeState()を funnel に select/nowPlaying/play/result/courseResult を反映。BMSPlayerの GAS 処理直後に種別変化時のみゲージをライブ更新。 -
/state.json:phase / profile / nowPlaying(+playOption) / lastResult / session / history。難易度は難易度表表記(sl9 / ★13)、表外は ★+PLAYLEVEL。選曲履歴最大5件。 - 設定:Config.obsEnabled/obsPort、PlayerConfig.dan/obsComment+永続化、ランチャー Stream タブ UI。obsEnabled=false 時ゼロ影響。
- テスト:
xvfb-run -a ./gradlew test成功。