マニフェスト
manifest.json のすべてのフィールド――エントリー、contributes の全体像、そしてほとんどの権限が手書きではなく派生である理由
manifest.json は、Studio がコードを実行せずに読み取る唯一のファイルです。プラグインが提供するものを宣言し、インストールプロンプトに表示されるすべてがそこから計算されます。
{
"manifestVersion": 2,
"id": "yourname.hello",
"name": "Hello",
"version": "1.0.0",
"publisher": "Your Name",
"description": "A starter plugin.",
"entries": { "studio": "main.js", "runtime": "runtime.js" },
"contributes": {
"blueprintNodes": ["yourname.hello.log"],
"runtimeCapabilities": ["store"]
},
"permissions": []
}トップレベルのフィールド
| フィールド | 必須 | 備考 |
|---|---|---|
manifestVersion | はい | 常に 2。バージョン 1 は拒否されます。 |
id | はい | 名前空間付き:publisher.plugin-name、すべて小文字、少なくとも 1 つのドット。 |
name | はい | 表示名。 |
version | はい | セマンティックバージョン(1.0.0)。プレリリースやビルドの接尾辞を付けてもかまいません。 |
publisher | いいえ | プラグイン一覧に表示されます。 |
description | いいえ | 1 行、インストール時に表示されます。 |
entries | はい | { studio?, runtime? }――パッケージ相対の ESM パス。少なくとも 1 つ必要で、両方のファイルが存在しなければなりません。 |
contributes | いいえ | プラグインが提供するすべて。キーを省略すると空のリストと同じ扱いになります。 |
permissions | いいえ | 作者が宣言する特権的な能力のみ――下記を参照してください。 |
エントリーパスは絶対パスにしたり、..、.、null バイト、?、# を含めたりすることはできません。
contributes
contributes は、プラグインが何をできるかについての唯一の真実の源です。Studio はプラグインのコードを実行せずにこれをもとにプロジェクトを検証し、インストール時の権限セットもここから導出されます。
| キー | 型 | 宣言する内容 |
|---|---|---|
blueprintNodes | string[] | このプラグインが提供するブループリントノード型。 |
widgets | string[] | このプラグインが提供するウィジェット要素型。 |
widgetText | object | 提供する各ウィジェットで、プレイヤーが読む文字を持つプロパティ。 |
locales | object[] | Studio の言語パック――プラグインが追加または補完するロケール。 |
runtimeData | string[] | ゲームと一緒に公開するプラグインストレージの名前空間。 |
runtimeCapabilities | string[] | runtime エントリーが使用してよい能力ドメイン。 |
sidecars | object[] | 作者のゲームに同梱されるネイティブの子プロセス。 |
buildDependencies | object[] | ビルド時に取得しキャッシュする外部バイナリ。 |
tests | string[] | Run > Test 向けに、このプラグインが提供するテスト種別。 |
buildConfig | object[] | このプラグインがビルドダイアログに追加するビルド構成フィールド。 |
externalLinks | string[] | runtime エントリーがプレイヤーのブラウザーで開いてよいアドレスパターン。 |
network | string[] | runtime エントリーがバイトを取得してよいホストパターン。 |
reservedSaveIds | string[] | プラグインが自分用に確保するセーブ id。プレイヤーのセーブ一覧には出ません。 |
structs | object[] | プラグインのノードが返す行のフィールド。 |
agentTools | object[] | studio エントリーが AI エージェントに提供するツール。それぞれ { name, write }。エージェント用ツールを参照。 |
agentGuide | string | パッケージ内の Markdown ファイル。このプラグインのガイドの章としてエージェントに提供される。 |
すべてのノード型、ウィジェット型、ストレージの名前空間、sidecar id、ビルド依存関係 id、予約セーブ id、構造体 id には、あなたのプラグイン id を前置しなければなりません。contributes の下に未知のキーがあると、無視されるのではなく拒否されます。
blueprintNodes と widgets
宣言していない型を登録すると、両方のエントリーで読み込み時にエラーになります。この宣言があるからこそ、Studio はビルドの前に、プロジェクトが使っているノードに runtime の提供元がないことを作者に伝えられます――何もしないノードを含むゲームを出荷してしまう代わりに。
widgetText
タイトルや説明文のように、プレイヤーが読む文字を表示するウィジェットは、その文字を持つプロパティをウィジェット型ごとに挙げます。
{
"contributes": {
"widgets": ["yourname.hello.badge"],
"widgetText": {
"yourname.hello.badge": [
{ "prop": "title", "label": "Title", "localized": { "zh": "标题", "ja": "タイトル" } },
{ "prop": "caption", "multiline": true }
]
}
}
}| フィールド | 備考 |
|---|---|
prop | 必須。文字を持つプロパティ。 |
keyProp | 作者が翻訳キーから文字を取るときに、翻訳キーを保持するプロパティ。省略時は <prop>LocalizationKey。Studio が書き込み、ウィジェットは描画に使いません。 |
label | Properties でその文字を呼ぶ名前。省略時はプロパティ名。 |
localized | エディターの言語ごとの label。zh や ja のような、エディターのロケールコードをキーにします。 |
multiline | 文字を複数行にできるかどうか。省略時は 1 行。 |
Studio は、宣言された各プロパティを、自身の Text や Button ウィジェットの文字と同じように扱います。Properties の Content(内容)セクションで Direct(直接入力)か Translation key(翻訳キー)を選べ、キャンバスはキーの文字を描画し、文字は翻訳テーブルと書き出しに並び、プロジェクトの検査は翻訳やキーの欠落を報告し、グリフの検査も文字を読みます。ゲームの中では、render は宣言したプロパティにプレイヤーの言語の文字をすでに受け取っており、ウィジェットがキーを自分で解決することはありません。
この宣言は両方のエントリーが読みます。studio エントリーのウィジェットはこれに従って編集され、runtime エントリーのウィジェットはこれに従って描画されます。ここで挙げる型はすべて contributes.widgets にも含まれ、1 つ以上の項目を持つ必要があります。prop と keyProp は識別子で、runtimeTextOrigin や runtimeTextUnit は使えず、同じウィジェットの中で名前を重複させられません。widgetText からインストール時の権限は派生しません。
runtimeData
プラグインのストレージはプロジェクトのエディターディレクトリの下にあり、そこは決してパッケージ化されません。runtime エントリーが Studio 内で作者が作成したデータ――カタログや検索テーブル――を必要とする場合は、その名前空間をここに列挙し、app.game.data.readJson(namespace) で読み取ってください。
{ "contributes": { "runtimeData": ["yourname.hello.catalog"] } }このリストは明示的な許可リストであり、エディター専用のプラグイン状態が誤って公開されたゲームに漏れ出さないようにするためのものです。readJson は同期的です――データはパックと一緒に運ばれます――名前空間が宣言されていない場合、プロジェクトがそれを一度も書き込んでいない場合、またはゲームがそのデータの公開より前のものである場合は null を返します。作者が作成したデータが存在することを前提にせず、優雅に縮退させてください。
runtimeCapabilities
13 個の能力ドメインがあり、それぞれが app.game の 1 つの名前空間に対応します。宣言されていないドメインは、エラーを投げるメソッドになるのではなく、オブジェクトから欠落します。entries.runtime を伴わずにこれを宣言することはマニフェストエラーです。
{ "contributes": { "runtimeCapabilities": ["store", "events", "state.read"] } }store · events · state.read · state.write · saves.read · saves.write · ui.overlay · assets · locale · menu · story.compile · diagnostics · process.memory
完全なモデル、それぞれが具体的に何を許可するか、そして各環境がそれをどうさらに狭めるかについては、Runtime APIを参照してください。
sidecars と buildDependencies
もっとも重い 2 つの宣言です。sidecarは、作者が構築するゲームに同梱されるネイティブの子プロセスです。ビルド依存関係は、Studio がビルド時にダウンロード、検証、キャッシュする外部バイナリです。どちらも <platform>-<arch> をキーとし、どちらも sha256 を必須とします。
locales
Studio の言語パックです――プラグインが追加するロケール、または組み込みロケールの欠落を補うものです。プラグインを作るを参照してください。contributes.locales だけを含み、エントリーのコードを持たないマニフェストも有効なプラグインです。
reservedSaveIds
プラグインが自分の id で書き込むセーブ(クイックセーブのスロットやチェックポイントなど)は、プレイヤーのセーブと同じセーブストアに置かれます。それらの id をここに列挙すると、プレイヤーのセーブ一覧から外れます。List Saves、それを使うすべてのセーブ画面、Dev Mode のセーブパネルは、オートセーブと同じようにそれらを除外します。それ以外の経路からは引き続き到達できます。プラグイン自身の app.game.saves(その listIds はストアをそのまま返します)と、id を受け取るセーブノード(Load Save、Delete Save、各セーブ読み取りノードなど)です。
{ "contributes": { "reservedSaveIds": ["yourname.quicksave.slot"] } }各 id はあなたのプラグイン id で始まらなければならないため、プラグインが作者自身の Save Game で書かれたスロットを隠すことはできません。id が予約されるのはプラグインがゲーム内で動いている間だけで、そのプラグインを動かさないゲームでは、それらのセーブもほかのセーブと同じように一覧に出ます。
structs
ギャラリーの項目、実績、ショップの商品など、レコードの集まりを返すノードは、そのレコードのフィールドをここで宣言し、出力ピンの型をその構造体にします。
{
"contributes": {
"blueprintNodes": ["yourname.shop.getGoods"],
"structs": [
{
"id": "yourname.shop.good",
"name": "Shop item",
"localized": { "zh": "商品", "ja": "商品" },
"fields": [
{ "key": "id", "type": "string" },
{ "key": "name", "type": "string" },
{ "key": "price", "type": "number" },
{ "key": "icon", "type": "image" },
{ "key": "owned", "type": "boolean" }
]
}
]
}
}{ id: "goods", kind: "output", semantic: "data", valueType: "array<struct:yourname.shop.good>", label: "Goods" }| フィールド | 備考 |
|---|---|
id | 必須。プラグイン id を前置します。レコードを 1 つ運ぶピンの型は struct:<id>、配列を運ぶピンの型は array<struct:<id>> です。 |
name | 必須。エディターでの構造体の名前で、ピンと List の Struct(構造体)の選択肢に表示されます。 |
localized | エディターの言語ごとの name。zh や ja などエディターのロケールコードをキーにします。 |
fields | 必須。1 つ以上。各フィールドを { key, type } で書き、この順序で List に表示されます。type は string、number、boolean、image、color、json のいずれかです。同じ key を 2 回使うことはできません。 |
宣言した構造体は、エンジン自身の構造体(エンディングやセーブの項目など)と同じように扱われます。
- List は Edit content(内容を編集)でそれを Struct に選べます。フィールドはプラグインから提供され、編集できないため、作者がドキュメントを見てフィールド名を書き写す必要はありません。
- そのピンからワイヤーを引き出すと、ノード追加メニューに各フィールドが並びます。
Get Fieldはフィールドを名前で読み取り、出力はフィールドの型になります。 - List の構造体のフィールドが欠けた行を
Set List Contentに接続すると、キャンバスとプロジェクトチェックで報告されます。
フィールドの key はそのまま id でもあるため、作者がプラグインの構造体のフィールド名を変えることはできません。エディター、実行中のゲーム、コマンドラインのチェックは、プラグインのコードを実行する前にマニフェストからこの宣言を読み取ります。プラグインが読み込まれていない場合(無効、または未インストール)、その構造体はノードと同様に不明になります。その構造体を使う List にはフィールドが表示されず、プロジェクトの依存関係のチェックがプラグインを示します。structs はインストール権限を派生させません。
権限は派生するものです
インストール権限には 2 つの系統があり、この区別こそが要点です。
作者が宣言するもの――あなたが permissions[] に書きます。これらは contributes とは無関係な、特権的な Studio の制御項目です。
{
"permissions": [
{ "kind": "filesystem", "path": "/absolute/path", "mode": "readwrite", "recursive": true },
{ "kind": "api", "capability": "bash.execute" }
]
}| 種類 | 形 |
|---|---|
filesystem | { path, mode: "read" | "write" | "readwrite", recursive }――実際のパス文字列。 |
api | { capability }――現時点でプラグインが使用できるのは bash.execute のみです。 |
これらは studio エントリーにのみ影響します。付与は pluginId@version ごとに記録されるため、バージョンを上げると作者は再度承認が必要になります。
派生するもの――Studio が contributes から計算し、あなたが手書きすることは決してありません。
| 種類 | 派生元 |
|---|---|
runtime | contributes.runtimeCapabilities の各項目。 |
sidecar | contributes.sidecars の各エントリーと、それが同梱するプラットフォームキー。 |
buildDependency | contributes.buildDependencies の各エントリーと、そのダウンロード元のホスト名。 |
externalLink | contributes.externalLinks。作者自身のパターンそのまま――書き換えられないので、プロンプトとマニフェストは同一の文書になります。 |
network | contributes.network。externalLink とは別の問題であり、決して統合されません。ページを開く操作はアドレスをブラウザーに渡すだけで何も返ってきませんが、これはバイトを取得する操作であり、返ってきたものはゲームの内部で実行されます。 |
agentTools | contributes.agentTools。すべてのツールと、そのうちプロジェクトを変更するもの。ツールを追加したり、読み取りのツールを書き込みのツールに変えたりすると、もう一度確認されます。 |
runtime、sidecar、buildDependency、externalLink、network、agentTools の権限を手で書くことはマニフェストエラーです――プラグインは "permission kind … is derived from contributes and must not be declared by hand." というメッセージとともにインストールに失敗します。能力を contributes に一度だけ宣言すれば、権限は自動的についてきます。
これが、インストールプロンプトの正直さを保っている仕組みです。ある能力はただ 1 か所でしか宣言されないため、プロンプトが列挙する内容とプラグインが実際に到達できる範囲がずれることはあり得ません。これはまた、更新の挙動も健全にします――能力を追加すると派生する権限セットが広がり、作者への再確認が自動的に発生します。一方、何も広げないバージョンは既存の許可をそのまま引き継ぎます。
検証エラー
| 症状 | 確認事項 |
|---|---|
| インストールが拒否される | manifest.json が正しい JSON でない、または id / version / entries の検証に失敗している。 |
| "requires a runtime entry" | entries.runtime を伴わずに runtimeCapabilities または sidecars が宣言されている。 |
| "must not be declared by hand" | 派生される種類の権限が permissions[] に書かれている。 |
| "Unknown plugin runtime capability" | runtimeCapabilities の綴りが誤っている。このリストは閉じています。 |
| 登録が読み込み時にエラーになる | contributes にない、またはプラグイン id を前置していないノード型やウィジェット型。 |
| プレビューが "Plugin validation failed" で失敗する | プロジェクトが使用するノードまたはウィジェットに runtime の提供元がない――プラグインが無効化されている、存在しない、entries.runtime がない、またはその型が宣言されていない。 |
| インストール時にダイジェストが一致しない | sidecar ファイルのバイト列がその sha256 と一致しない。 |
権限を一切派生させない貢献
tests、buildConfig、reservedSaveIds、structs は、インストール権限を一切生じさせない貢献であり、いずれの場合もそれは見落としではなく裁定です。
その他のコードを伴う貢献はすべて、プラグインが読み込まれた瞬間に何らかの力を得ます。テストは、作者が Run > Test を開き、ダイアログでそれを選び、Start を押したときにしか実行されません。インストール時に同意すべき常在の能力というものは存在せず、実行中のテストが何に触れられるかは、そのテスト自身の requires によって別途制御されます。ビルド構成フィールドは作者が埋める空欄にすぎず、それを埋めてもプラグインが以前より多くのものに触れられるようになるわけではありません。予約セーブ id は、プラグイン自身の名前空間にある id をプレイヤーのセーブ一覧から外すだけです。構造体は、プラグインのノードがもともと返す行のフィールドを記述するだけです。