読み込みとライフサイクル
エディターはプラグインフォルダーをスキャンして plugin.json を探します。検査に通り、有効になっているプラグインだけを読み込みます。プラグインのコードで例外が発生すると、そのプラグインだけを無効にし、エディターは動作を続けます。これを隔離といいます。
読み込みの順序
Section titled “読み込みの順序”- フォルダーのスキャン。 各プラグインフォルダーの直下にあるサブフォルダーを名前順に確認します。
plugin.jsonがあるフォルダーだけがプラグインです。 - plugin.json の検査。 項目に誤りがあると「エラー (ファイル)」です。
- SDK バージョンの比較。
sdk範囲にエディターの SDK バージョンが含まれない場合は「バージョン不一致」です。 - 有効状態の確認。 ユーザーが無効にしたプラグインは「オフ」です。
- 初回実行の警告。 プラグインを初めて見つけると、警告ウィンドウが 1 回表示されます。「信頼できる — 読み込む」を押すと記憶して読み込みます。「今回は読み込まない」を押すと、今回は何も読み込みません (「待機」のまま残ります)。
- ロードコンテキストの作成。 プラグインごとに個別に作成します。dll はバイトとして読み込むため、ファイルをロックしません。同じフォルダーに
.pdbがあれば一緒に読み込みます。 - エントリークラスの作成。
entry(なければ最初のINpEditorPlugin実装) を引数なしコンストラクターで作成します。クラスが見つからない、または作成できない場合は「エラー (隔離済み)」です。 Register(host)の呼び出し。 例外が発生すると何も登録されず、「エラー (隔離済み)」になります。- エディターへの追加。 メニュー・モード・パネル・コマンドなどをエディターに追加します。追加中に例外が発生すると「エラー (隔離済み)」です。名前が既に存在するコマンド・アクタータイプは、その項目だけが追加されません。
- オン。 出力ログに
[プラグイン <id>] <バージョン> 有効 — <登録の要約>が記録されます。
エディターの起動時に 1〜10 を 1 回行います。プラグインマネージャーの「再スキャン」やコンソールの plugins reload は 1〜10 をもう一度行います。既に有効なプラグインは再読み込みしません。
プラグインマネージャーの「状態」列と、コンソールの plugins の結果に表示されます。
| 状態 | 意味 | 対処 |
|---|---|---|
| 待機 | 見つかったが、まだ読み込んでいない (警告の確認前など) | 警告を確認するか「再スキャン」 |
| オン | 正常に動作中 | — |
| オフ | ユーザーが「有効」のチェックを外した | チェックを付け直す |
| エラー (ファイル) | plugin.json の誤り・dll がない・同じ id | 下の詳細行に表示される理由を修正 |
| バージョン不一致 | sdk 範囲がエディターの SDK を含まない。またはエディターの SDK にないメンバーを使っていて読み込みに失敗 | sdk を修正するか、エディターを更新 |
| エラー (隔離済み) | エントリークラスを作成できない・Register やコールバックで例外 → そのプラグインだけをアンロード | 出力ログの例外を修正し「再読み込み (選択)」 |
プラグインマネージャー
Section titled “プラグインマネージャー”ツール › プラグインマネージャー… で開きます。
| 列・ボタン | 役割 |
|---|---|
| 有効 | チェックすると有効にし (読み込み)、外すとアンロードします。状態は plugins_state.json に保存されます。 |
| 名前・バージョン・状態 | plugin.json の name ・ version と現在の状態です。 |
| 登録 | 有効なら登録の要約 (例 메뉴 1 · 빠른 추가 1)、そうでなければエラーの理由です。 |
| 再スキャン | フォルダーをもう一度確認します。新しいプラグインを見つけ、なくなったフォルダーはアンロードします。 |
| 再読み込み (選択) | 選択したプラグインをアンロードし、plugin.json ・ dll を読み直して有効にします。ビルド後に使います。 |
| フォルダーを開く | ユーザープラグインフォルダーを開きます。 |
無効化・再読み込み・アンロード
Section titled “無効化・再読み込み・アンロード”次の場合にプラグインをアンロードします: 「有効」のチェックを外す・「再読み込み (選択)」・フォルダー削除後の「再スキャン」・エラーによる隔離。エディターを終了するときは、アンロード処理を経ずにプロセスが終了します。そのため、保存のように必ず行うべき処理を Dispose に置かないでください。
- エディターが登録されたものをすべて取り外します (メニュー・モード・パネル・コマンド・ショートカット・アクタータイプ・セルガード・対称)。そのプラグインのモードを使用中だった場合は、ブロックブラシモードに戻ります。
- エントリークラスが
IDisposableであればDispose()を呼び出します。 - ワールドロジック type を登録簿から削除します。その type を使っていたルールは削除せず、「不明な type」の注意として残します。
- プレイカメラを保持していた場合は解放します (元の視点に戻ります)。
host.Drawで描いたものを消去します。- ロードコンテキストをアンロードします (collectible アンロード)。
host.Settings の値はファイルに残ります。再び有効にすると、そのまま読み込みます。
Dispose のルール
Section titled “Dispose のルール”| やるべきこと | 理由 |
|---|---|
静的イベント・タイマー・スレッドに登録したものは Dispose で解除します。 | 残っているとロードコンテキストがメモリからアンロードされません。 |
Task.Run で実行した処理は Dispose で停止させます (CancellationToken)。 | 無効化した後にワールドに触れてはいけません。 |
host.WorldChanged ・ Committed ・ View.PlayTicked などのホストイベントは解除しなくてもかまいません。 | アンロード時にホストがすべて破棄します。 |
Dispose で例外をスローしません。 | 出力ログに記録され、アンロードは続行されます。 |
public sealed class Plugin : INpEditorPlugin, IDisposable{ private readonly CancellationTokenSource _stop = new();
public void Register(INpEditorHost host) { // … 登録 … }
public void Dispose() => _stop.Cancel(); // バックグラウンド処理を停止}実行スレッド
Section titled “実行スレッド”- すべてのコールバック (メニュー・コマンド・イベント・
NpUi・Simulate・PlayTicked) は、エディターのメインスレッドで呼び出されます。 - 時間のかかる計算は
Task.Runに移します。 World.EditとDrawはメインスレッド (コールバック内) でのみ呼び出します。
ファイルの場所
Section titled “ファイルの場所”| 対象 | 場所 |
|---|---|
| ユーザープラグインフォルダー | %APPDATA%\NPEditor\plugins\<フォルダー>\ (ツール › プラグインフォルダーを開く) |
| インストールフォルダーのプラグイン | <インストールフォルダー>\plugins\<フォルダー>\ |
| 追加で確認するフォルダー | 環境変数 NP_EDITOR_PLUGINS (複数の場合は ; で区切る) |
| 有効化・無効化・警告の確認 | %APPDATA%\NPEditor\plugins_state.json |
プラグイン設定 (host.Settings) | %APPDATA%\NPEditor\plugin_settings\<id>.json |
| ユーザーが変更したショートカット | %APPDATA%\NPEditor\shortcuts.json |
| 大きな独自データ | host.PluginDir (= plugin.json があるフォルダー) の下の独自ファイル |
フォルダーは上の表の順にスキャンします。同じ id は先に見つかったものを使います。
id 名前空間
Section titled “id 名前空間”| 登録したもの | エディター内の名前 | 衝突した場合 |
|---|---|---|
| メニュー・クイック追加・インポート/エクスポート・パネル・モード・ショートカット | ext.<プラグイン id>.<自分の id> | 他のプラグイン・内蔵機能とは衝突しない |
ショートカットの Mode | 自分のモード id をそのまま書けば、エディターが ext.<プラグイン id>. を付ける | — |
| コンソールコマンド名 | そのまま (空白なし) | 既に存在すればそのコマンドだけ追加されない (先にあったものが優先) |
| ワールドロジック type | そのまま (同じ役割の中で) | 既に存在すれば (内蔵を含む) 登録時に例外 → そのプラグインを隔離 |
アクタータイプの Kind | そのまま | 既に存在すればそのタイプだけ追加されない (先にあったものが優先) |
コマンド名・ワールドロジック type・アクターの kind の前には自分の名前を付けます (例 road_build、myquest_start、myroad.zone)。
隔離で防げないもの
Section titled “隔離で防げないもの”- 無限ループ・スタックオーバーフロー・プロセスの終了 (
Environment.Exit) - ファイル・ネットワークへのアクセス
プラグインはエディターと同じ権限で動作するコードです。隔離はミスを防ぐ仕組みであり、セキュリティ境界ではありません。