コンテンツにスキップ

テストとデバッグ

やりたいこと方法
ログを見る出力ログパネル — [自分の id] … (host.Log)、隔離された場合は例外の内容
状態を見るプラグインマネージャー・コンソールの plugins
修正した dll を再読み込みするプラグインマネージャーの「再読み込み (選択)」
ロジックだけを素早くテストする偽の INpWorld で単体テスト
ブレークポイントを設定する実行中のエディターにデバッガーをアタッチ
画面まで自動検査するAddShotScenario (開発用ビルド専用、下の制限を参照)

出力ログは下部エリアのパネルです。閉じている場合はウィンドウメニューから開きます。プラグインに関連する行は次のとおりです。

行意味
[<id>] …host.Log で記録した文字列
[プラグイン <id>] <バージョン> 有効 — 메뉴 1 · 빠른 추가 1読み込みの成功と登録の要約
[プラグイン] <名前> 接続 — …エディターに追加
[プラグイン <id>] 読み込みに失敗: <例外の型>: <内容>Register ・コンストラクターで例外
[プラグイン <id>] エラー → 隔離 — <場所>: <例外の型>: <内容>コールバックで例外。<場所> は メニュー <名前> ・ コマンド <名前> ・ Committed のように表示されます。
[プラグイン <id>] アンロード — <理由>無効化・再読み込み・エラーによるアンロード

隔離されると、画面下部にも「プラグイン「<名前>」はエラーでオフになりました — …」という通知が表示されます。

コマンド役割
pluginsプラグイン一覧: id・バージョン・状態・エラー
plugins reloadフォルダーを再スキャンし、有効なものを読み込む (既に有効なものは再読み込みしない)
  1. 再ビルドします。エディターは dll をロックしないため、起動したままビルドできます。
  2. 新しい dll (と pdb) をプラグインフォルダーに上書きします。
  3. プラグインマネージャーでその行を選択し、「再読み込み (選択)」を押します。

ビルド出力フォルダーをそのままプラグインフォルダーとして使えば、手順 2 は不要です。環境変数 NP_EDITOR_PLUGINS にビルド出力の親フォルダーを設定します。その下の各サブフォルダーが 1 つのプラグインです。

NP_EDITOR_PLUGINS=D:\dev\np_plugins_out
D:\dev\np_plugins_out\my_first_plugin\ ← plugin.json · MyFirstPlugin.dll · .pdb

環境変数は、エディターを再起動しないと読み込まれません。

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 を呼び出す

エディターとまったく同じロック・元に戻すの動作は、偽のワールドでは確認できません。最後はエディターで手動確認します。

  1. プラグインを Debug でビルドします。
  2. .dll と .pdb を一緒にプラグインフォルダーに入れます。エディターは同じフォルダーの .pdb も一緒に読み込みます。
  3. エディターを起動し、プラグインを有効にします。
  4. IDE の「プロセスにアタッチ」で NPEditor.exe を選びます (.NET / CoreCLR デバッガー)。
  5. コードにブレークポイントを設定します。メニューを押したりコマンドを実行したりすると停止します。
  • ブレークポイントで長く停止すると、エディターの画面も止まります。すべてのコールバックがメインスレッドで実行されるためです。
  • 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) が届きます。

制限: 画面検証ランナーはエディターの開発用ビルドにのみ含まれます。インストール版にはありません。また、このモードではユーザープラグインフォルダーを自動で読み込みません。そのため、現在のバージョンのインストール版では外部プラグインのシナリオを実行できません。外部プラグインには単体テストと手動確認を使ってください。

確認理由
有効 → 無効 → 有効メニュー・パネルが二重にできたり残ったりしないか
新規ワールド・別のワールドを開くWorldChanged で状態をクリアしているか
機能を 1 回 → Ctrl+Z を 1 回元に戻すの 1 ステップできれいに戻るか
選択なし・空のワールドSelection ・ Bounds が null のときに例外が出ないか
大きなワールドCommitted ・ライブテキストがエディターを止めないか