Skip to content

開発・テストガイド

SDK はサーバーなしでのローカル開発から、マルチプレイテスト、パブリッシュまでの一連のワークフローをサポートしています。

ローカル開発(素の vite)

素の vite で 1 player 用の生 debug をする場合:

bash
npm run dev
# http://localhost:5173 を開く

素の vite (single-frame) の動作:

  • 親 frame として index.html がそのまま出るだけ。 SDK は !isHosted で早期 return し、 run() / sync() / init() は何もしない
  • pure vite の HMR や 1 player view のスタイル調整には十分

複数 player でのマルチプレイシミュレートやサーバー authoritative logic の debug には次節の uzu dev を使う。


uzu dev (harness + in-memory サーバー)

uzu dev は scenario の dev command を子 process で起動しつつ、 別 port で harness page (iframe grid + HUD + inspector) と in-memory GameRoom / SyncRoom / RelayRoom (本番と同一 WebSocket プロトコル) を serve するサブコマンドです。

scenario ディレクトリ配下では以下のいずれかで起動できます:

bash
# scenario ディレクトリ内で
pnpm run harness           # 各 scenario の package.json に "harness": "uzu dev" が仕込まれている

# node_modules/.bin 経由で直接
npx uzu dev

scenario 側は run<S>({ logic, events }) (or sync / init) を書くだけ。 mount コードや import 文は不要です。

動作

  • 起動すると 2 つの HTTP server が並走する:
    • scenario の vite (or 任意の) dev server — dev.command (default pnpm run dev) を子 process で起動
    • CLI harness server — free port で開き、 harness page + /ws/games/dev/:roomId / /ws/sync/:roomId / /ws/rooms/:roomId の WebSocket endpoint を提供
  • 子 iframe には ?server=ws://localhost:<port>&roomId=<key>&seatId=dev_N&seats=<json>&revisionId=dev が付き、 SDK は既存 online mode で dev-server に接続します (本番 Cloudflare Worker への接続経路と同じ shape)
  • manifest で "admin": true / "spectator": true を宣言すると、 グリッドに進行管理席 (admin_0) / 観戦席 (spec_0) の iframe が追加されます。 これらは seatskind: 'admin' | 'spectator' として載り、 ゲームの player 数には数えません。 scenario 側は setup() で kind を見て観戦系の view / 権限に振り分けます
  • GameRoom の roster は dev-server が manifest から組んで注入します (server 権威)。 接続クエリの roster 申告は採用しないため、 リロード前の古い harness タブが reconnect しても roster は汚れません
  • virtual server は Node.js 上で走るため、 disconnect / reconnect / message ordering / seq gap リカバリなど dev でしか再現しなかった bug が early に出ます
  • 各 iframe の inner viewport 短辺が devMinIframeShortEdge (default 360 CSS px) を下回らないよう、 親 frame の body に CSS zoom を自動で当てる。 5p landscape を mac 標準モニタで開くような狭い viewport でも iframe 内部の window.innerWidth/Height は実機サイズ相当を保つ

プレイヤー / orientation の解決順序

  1. manifest.jsoncharacters 配列長 → playerCount
  2. manifest.jsonplayerCount / orientation
  3. ハードコード fallback (playerCount: 2, orientation: 'portrait', devMinIframeShortEdge: 360)

manifest.json の dev フィールド

dev.commanddev.readyPattern は任意フィールド。 詳細は manifest リファレンス 参照。

jsonc
{
  "dev": {
    "command": "pnpm run dev",
    "readyPattern": "Local:\\s+http://[^\\s]+:(\\d+)",
  },
}

HUD

harness page 左上に浮遊する "UZU" ボタン。 クリックすると State Inspector などのデバッグ機能にアクセスできます。

State Inspector

sync() モードを使用している場合、State Inspector で現在の同期 state を JSON 形式でリアルタイムに確認できます(200ms 自動更新)。

デバッグボタンをクリックし、State Inspector を選択してください。


ビルドとパブリッシュ

ビルド

bash
npm run build    # tsc && vite build → dist/ に出力

パブリッシュ

uzu publish コマンドでビルドからデプロイまでを一括実行します。

bash
uzu publish              # ビルド → ZIP → R2 アップロード → リビジョン登録
uzu publish --skip-build # ビルド済みの場合はスキップ可能

publish には認証が必要です。ローカルでは uzu login --env dev で保存した認証情報が使われます。

処理フロー:

  1. manifest.json から id, playerCount, output を読み取り
  2. manifest.jsonbuild コマンドを実行
  3. output ディレクトリを ZIP 化
  4. R2 バケットに ZIP をアップロード
  5. manifest.jsonserverActionLogicPath の有無でモード判定
    • serverActionLogicPath あり → ServerAction: logic をビルド → R2 + WfP にデプロイ
    • serverActionLogicPath なし → SyncState: クライアント zip のみ
  6. UZU にリビジョン登録
  7. UZU Studio の URL を出力

CI から publish する (publish token)

ブラウザを開けない CI では、publish token を環境変数で渡します。uzu-cli は UZU_PUBLISH_TOKEN があればそれを短命の ID Token に交換して publish します (認証情報の優先順位は UZU_PUBLISH_TOKENuzu login の認証情報)。

bash
uzu token create --env dev --name "github actions"  # 平文はこの 1 回だけ表示される
uzu token list --env dev
uzu token revoke <id> --env dev
yaml
- name: Publish
  run: pnpm run publish
  env:
    UZU_PUBLISH_TOKEN: ${{ secrets.UZU_DEV_PUBLISH_TOKEN }}
  • token は発行者本人の権限で動きます。発行者が publish できるシナリオだけが対象です。
  • 平文は再表示できません。失くしたら revoke して発行し直します。
  • uzu token コマンドは uzu login の認証情報でしか使えません。UZU_PUBLISH_TOKEN からは token を発行・失効できません (漏れたトークンで失効を回避されないため)。
  • 現状 dev 環境のみ対応です。

デバッグのヒント

state が同期されない場合

  1. ブラウザの DevTools Console でエラーを確認
  2. デバッグボタンの State Inspector で state を確認
  3. connection.onPatchFailed コールバックでパッチエラーを検知

ローカルモードで動作するが Dev Harness で動作しない場合

  • initialState() が全プレイヤーで同じ結果を返すか確認(ランダム値を使う場合は SeededRandom を使用)
  • patch のパスが正しい JSON Pointer 形式か確認(例: /board/0/1