Skip to content

feat: ゲームを起動せずに対戦相手・観戦者として振る舞うモッククライアント - #25

Open
progre wants to merge 31 commits into
mainfrom
claude/happy-shannon-8lkx5j
Open

progre wants to merge 31 commits into
mainfrom
claude/happy-shannon-8lkx5j

Conversation

@progre

@progre progre commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

概要

ゲームを1つだけ起動すれば対戦・観戦の動作を確認できるよう、テスト用のモッククライアントを example として追加します。

#23 をベースにしています(観戦プロトコル JUNOWEN/1.2 を使うため)。CI を実行するためにマージ先を main にしているので、差分には #23 の変更も含まれます。この PR 自体の変更は、b1ab8e0 と fc90a46 の2コミットです。

使い方

cargo run -p junowen --example mock_client -- <host|guest|spectator> [--name <NAME>]
モード モックの振る舞い ゲーム側の操作
host Pure P2P の対戦ホスト 「Connect as a Guest」で接続
guest Pure P2P の対戦ゲスト 「Connect as a Host」で接続
spectator Pure P2P の観戦者 対戦中に F1 で受け入れ
  • モックは自分のシグナリングコードをクリップボードにコピーします。相手のコードは、待ち始めた後にクリップボードにコピーされたものか、標準入力に貼り付けたものを使います。
  • 対戦者として動くときは、受け取った相手の入力をそのまま送り返します(ミラー入力)。両プレイヤーは同じフレームに同じ操作をします。
    • InitMatch と InitRound は相手から受け取ったときに応答します。ホストとして動く場合、試合設定は GameSettings の既定値で、乱数シードはラウンドごとに現在時刻から作ります。
  • 観戦者として動くときは、受信したメッセージ(InitSpectator / InitGame / InitRound)とフレーム数を表示します。

主な変更

  • junowen/examples/mock_client.rs を追加しました。
    • junowen は cdylib で直接参照できないので、#[path] で session モジュールを取り込んでいます。
  • SessionMessage を example から使えるように、session_message を pub mod にしました。
  • CI でモッククライアントをビルドし、CI アーティファクトの examples/mock_client.exe に含めるようにしました。リリース用の zip は同梱ファイルを個別に指定しているため、リリース物には含まれません。

確認状況

  • i686-pc-windows-msvc ターゲットで clippy --all-targets / fmt が通ることを確認しました。新しい警告はありません。
  • 実際の DelayedInputs とミラー応答を channel でつないだシミュレーションを、ゲームがホストの場合とゲストの場合の両方で実行しました。20ラウンド・ディレイ変更ありで、止まらずに常に p1 == p2 になりました。
  • 実機での動作は未確認です。

🤖 Generated with Claude Code

https://claude.ai/code/session_01UG779zc4sFhopV41zHJpXr

これまで観戦者は難易度選択画面 (カード未選択時) でしか参加できなかった。
ホストが直近の同期ポイント (難易度選択画面・キャラクター選択画面に入った時点) の
状態と乱数シード、およびそれ以降の入力を記録しておき、途中参加した観戦者には
それらを送信して早送りで再生させることで、キャラクター選択中や対戦中でも
参加できるようにする。

- 観戦者の受け付けを画面に関わらず行う (F1 も対戦中に受け付ける)
- 観戦者側はホストからの初期化情報を非ブロッキングで待つ
- キャラクター選択画面から同期する場合は難易度を決定して画面遷移し、
  到達時にカード選択状態と乱数シードを再現する
- 対戦中に未処理の入力が溜まっている場合は早送りで追いつく

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
- sync_screen を bool を返す is_sync_point に変更
- SpectatorSelect の initializing_state を enum 化し、
  キャラクター選択画面経由の同期でのみ到達時に乱数シードを設定するよう整理
- WaitingForPureP2pSpectator の ready を実態に合わせて show_hint に改名
- pushed_f1 の戻り値の変数名を f1_pushed に改名
- SyncPoint のメッセージ蓄積量についてコメントを追記

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
- 同期ポイントへの移動処理を SpectatorSelect から SpectatorSync 状態に切り出し、
  SpectatorSelect を従来に近いシンプルな形に戻す
- 実質到達しない pending_sessions と関連表示を削除し、
  同期ポイントが無い場合は途中参加を拒否する
- 同期ポイント以降の記録量に上限 (約 10 分) を設け、超えたら次の同期ポイントまで
  途中参加を受け付けない
