テストとデバッグ
| やりたいこと | 方法 |
|---|---|
| ログを見る | 出力ログパネル — [自分の id] … (host.Log)、隔離された場合は例外の内容 |
| 状態を見る | プラグインマネージャー・コンソールの plugins |
| 修正した dll を再読み込みする | プラグインマネージャーの「再読み込み (選択)」 |
| ロジックだけを素早くテストする | 偽の INpWorld で単体テスト |
| ブレークポイントを設定する | 実行中のエディターにデバッガーをアタッチ |
| 画面まで自動検査する | AddShotScenario (開発用ビルド専用、下の制限を参照) |
出力ログは下部エリアのパネルです。閉じている場合はウィンドウメニューから開きます。プラグインに関連する行は次のとおりです。
| 行 | 意味 |
|---|---|
[<id>] … | host.Log で記録した文字列 |
[プラグイン <id>] <バージョン> 有効 — 메뉴 1 · 빠른 추가 1 | 読み込みの成功と登録の要約 |
[プラグイン] <名前> 接続 — … | エディターに追加 |
[プラグイン <id>] 読み込みに失敗: <例外の型>: <内容> | Register ・コンストラクターで例外 |
[プラグイン <id>] エラー → 隔離 — <場所>: <例外の型>: <内容> | コールバックで例外。<場所> は メニュー <名前> ・ コマンド <名前> ・ Committed のように表示されます。 |
[プラグイン <id>] アンロード — <理由> | 無効化・再読み込み・エラーによるアンロード |
隔離されると、画面下部にも「プラグイン「<名前>」はエラーでオフになりました — …」という通知が表示されます。
コンソールコマンド
Section titled “コンソールコマンド”| コマンド | 役割 |
|---|---|
plugins | プラグイン一覧: id・バージョン・状態・エラー |
plugins reload | フォルダーを再スキャンし、有効なものを読み込む (既に有効なものは再読み込みしない) |
修正して再読み込みする
Section titled “修正して再読み込みする”- 再ビルドします。エディターは dll をロックしないため、起動したままビルドできます。
- 新しい dll (と pdb) をプラグインフォルダーに上書きします。
- プラグインマネージャーでその行を選択し、「再読み込み (選択)」を押します。
ビルド出力フォルダーをそのままプラグインフォルダーとして使えば、手順 2 は不要です。環境変数 NP_EDITOR_PLUGINS にビルド出力の親フォルダーを設定します。その下の各サブフォルダーが 1 つのプラグインです。
NP_EDITOR_PLUGINS=D:\dev\np_plugins_outD:\dev\np_plugins_out\my_first_plugin\ ← plugin.json · MyFirstPlugin.dll · .pdb環境変数は、エディターを再起動しないと読み込まれません。
単体テスト (偽の INpWorld)
Section titled “単体テスト (偽の INpWorld)”SDK はインターフェイスだけです。そのため、INpWorld を模倣した小さなクラスを使って、プラグインのロジックをエディターなしでテストできます。ブロックを作るコードは、INpWorld を受け取る関数として分離しておきます。
using NP.Editor.Sdk;
/// <summary>テスト用の偽ワールド: マス → ブロックステート。</summary>public sealed class FakeWorld : INpWorld{ private readonly Dictionary<NpCell, string> _b = new(); public string? FilePath => null; public NpBox? Selection { get; set; } public NpBox? Bounds => null; public string GetBlock(int x, int y, int z) => _b.TryGetValue(new NpCell(x, y, z), out var s) ? s : "minecraft:air"; public bool IsAir(int x, int y, int z) => !_b.ContainsKey(new NpCell(x, y, z)); public IEnumerable<NpBlock> Blocks(NpBox? box = null) => _b.Where(kv => box is not { } bb || bb.Contains(kv.Key.X, kv.Key.Y, kv.Key.Z)) .Select(kv => new NpBlock(kv.Key.X, kv.Key.Y, kv.Key.Z, kv.Value)); public long CountBlocks() => _b.Count; public int Edit(string label, Action<INpEdit> build) { var e = new Ed(_b); build(e); return e.Changed; }
private sealed class Ed : INpEdit { private readonly Dictionary<NpCell, string> _b; public int Changed; public Ed(Dictionary<NpCell, string> b) => _b = b; public void Set(int x, int y, int z, string state) { var c = new NpCell(x, y, z); if (state == "minecraft:air") { if (_b.Remove(c)) Changed++; return; } if (!_b.TryGetValue(c, out var old) || old != state) { _b[c] = state; Changed++; } } public void Clear(int x, int y, int z) => Set(x, y, z, "minecraft:air"); }}
// テスト対象のコード (プラグイン内)public static class PillarBuilder{ public static int Build(INpWorld w, NpCell at) => w.Edit("3×3 기둥", e => { for (int y = 0; y < 6; y++) for (int x = 0; x < 3; x++) for (int z = 0; z < 3; z++) e.Set(at.X + x, at.Y + y, at.Z + z, "minecraft:stone_bricks"); });}xUnit のテスト例です。
public class PillarTests{ [Fact] public void Pillar_is_3x3x6() { var w = new FakeWorld(); int n = PillarBuilder.Build(w, new NpCell(0, 1, 0)); Assert.Equal(54, n); Assert.Equal("minecraft:stone_bricks", w.GetBlock(2, 6, 2)); Assert.True(w.IsAir(0, 7, 0)); }}| テストしやすいもの | 方法 |
|---|---|
| ブロックを配置する計算 | 偽の INpWorld に Edit した後、GetBlock で確認 |
| コマンドの結果文字列 | NpCommand.Run(new[] { "6" }) の戻り値を確認 |
| CSV などのエクスポート | 一時パスで NpFileFormat.Run(path) を呼び出し、ファイルを確認 |
| ワールドロジックのプレビュー | new NpSceneSim(...) を直接作って Simulate を呼び出す |
エディターとまったく同じロック・元に戻すの動作は、偽のワールドでは確認できません。最後はエディターで手動確認します。
デバッガーのアタッチ
Section titled “デバッガーのアタッチ”- プラグインを Debug でビルドします。
.dllと.pdbを一緒にプラグインフォルダーに入れます。エディターは同じフォルダーの.pdbも一緒に読み込みます。- エディターを起動し、プラグインを有効にします。
- IDE の「プロセスにアタッチ」で
NPEditor.exeを選びます (.NET / CoreCLR デバッガー)。 - コードにブレークポイントを設定します。メニューを押したりコマンドを実行したりすると停止します。
- ブレークポイントで長く停止すると、エディターの画面も止まります。すべてのコールバックがメインスレッドで実行されるためです。
Registerにブレークポイントを設定するには、アタッチした後に「再読み込み (選択)」を押します。
画面検証シナリオ (AddShotScenario)
Section titled “画面検証シナリオ (AddShotScenario)”AddShotScenario は、エディターの画面検証ランナー用のシナリオを登録します。シナリオはワールドを変更し、カメラを配置し、判定とスクリーンショットを記録します。
host.AddShotScenario(new NpShot("myroad_smoke", async api =>{ api.World.Edit("시험", e => e.Set(0, 1, 0, "minecraft:stone")); api.LookAt(6, 6, -6, 0, 1, 0); // 目 (6,6,-6) → (0,1,0) を見る await api.Frames(5); // 5 フレーム待つ api.Check(api.World.GetBlock(0, 1, 0) == "minecraft:stone", "돌 놓임"); await api.Shot("smoke"); // PNG を保存}));ランナーのコマンドラインです。
NPEditor.exe -- --np-shot=myroad_smoke --np-out=<結果フォルダー> --np-quit| 引数 | 意味 |
|---|---|
--np-shot=<id>[,<id>…] | 実行するシナリオ |
--np-out=<フォルダー> | PNG と結果ファイル (shot.json — シナリオごとの合格/不合格) を書き出すフォルダー |
--np-quit | 終了したらエディターを閉じる |
- シナリオで例外が発生すると、その判定は不合格になり、プラグインは隔離されます。
- シナリオの開始時に
WorldChanged(Reason = ShotScenario) が届きます。
制限: 画面検証ランナーはエディターの開発用ビルドにのみ含まれます。インストール版にはありません。また、このモードではユーザープラグインフォルダーを自動で読み込みません。そのため、現在のバージョンのインストール版では外部プラグインのシナリオを実行できません。外部プラグインには単体テストと手動確認を使ってください。
手動確認リスト
Section titled “手動確認リスト”| 確認 | 理由 |
|---|---|
| 有効 → 無効 → 有効 | メニュー・パネルが二重にできたり残ったりしないか |
| 新規ワールド・別のワールドを開く | WorldChanged で状態をクリアしているか |
| 機能を 1 回 → Ctrl+Z を 1 回 | 元に戻すの 1 ステップできれいに戻るか |
| 選択なし・空のワールド | Selection ・ Bounds が null のときに例外が出ないか |
| 大きなワールド | Committed ・ライブテキストがエディターを止めないか |