跳转到内容

注册点

注册点是插件向编辑器挂接功能的位置。全部在 Register(host) 中以 host.Add…(数据类型) 调用。数据类型为 C# record。有默认值的字段可以用命名参数(Order: 600)传入。

注册点数据类型SDK编辑器界面
AddMenuItemNpMenuItem1.0菜单栏
AddQuickAddItemNpQuickAdd1.0主工具栏「添加」
AddCommandNpCommand1.0「控制台」面板
AddImporter · AddExporterNpFileFormat1.0「文件 › 导入: …」 · 「构建 › 导出: …」
AddContentTabNpContentTab1.0下方区域面板(「窗口」菜单)
AddModeNpMode1.0模式栏 · 工具面板 · 视口点击
AddActorTypeNpActorType1.0大纲「类型」列
AddCellGuardFunc<int,int,int,bool>1.0所有方块编辑
AddSymmetryFunc<IReadOnlyList<NpBlock>, IEnumerable<NpBlock>>1.0放置 · 破坏 · 笔刷 · 形状
AddShotScenarioNpShot1.0--np-shot=<id>
AddShortcutNpShortcut1.1「工具 › 快捷键…」
AddSceneEventNpSceneEvent1.2世界逻辑 — 添加世界逻辑 type

以下是通用规则。

  • Id 在编辑器内会加上 ext.<插件 id>.。只要在插件内部不重复即可。
  • Id(命令为 Name,Actor 类型为 Kind)为空或执行函数(Run · Spawn · Build)为 null 时,注册抛出异常。此时整个插件都不会启用。
  • 注册的回调中发生异常时,该插件被隔离。
  • Order 是同一位置内的顺序,越小越靠前。默认值为 500。
  • 界面文字中不要使用表情符号 · 图形字符。菜单 · 标签页 · 模式名称中的图形字符会在界面上被去除。图标通过 Icon 字段以图标名称(prefab · block · light · trigger · logic …)指定。
public sealed record NpMenuItem(string Id, string Menu, string Label, Action Run, string Shortcut = "", int Order = 500);
字段说明
Id菜单项 id
Menu"파일" · "편집" · "창" · "도구" · "빌드" · "도움말"。若为其他名称,则在「帮助」前新建菜单。
Label菜单中显示的文字
Run点击时执行
Shortcut快捷键字符串(例 "Ctrl+Alt+H")。若为可识别的键,则显示在菜单文字旁,按下即执行,并可在「工具 › 快捷键…」中更改。
Order菜单内顺序。百位发生变化处会出现分隔线(例 499 与 500 之间)。
host.AddMenuItem(new NpMenuItem("hollow", "도구", "선택 속 비우기", () =>
{
if (host.World.Selection is not { } b) { host.Message("먼저 영역을 고르세요"); return; }
int n = host.World.Edit("속 비우기", e =>
{
for (int y = b.Min.Y + 1; y < b.Max.Y; y++)
for (int z = b.Min.Z + 1; z < b.Max.Z; z++)
for (int x = b.Min.X + 1; x < b.Max.X; x++)
e.Clear(x, y, z);
});
host.Message($"{n:N0}칸 비움");
}, Order: 550));
  • 「窗口」菜单的菜单项挂在面板列表下方。
  • 具有导出性质的菜单项放在「构建」中。
public sealed record NpQuickAdd(string Id, string Label, string Category, Action<NpQuickAddContext> Spawn,
string Keywords = "", int Order = 500, string Icon = "", string IconState = "");
public sealed record NpQuickAddContext(INpWorld World, NpCell Place);
字段说明
Label列表中显示的名称
Category分类名称。若为新名称,则在内置分类之后新建分类。
Spawn选中时执行。ctx.Place = 放置的格(鼠标下方方块面外侧的格,没有时为画面中央前方)
Keywords搜索词(空格分隔)。同时写入韩语 · 英语便于查找。
Icon图标名称(例 "prefab" · "light")
IconState作为图标绘制的方块状态字符串。若有,则优先于 Icon 使用。
host.AddQuickAddItem(new NpQuickAdd("lamp_post", "가로등", "도로", ctx =>
ctx.World.Edit("가로등", e =>
{
for (int y = 0; y < 3; y++) e.Set(ctx.Place.X, ctx.Place.Y + y, ctx.Place.Z, "minecraft:dark_oak_fence");
e.Set(ctx.Place.X, ctx.Place.Y + 3, ctx.Place.Z, "minecraft:lantern[hanging=false]");
}),
Keywords: "lamp light 가로등", Icon: "light", IconState: "minecraft:lantern"));
public sealed record NpCommand(string Name, string Usage, string Help, Func<string[], string> Run);
字段说明
Name命令名称。不含空格。由于不加前缀,请在前面加上自己的名称(例 road_count)。
Usage一行用法(例 "road_count [블록 id]")
Help一行说明
Run参数(名称后的各个词) → 显示在控制台中的结果字符串
host.AddCommand(new NpCommand("road_count", "road_count [블록 id]", "선택 안에서 그 블록 수를 셈", args =>
{
string id = args.Length > 0 ? args[0] : "minecraft:stone";
long n = host.World.Blocks(host.World.Selection).LongCount(b => b.State.StartsWith(id));
return $"{id}: {n:N0}칸";
}));
  • 若名称已存在(内置 · 其他插件),则该命令不会挂接。
  • Run 中发生异常时,控制台显示为失败,插件被隔离。

