拡張機能をつくる
拡張機能は、ふつうのネイティブバイナリです。vrOS がそれを起動し、監視し、ローカル のパイプ越しに — Windows では named pipe、Linux では Unix domain socket — VXP とい う小さな JSON-RPC 2.0 のプロトコルで通信します。1 行につき JSON オブジェクトが 1 つ、拡張機能ごとにパイプが 1 本、そしてトランスポートのどこにもネットワークはあり ません。
マニフェストのスキーマ、ワイヤ型、非同期クライアントを提供する Rust の SDK クレー トがあります。これは便利のためのものであって、必須ではありません。VXP はパイプの 上のただの JSON-RPC であり、パイプを開いて 1 行書ける言語ならどれでも話せます。す べてのリクエスト、応答、イベントの実際のバイト列はプロトコルリファレンスにあります。
動く拡張機能の全体像はクイックスタートを参照してください。
2 つの階層、1 つの API
Section titled “2 つの階層、1 つの API”| プロセス外 | バンドル型 | |
|---|---|---|
| 動き方 | vrOS が起動し監視する、それ自身のプロセス | vrOS 内部の監視付きタスク |
| クラッシュの隔離 | 完全 — プロセス境界 | パニックからの隔離と再起動 |
| 言語 | 任意 | Rust |
| 配布するのは | 作者自身 | vrOS |
vrOS に付属する拡張機能 — VRChat OSC、サウンドボード — はバンドル型で、誰でも使えるのと同じ API の上に作られ ています。ファーストパーティの機能が拡張機能 API の上で作れないなら、間違っている のは API のほうです。どちらの階層も同じディスパッチ経路を通るので意味は同一で、バ ンドル型から独立したプロセスへ移すのはマニフェストの変更だけで、ほかには何もいり ません。
ケーパビリティモデル
Section titled “ケーパビリティモデル”マニフェスト vros-ext.toml に、必要なものを宣言します。既定では何も許可されませ
ん。インストールされた拡張機能は設定 → 拡張機能に無効として並
び、要求したケーパビリティが列挙され、利用者が判断するまで停止したままです。新し
いものを求めるバージョンアップでは、その差分だけをもう一度たずねます。
| ケーパビリティ | 利用者が同意する内容 |
|---|---|
bus |
列挙したイベントトピックの受信と送信 |
overlays |
マニフェストが宣言したもの以外に、実行時に新しいパネルを作ること |
audio |
サウンドを読み込み、vrOS のオーディオエンジンで再生すること |
media |
インターネットの映像と音声を vrOS のプレーヤーで再生すること。映像を隠しても音声は続けられます |
face_provide |
フェイストラッキングのソース(目、口、またはその両方)を提供すること |
face_read |
利用者が選んだフェイストラッキングのデータを読むこと |
secrets |
OS のシークレットストアに資格情報を保存すること |
capture |
特定のウィンドウのキャプチャーオーバーレイを要求すること |
osc |
自前の OSC ソケットを開くこと |
network |
自前のネットワーク接続を開くこと |
ひとつも宣言しなくても、ハンドシェイク、自身の KV ストア、自身のバスプレフィック ス、マニフェストのパネル、コマンド、設定、文字列、パネルの画像は使えます。
2 つのフェイスのケーパビリティは、それだけで個別に許可されます。拡張機能に同意し てもオフのままで、利用者が拡張機能ごとにオンにし、動いている最中でもオフに戻せま す。
capture はマニフェストのスキーマにありますが、キャプチャーの名前空間はまだ提供
されていません。ホストは提供していないものを拒否するので、早すぎる要求に返るのは
沈黙ではなく型の付いたエラーです。
シークレットは、Windows では Credential Manager が支えます。Linux ではまだ資格情報 のバックエンドがつながっていないので、ストアは保存できるふりをせずに拒否します。 Linux ではセッションごとにたずねる前提で考えてください。
API が及ぶ範囲
Section titled “API が及ぶ範囲”| 名前空間 | 必要なもの | 何をするか |
|---|---|---|
hello |
— | 必須の最初のフレーム。応答は、許可されたケーパビリティ一式、利用者のロケール、vrOS のコンテンツフォルダーを返します。 |
kv |
— | 拡張機能ごとのキー / 値ストア。再起動、クラッシュ、PC の再起動をまたいで残ります。 |
secrets |
secrets |
OS の資格情報ストアの中の、名前の付いたスロット。vrOS がディスクに書き出すことはありません。 |
bus |
— | 送信と購読。自身の ext.<id>.* プレフィックスはいつでも使えます。それ以外にはマニフェストのパターンが必要です。現在中継されているコアのトピックは vr.lifecycle です。 |
panel |
— | 自身のパネルへプロパティを送り込み、そのコールバックを受け取ります。実行時に新しいパネルを定義するには overlays が必要です。 |
assets |
— | パネルに表示する画像(静止画またはアニメーション)をアップロードします。 |
commands |
— | 利用者が割り当てて実行できる、名前の付いたコマンドを登録します。 |
settings |
— | マニフェストから vrOS が描くフォームの値を読みます。 |
strings |
— | パネルのテキストのための、ロケール解決付きの参照。 |
audio |
audio |
サウンドファイルを読み込み、サウンドボード、マイク送り、UI のチャンネルで再生します。 |
media |
media |
vrOS のプレーヤーで映像や音声のストリームを開き、操作します。vrOS 全体で同時に 4 つまで。現在は Windows のみです。 |
face |
face_provide / face_read |
フェイストラッキングのソースを登録してサンプルを送る、または選ばれた目と口のデータを購読します。 |
パネルは、拡張機能のフォルダーに同梱する .slint のソースファイルです。ホストはそ
れを隔離したインタープリターでコンパイルするので、vrOS の中で外部のコードが動くこ
とはありません。コンパイルに失敗したパネルは、何かを巻き添えにするのではなく、目
に見えるバッジを出して自分を無効にします。ホストは自身の画面と同じエンジンで描画
するので、パネルは利用者のテーマ、スケール、密度を自動的に受け継ぎます。
[panels] で宣言できるロールは 2 つです。dash-tab はダッシュのレールとデスク
トップアプリにタブを置き、overlay は、利用者が配置でき、手首にアンカーでき、再起
動しても同じ場所に戻ってくる、浮かぶパネルを与えます。オーバーレイは width_mm
で空間内の物理的な幅を 100 から 10000 の範囲で提案できます。既定は 400 で、利用者
が自分でサイズを変えたら、そちらが優先されます。レールの枠には、4 文字までのラベ
ル(rail_label)と、vrOS 自身が描くアイコンのひとつ(icon)を付けられます。
書き方を決める規則が 5 つあります。
- 許可されている import は
@vros/theme.slintだけです。ほかの import も、画像ファ イルも、@trも使えません。 @trがないということは、言葉はプロパティとして届くということです。文字列のプロ パティを宣言し、テキストは拡張機能の側から送り込んでください。- 画像も同じように届きます。デコード済みのピクセルを
assetsでアップロードし、そ のキーを[image]のプロパティへ送ってください。vrOS が代わりに URL を取りに行 くことはありません。 - プロパティを送るのは、パネルが表示されているあいだだけにしてください。ホストは
panel_shownとpanel_hiddenのエッジを送り、送られたものを毎秒およそ 30 回に まとめ、かまわず送り続ける拡張機能にはレート制限をかけます。 - マニフェストはパネルのサイズを提案できます。ホストはそれを制限します。
テキスト欄を持つパネルは、ルートのコンポーネントに予約された 2 つのメンバーを置く
ことで VR キーボードを受け取れます。各欄のフォーカスのエッジで
callback field-focus(bool) を呼び、ホストが in property <int> field-blur を進め
たら、欄のフォーカスを外してください。どちらも宣言しないパネルは、単にキーを受け取
りません。完全な契約はプロトコルリファレンスにあります。
ライフサイクル
Section titled “ライフサイクル”vrOS は、環境変数にエンドポイントを入れてバイナリを起動します。起動から 5 秒以内に
hello を送り、初期化はそのあとで行ってください。有効な hello より前に送られた
ものはすべて拒否されます。restart のポリシーは never、on-crash、always のい
ずれかです。クラッシュするとバックオフをはさんで再起動され、クラッシュを繰り返す拡
張機能は、黙って消えるのではなく、拡張機能のペインで理由付きの無効に落ち着きます。
終了コード 0 のきれいな終了は再起動されません。
永続する状態は KV だけだと考え、それ以外は起動のたびに組み立て直してください。 vrOS はいつでも再起動をかけることがあります。再起動がリセットではなく再開になる、 というのがこのストアの狙いです。
run_without_vr の既定は true なので、ヘッドセットも VR ランタイムもない状態で動き
続けます。気にする拡張機能は vr.lifecycle を購読してください。VR がなくなるとオー
バーレイは隠れますが、状態が壊されることはありません。
アイドルはアイドルであるべきです。vrOS は、何も変わっていないときフレームあたりの 仕事をゼロに保つと自らに課しており、拡張機能にも同じことを期待します。ポーリング ではなく、自身の接続と自前のソケットでブロックしてください。そして仕事は、表示と 非表示のあいだの区間に収めてください。フレームの上限は 3 MiB で、許される最大の アニメーション画像が収まる大きさです。上限を超えた要求や敵対的な要求に対して、ホ ストは切断ではなく型の付いたエラーを返します。
プロトコルのバージョンは MAJOR.MINOR の形で、現在は VXP 1.7 です。ホストは
現行のメジャーと 1 つ前のメジャーを受け付け、マイナーは追加しかしません — 新しい
フィールド、新しいリクエスト、新しいイベント。古い 1.x に合わせて書いた拡張機能は、
新しいホストでも動き続けます。それ以降に加わったものが見えないだけです。いくつか
のイベントは、十分に新しいマイナーで hello を送った拡張機能にだけ届くので、古いク
ライアントが読めない形を受け取ることはありません。各マイナーで何が加わったかは
プロトコルリファレンスにあります。
SDK はどこにあるか
Section titled “SDK はどこにあるか”SDK のクレート、リファレンスの hello 拡張機能、テーマモジュール、オーサリング契約 はいずれも存在し、MIT または Apache-2.0 の選択制でライセンスされていますが、まだ公 開されていません。クレートは crates.io になく、リポジトリは非公開です。それが変 わったら、このページとクイックスタートからリンクします。そ れまでは Discord でたずねてください。