- 対戦中の F1 受け付けをやめ、ready の改名も元に戻す
- Screen::Game を削除して unimplemented!() によるパニック経路をなくす
- buffered_len を副作用がわかる poll_buffered_len に改名

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
入力ログを記録・再生する方式をやめ、観戦者が接続した時点の状態を送る方式にする。

- キャラクター選択画面では、カーソル位置・カード・各プレイヤーの進行段階
  (キャラクター選択/カード選択/準備完了) を送る。進行段階はメモリ上の値が
  未解析のため、ホストが決定キーとキャンセルキーの押下から推定する
- 観戦者は決定キーを自動で押して進行段階を再現してから、ホストの入力を再生する
- 対戦中など状態を送れない間に接続した観戦者は、次に状態を送れるまで待機させる
- 入力ログとその上限を削除
- 観戦用の接続のデータチャネルプロトコルを JUNOWEN/1.2 にし、
  旧バージョンの観戦者を接続時に拒否する (対戦は JUNOWEN/1.1 のまま)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
キャラクター選択とカード選択は同時に進み、準備完了後もカードを変更できるため、
キャラクター選択画面の途中状態を送る方式をやめる。

- ホストは観戦者の接続時にプレイヤー名と設定を送り、試合開始時 (読み込み画面に
  移ったとき) に難易度・キャラクター・カード・乱数シードを送る
- 観戦者は難易度選択画面またはキャラクター選択画面で待機し、試合開始時の状態を
  受け取ったら難易度・カーソル・カードを合わせ、一致を確認してから決定する
- 選択画面の入力は観戦者に送らず、試合中の入力のみを送る
- 対戦中に接続した観戦者は次の試合から観戦する
- SpectatorSync / SpectatorSelect を SpectatorStandby にまとめる

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
- 古い方式を前提にしたコメントを直す
- 試合開始時の状態が一致しない場合は観戦を終了し、状態を合わせられないまま
  一定フレームを超えた場合も諦める
- 観戦者の試合中に次の試合の開始を受け取ったら、今の試合を打ち切って
  次の試合から同期し直す
- 待機中もゲームパッドの PAUSE で中断できるようにする (自動で入力した PAUSE は除く)
- try_recv_init_game の副作用をなくし、待機状態に入るときに明示的に破棄する

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
ホストは進行中の試合の開始時の状態と、それ以降のラウンド初期化情報・入力を
記録する (1試合分で、試合ごとに作り直す)。試合中に接続した観戦者には
これらをまとめて送り、観戦者は試合の最初から早送りでホストに追いつく。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
- next_game_initial フィールドをやめ、試合中に受け取った InitGame は
  受信バッファの先頭に戻す
- 待機の上限を超えたときに RecvError を中断の合図として流用していることをコメントに明記

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
@progre
progre changed the base branch from claude/funny-lamport-c1rav2 to main September 25, 2026 14:28
@progre progre closed this Sep 25, 2026
@progre progre reopened this Sep 25, 2026
claude and others added 14 commits September 27, 2026 15:27
観戦者側で selection.card に書き込んでも、試合では常に初期位置のカードが
使われることがわかった。ゲームが実際に使うカードの値の場所を特定するため、
試合開始時にホストと観戦者の両方で候補の値をログに出力する。
原因がわかったら削除する。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
カードは入力で選ばせる方式にするため、診断ログは不要になった。

This reverts commit e7ef63c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
selection.card への書き込みはキャラクター選択画面の表示にのみ影響し、
試合で使われるカードには反映されなかった。通常の操作と同じく
「低速移動を押しながら左右」の入力でカードを切り替える。

- 観戦者側で現在のカードを把握する。タイトル画面を経由すると初期位置に戻り、
  以降は前の試合の選択が引き継がれる
- キャラクター選択画面に入ってから少し待ち、目標との差の分だけ入力してから決定する

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
画面遷移中などに左右を押すと入力が無視され、カードが切り替わらなかった。
押す前の selection.card を覚えておき、値が変わったら入力が受け付けられたとみなして
次へ進む。一定フレーム変わらなければ押し直す。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
selection.card は v1.10c で構造体の配置が変わった後、正しい値を指していない
可能性が高く、ホストが常に 0 を送っていたため観戦者はカードを切り替えなかった。