AddImporter · AddExporter — 文件格式

Section titled “AddImporter · AddExporter — 文件格式”
public sealed record NpFileFormat(string Id, string Label, string[] Extensions, Action<string> Run, int Order = 500);
字段说明
Label菜单文字。显示为「文件 › 导入: Label」 · 「构建 › 导出: Label」。
Extensions扩展名列表(不含点,例 new[] { "csv" })。用于文件对话框的过滤。
Run接收用户所选文件的完整路径。
Order菜单内顺序
host.AddExporter(new NpFileFormat("road_stats", "블록 통계 CSV", new[] { "csv" }, path =>
{
var rows = host.World.Blocks(host.World.Selection)
.GroupBy(b => b.State.Split('[')[0])
.OrderByDescending(g => g.Count())
.Select(g => $"{g.Key},{g.Count()}");
File.WriteAllLines(path, rows.Prepend("block,count"), new System.Text.UTF8Encoding(true));
}));
  • 导出对话框的默认文件名为 <世界名称>.<第一个扩展名>。
  • 导入时更改方块,也要合并在一次 World.Edit 中。
public sealed record NpContentTab(string Id, string Label, Func<NpUi> Build, int Order = 500);
字段说明
Label面板标题。「窗口」菜单的面板列表中也以此名称显示。
Build面板内容(NpUi)。启用插件时调用一次。
Order面板列表顺序
host.AddContentTab(new NpContentTab("panel", "도로", () => new NpUi()
.Title("도로 만들기")
.Number("폭", _width, 1, 9, 1, v => _width = (int)v)
.Check("가로등", _lamps, v => _lamps = v)
.Button("선택 영역에 깔기", () => host.Message($"폭 {_width}"))));
  • 面板以关闭状态生成在下方区域。由用户从「窗口」菜单打开。打开后可以拖到其他区域,或拖出为浮动窗口。
  • 图标为 plugin 图案。
  • NpUi 的用法见面板 UI (NpUi)。
public sealed record NpMode(string Id, string Label, Func<NpUi>? Panel = null, Func<NpClick, bool>? OnClick = null,
Action? Enter = null, Action? Exit = null, string Shortcut = "", int Order = 500);
public sealed record NpClick(NpCell Hit, NpCell Place, bool Shift, bool Ctrl);
字段说明
Label模式名称(模式栏工具提示 · 工具面板标题)
Panel工具面板中显示的内容。首次显示时创建一次并重复使用。没有时只显示标题。
OnClick视口左键点击。Hit = 命中的方块格,Place = 该面外侧的格(放置位置)。处理后返回 true。
Enter · Exit进入模式时 · 离开模式时
Shortcut切换到该模式的快捷键字符串
Order插件模式之间的顺序。插件模式始终排在内置模式之后。
host.AddMode(new NpMode("flag", "깃발 꽂기",
Panel: () => new NpUi().Title("깃발").Label("왼쪽 클릭 = 깃발 · Shift+클릭 = 지우기"),
OnClick: c =>
{
var p = c.Shift ? c.Hit : c.Place;
host.World.Edit(c.Shift ? "깃발 지우기" : "깃발", e =>
{
if (c.Shift) { for (int i = 0; i < 3; i++) e.Clear(p.X, p.Y + i, p.Z); return; }
e.Set(p.X, p.Y, p.Z, "minecraft:oak_fence");
e.Set(p.X, p.Y + 1, p.Z, "minecraft:oak_fence");
e.Set(p.X, p.Y + 2, p.Z, "minecraft:red_wool");
});
return true;
},
Exit: () => host.Draw.Clear()));
  • 若有 OnClick,该模式下视口的所有左键点击都交给 OnClick,不执行默认的方块放置。
  • 未命中方块的点击(天空)不会调用 OnClick。
  • 目前还无法接收拖动(按住移动),只接收点击。
  • 模式栏前 8 个之后的模式会放入「更多」中。插件模式通常位于「更多」中。
  • 禁用插件时若正在使用该模式,则返回方块笔刷模式。
public sealed record NpActorType(string Kind, string Label, string Color = "#9a9a9a");
字段说明
KindActor kind 字符串。在前面加上自己的名称(例 "myroad.zone")。
Label大纲「类型」列中的文字
Color「类型」列的颜色 "#rrggbb"。无法识别时为灰色。
host.AddActorType(new NpActorType("myroad.zone", "도로 구역", "#44aaff"));
  • 决定该 kind 的 Actor 显示在大纲中时的文字和颜色。
  • SDK 中目前还没有创建 Actor 的 API。
void AddCellGuard(Func<int, int, int, bool> guard);

返回 true 的格不能被任何编辑更改。适用于笔刷 · 形状 · 填充 · 控制台命令 · 其他插件的 World.Edit。撤销 · 重做不做检查。

host.AddCellGuard((x, y, z) => y < 0); // y 0 以下不可更改
  • 有多个守卫时,只要有一个返回 true 就会阻止。
  • 每个格都会调用。请写得非常轻量(字典 · 框比较程度)。
void AddSymmetry(Func<IReadOnlyList<NpBlock>, IEnumerable<NpBlock>> expand);

接收用户编辑(放置 · 破坏 · 笔刷 · 形状 · 拖动框 · 绘制)要更改的格列表,返回扩展后的列表。接在内置对称之后执行。

host.AddSymmetry(cells => cells.Concat(cells.Select(b => b with { X = -b.X }))); // 以 X=0 面镜像
  • 返回时要包含原来的格。若省略,原来的编辑会消失。
  • 改为空气的格(破坏)的 State 为 "minecraft:air"。
public sealed record NpShot(string Id, Func<INpShotApi, Task> Run);
INpShotApi 成员说明
World世界(INpWorld)
LookAt(float ex, float ey, float ez, float tx, float ty, float tz)将相机设为从眼睛位置 → 看向目标点
Frames(int n)等待 n 帧(await)
Check(bool ok, string what)记录一项判定(PASS/FAIL)
Shot(string name)将画面保存为 PNG(await)

示例、运行方法和限制见测试与调试。

public sealed record NpShortcut(string Id, string Label, string Key, Action Run, string Mode = "");
字段说明
Id快捷键 id。用户更改的键以此 id 保存。
Label快捷键窗口中显示的名称。在前面加上插件名称便于查找(例 "도로: 속 비우기")。
Key默认键字符串。遵循下表。
Run按下时执行
Mode"" = 任何位置。模式 id = 仅在该模式下。若是自己的模式,直接使用 AddMode 中写的 id。
host.AddShortcut(new NpShortcut("hollow_key", "도로: 선택 속 비우기", "Ctrl+Alt+H", Hollow));
host.AddShortcut(new NpShortcut("flag_undo", "깃발: 마지막 지우기", "Shift+J", RemoveLast, Mode: "flag"));
键字符串可识别
Ctrl+Alt+H · ctrl + alt + h · Cmd+K(= Ctrl)是
Shift+F5 · Ctrl+Enter · Alt+Space · Ctrl+[是
J(不带修饰键的单个键)是。容易与内置键冲突,不推荐。
1~8 · W/E/R · Ctrl++否 → 注册异常 → 插件无法启用
  • 键 = Ctrl · Alt · Shift 加一个键。键名为单个字母 · 数字、F1~F24,或 Enter · Space · Delete · Esc 等名称。
  • 用户在「工具 › 快捷键…」(Ctrl+Alt+K)中更改键。更改后的键在重新启用插件后依然保留。
  • 与其他键冲突时也会注册。在快捷键窗口中以红色显示,并记录到输出日志。
  • 键相同时,模式专用键优先于任何位置键。编辑器固定键 · 默认键(Ctrl+Z · F1~F7 等)优先。