Appearance
hoscene アセットフォーマット v1(草案)
com.hoshiboshi.asset.hoscene は、Hoshiboshi ワールドの静的データ(シーングラフ、リソース、WASM モジュール)を 1 つの .hoscene パッケージにまとめた配布フォーマットです。Nocturne プロトコルの information.assetKinds に記載し、QUIC 直送または CDN 経由でクライアントに届けます。
.hoscene の配布 MIME 型は application/hoscene です。内部コンテナは ZIP 互換ですが、Nocturne や CDN のトップレベル contentType では application/zip ではなく application/hoscene を使います。
ステータス
本稿は草案です。フィールド名、必須/任意の区別、WASM sandbox の詳細は今後の稿で固定します。
ZIP レイアウト
example-world.hoscene
├── manifest.json
├── Scenes/
│ ├── main.scene.json
│ └── lobby.scene.json
├── Resources/
│ ├── textures/
│ │ ├── wall_diffuse.png
│ │ └── poster.png
│ ├── audio/
│ │ ├── bgm_ambient.opus
│ │ └── click.ogg
│ ├── models/
│ │ ├── lobby.glb
│ │ └── props/
│ │ └── chair.glb
│ └── physics/
│ └── collision_lobby.glb
├── WASM/
│ ├── door_logic.wasm
│ └── bgm_controller.wasm
└── Extensions/
├── godot/
└── unity/| パス | 必須 | 説明 |
|---|---|---|
manifest.json | はい | パッケージ全体のメタデータとリソース索引。 |
Scenes/ | はい | 1 つ以上のシーン定義ファイル(.scene.json)。シーンの内部構造は hoscene scene を参照。 |
Resources/ | いいえ | テクスチャ、音声、モデル、物理コリジョン等の静的データ。 |
WASM/ | いいえ | クライアントで実行する WebAssembly モジュール。 |
Extensions/ | いいえ | エンジン固有の補助データ。読めなくてもワールドは立ち上がること。 |
ZIP 内のパスは大文字小文字を区別します。パッケージ作成ツールは、ファイルシステム間の差異を避けるため、パスを記載どおりに格納する必要があります。
manifest.json のスキーマ
json
{
"assetKind": "com.hoshiboshi.asset.hoscene",
"formatVersion": "1.0.0",
"worldId": "medi:world:ed25519:base64url-world-public-key",
"name": "Starter Lobby",
"summary": "A small social lobby world.",
"entryScene": "Scenes/main.scene.json",
"units": {
"distance": "meter",
"mass": "kilogram",
"time": "second"
},
"physics": {
"gravity": [0.0, -9.80665, 0.0],
"defaultMaterial": {
"staticFriction": 0.6,
"dynamicFriction": 0.6,
"restitution": 0.0
}
},
"scenes": [
{
"id": "main",
"path": "Scenes/main.scene.json"
},
{
"id": "lobby",
"path": "Scenes/lobby.scene.json"
}
],
"resources": [
{
"id": "model.lobby",
"kind": "model",
"storage": "embedded",
"path": "Resources/models/lobby.glb",
"mimeType": "model/gltf-binary",
"digest": "sha256:1111...",
"bytes": 8243201
},
{
"id": "audio.bgm",
"kind": "audio",
"storage": "external",
"path": "Resources/audio/bgm_ambient.opus",
"mimeType": "audio/opus",
"digest": "sha256:2222...",
"bytes": 512004
},
{
"id": "texture.poster",
"kind": "image",
"storage": "embedded",
"path": "Resources/textures/poster.png",
"mimeType": "image/png",
"digest": "sha256:3333...",
"bytes": 248000
},
{
"id": "collision.lobby",
"kind": "collision",
"storage": "embedded",
"path": "Resources/physics/collision_lobby.glb",
"mimeType": "model/gltf-binary",
"digest": "sha256:4444...",
"bytes": 340122
}
],
"wasmModules": [
{
"path": "WASM/door_logic.wasm",
"entry": true,
"tickRate": 30,
"permissions": {
"rpc": true,
"objectWrite": true,
"objectRead": true,
"log": true
},
"requiredCapabilities": [
"core.object",
"core.rpc"
],
"optionalCapabilities": [
"client.godot.xr"
],
"sandbox": {
"network": false,
"filesystem": false,
"worldMutation": false,
"maxMemoryBytes": 16777216,
"maxExecutionMillis": 5
},
"moduleDigest": "sha256:abcd..."
},
{
"path": "WASM/bgm_controller.wasm",
"entry": false,
"tickRate": 10,
"permissions": {
"rpc": false,
"objectWrite": false,
"objectRead": true,
"log": true
},
"requiredCapabilities": [
"core.object",
"core.log"
],
"optionalCapabilities": [],
"sandbox": {
"network": false,
"filesystem": false,
"worldMutation": false,
"maxMemoryBytes": 8388608,
"maxExecutionMillis": 3
},
"moduleDigest": "sha256:ef01..."
}
]
}主要フィールド
| フィールド | 必須 | 説明 |
|---|---|---|
assetKind | はい | 固定値 "com.hoshiboshi.asset.hoscene"。クライアントはこの値でフォーマットを識別する。 |
formatVersion | はい | この manifest スキーマ自体のセマンティックバージョン。現在は "1.0.0"。 |
worldId | はい | このワールドを識別する VirMesh の world ID。クライアントは接続先の world と一致することを検証する。 |
name | はい | 表示名。 |
summary | いいえ | 短い説明文。 |
entryScene | はい | 最初にロードするシーンファイルのパス(manifest.json からの相対パス)。 |
units | はい | 単位系宣言。 |
physics | いいえ | ワールド共通の物理既定値。省略時は上記例の値を使用。 |
scenes | はい | シーン一覧。各エントリは id(シーン識別子)と path(ファイルパス)を持つ。 |
resources | はい | リソース索引。空配列も可。 |
wasmModules | いいえ | WASM モジュール一覧。空配列も可。 |
パッケージ全体の digest
hoscene パッケージ全体の digest は、パッケージ内部の manifest.json ではなく、Nocturne の assetHash または World manifest の assets[].hash など、外側の配布メタデータで宣言します。
manifest.json 内に ZIP 全体の digest を含めると digest 対象が自己参照になるため、manifest.json は個別リソースと WASM モジュールの digest だけを持ちます。クライアントは、まず外側の配布メタデータで ZIP 全体を検証し、その後 manifest.json の resources[].digest と wasmModules[].moduleDigest で同梱または外部リソースを検証します。
単位系(units)
| キー | 値 | 説明 |
|---|---|---|
distance | "meter" | 距離の単位。 |
mass | "kilogram" | 質量の単位。 |
time | "second" | 時間の単位。 |
回転はクォータニオン [x, y, z, w] で表現し、単位系の影響を受けません。座標系は右手系・Y 軸上方向です。
リソースレジストリ(resources[])
シーンはリソースをファイルパスで直接参照せず、resourceId による間接参照を使います。実ファイルの所在は manifest.json の resources[] が解決します。
| フィールド | 必須 | 説明 |
|---|---|---|
id | はい | パッケージ内で一意なリソース識別子。シーンの各コンポーネントがこの ID で参照する。 |
kind | はい | リソース種別。"model" | "audio" | "image" | "collision"。 |
storage | はい | "embedded"(ZIP 内に同梱)または "external"(CDN 等の外部から取得)。 |
path | はい | embedded の場合は ZIP 内パス。external の場合は package base URI からの相対パス。 |
mimeType | はい | MIME 型。 |
digest | はい | リソースファイル単体の SHA-256 ハッシュ("sha256:base64url")。 |
bytes | はい | ファイルサイズ(バイト)。 |
storage: "external" のリソースは ZIP に実体を含みません。クライアントは hoscene パッケージを取得した URL、または Nocturne / World manifest が提供する asset URL の親ディレクトリを package base URI とし、path を相対解決して外部リソースを取得します。取得後は digest で個別に検証します。
QUIC 直送など package base URI が存在しない取得経路では、external resource を含む package はロード不可とするか、外側の asset metadata で package base URI を明示する必要があります。
推奨リソースフォーマット
| kind | 推奨 |
|---|---|
model | GLB(glTF Binary) |
audio | Opus(.opus)または OGG Vorbis(.ogg) |
image | PNG(.png)または JPEG(.jpg) |
collision | 簡略化 GLB(レンダリング用とは別の低ポリゴンメッシュ) |
WASM モジュール(wasmModules[])
各 WASM モジュールの設定:
| フィールド | 必須 | 説明 |
|---|---|---|
path | はい | WASM ファイルの ZIP 内パス。 |
entry | はい | true の場合、シーンロード時に自動起動する。false の場合は他の WASM またはスクリプトから明示的に起動される。 |
tickRate | はい | 1 秒あたりの呼び出し回数(Hz)。30 なら 33ms 間隔。 |
permissions | はい | このモジュールに許可する操作。rpc(RPC 発行)、objectWrite(オブジェクト変更)、objectRead(オブジェクト参照)、log(ログ出力)。 |
requiredCapabilities | はい | このモジュールの動作に必須の host capability 一覧。不足時は起動を拒否する。 |
optionalCapabilities | いいえ | あれば使うが、なくても動作する capability。 |
sandbox | はい | WASM 実行制約。 |
moduleDigest | はい | WASM バイナリの SHA-256 ハッシュ。 |
Sandbox 制約
| フィールド | 説明 |
|---|---|
network | false 固定。WASM は直接ネットワークアクセス不可。RPC は host bridge 経由。 |
filesystem | false 固定。WASM はローカルファイルシステムにアクセス不可。 |
worldMutation | false 固定。WASM はワールドの永続状態を直接変更不可。変更は host API 経由。 |
maxMemoryBytes | WASM インスタンスあたりの最大メモリ(バイト)。 |
maxExecutionMillis | 1 tick あたりの最大実行時間(ミリ秒)。超過時はモジュールを停止する。 |
WASM モジュールの実行モデルと host API の詳細は .mw WASM host API v1 を参照してください。
.mw との関係
hoscene は .mw フォーマット(medi.world.mw.v1)から以下の設計を継承しています:
- ZIP コンテナ、エンジン中立な scene graph JSON
resourceIdによる間接リソース参照storage: embedded | externalの 2 モード- digest ベースの検証(
sha256:base64url) - WASM を host 管理の安全な非同期ランタイムとして扱う方針
hoscene が .mw から意図的に変更した点:
| 項目 | .mw | hoscene |
|---|---|---|
| WASM の位置 | extensions/wasm/(拡張扱い) | WASM/(トップレベル) |
| WASM 設定 | 別ファイル runtime.json | manifest.json に統合 |
| フォーマット識別 | WorldServer 側 worldFormatId | パッケージ内 assetKind + Nocturne protocol information.assetKinds |
| コンポーネント命名 | component+me.virmesh.* 予定 | component+com.hoshiboshi.* |
セキュリティ
- クライアントは
manifest.jsonのworldIdが接続先ワールドと一致することを確認する。 - Nocturne の
assetHashまたは World manifest のassets[].hashが存在する場合、受信した ZIP 全体の SHA-256 が一致することを検証する。 - 各リソースの
digestが実ファイルと一致することを検証する。不一致のリソースはロードしない。 - WASM モジュールの
moduleDigestを検証し、不一致の場合は起動しない。 Extensions/内のファイルは、読めなくてもワールドが最低限動作する前提で扱う。extension の読み込み失敗でワールド全体を拒否しない。- ZIP 展開時に path traversal(
../、絶対パス、ドライブレター)を拒否する。 - パッケージのファイル数・合計サイズ・ZIP 展開後サイズに上限を設ける。
参考
- Nocturne プロトコル全体とアセット交換フロー: Nocturne
- シーン・コンポーネント詳細: hoscene scene
- WASM host API 草案:
.mwWASM host API v1 .mwフォーマット v1 概要:.mwv1 overview