- ホストは前回の選択を保持している vs_mode のカードを送り、観戦者も同じ値で照合する
- selection.card の変化で入力の受け付けを確認する方式は、値があてにならないため
  回数を数えて入力する方式に戻し、開始前の待ち時間を 60 フレームに延ばす
- 原因を確かめるため、試合開始時に両方の候補の値をログに出す (一時的)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
vs_mode のカードに 37 という、カードの番号としてありえない値が入っていたため、
正しい値の場所を改めて調べる。試合開始時にホストと観戦者の両方で、
selection・vs_mode のカード周辺・キャラクター選択カーソルの生のバイト列をログに出力する。
原因がわかったら削除する。

This reverts commit 718716e.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
v1.10c 対応で Player 構造体が大きくなった際、カードの位置が +90h のまま
残っており、常に別の値 (0 や 100) を読んでいた。診断ログで、カードの値が
+94h にあり vs_mode のカードと一致することを確認した。

カードの値を正しく読み書きできるようになったため、観戦者のカードは
入力ではなくキャラクター選択画面で直接書き込む方式に戻す。

- selection.card の位置を +94h に修正
- ホストは selection.card を送り、観戦者はキャラクター選択画面で書き込んで照合してから決定する
- 入力でカードを切り替える処理 (CardMover) と、観戦者側のカードの把握を削除
- 診断ログを削除

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
試合中に no wait を切り替えても反映されない可能性があり、読み込み画面で
no wait を強制的にオフにしていたため、試合全体が通常速度で進んでいた。
読み込み画面でも溜まった入力の量で判定し、溜まっていれば早送りで試合を始める。
早送り中も入力が届くまで待つので、追いついた後はホストの速度で進む。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
試合中に観戦を始めて10秒以上遅れても早送りが発生しなかった。
溜まった入力の量が条件を満たしていないのか、no wait を設定しても
ゲームに反映されていないのかを切り分けるため、約60フレームごとに
溜まった入力の数と no wait の設定前後の値をログに出力する。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
診断ログで、溜まった入力が 964 フレーム分あり no wait を有効にしても、
試合中は早送りされないことがわかった。試合中は表示の垂直同期待ちで
60fps に制限されていると考え、早送り中は Present を8回に1回だけ行う。
早送りの要求が途絶えたら2フレームで通常の表示に戻す。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qhChXocxUoUmMqt9cLXQa
protocol は SignalingSocket が決めて渡す値のため、PeerConnection は保持しない。
データチャネルの作成と相手との照合で使う箇所だけ引数で受け取る。

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
早送り中は表示を間引く対応を入れたため、原因調査用の一時的な診断ログを削除する。

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@progre
progre force-pushed the claude/happy-shannon-8lkx5j branch from fc90a46 to 9b05c75 Compare October 11, 2026 01:00
Direct3D9 のラッパーは windows クレート以外に依存しないため、
features として app から分離する。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@progre
progre force-pushed the claude/happy-shannon-8lkx5j branch from aa3fc42 to b2e5735 Compare October 11, 2026 04:43
画面の種類は型引数で受け取る。
入力は MenuButtons で、効果音・キーボード・描画は MenuDevice と MenuRenderer で受け取る。
Th19 とのつなぎ込みは app 側の lobby/common_menu.rs に置く。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@progre
progre force-pushed the claude/happy-shannon-8lkx5j branch from b2e5735 to cc40688 Compare October 11, 2026 06:44
progre and others added 2 commits October 11, 2026 18:12
…ュー定義を移動

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@progre
progre force-pushed the claude/happy-shannon-8lkx5j branch from cc40688 to e65d55b Compare October 11, 2026 09:31
@progre
progre force-pushed the claude/happy-shannon-8lkx5j branch from e65d55b to aff3bce Compare October 11, 2026 09:41
progre and others added 4 commits October 11, 2026 18:44
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
実機のゲームを 1 つだけ起動して対戦や観戦の動作を確認できるように、
Pure P2P でホスト・ゲスト・観戦者として接続する example を追加する。
対戦者は相手の入力をそのまま送り返すミラー入力で試合を進める。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UG779zc4sFhopV41zHJpXr
リリースの zip は同梱ファイルを個別に指定しているため、リリース物には含まれない。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UG779zc4sFhopV41zHJpXr
…l 引数に追随

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@progre
progre force-pushed the claude/happy-shannon-8lkx5j branch from aff3bce to 198412a Compare October 11, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants