등록점
등록점은 플러그인이 에디터에 기능을 붙이는 자리입니다. 모두 Register(host) 안에서 host.Add…(자료형) 으로 부릅니다. 자료형은 C# record 입니다. 기본값이 있는 칸은 이름 붙인 인자(Order: 600)로 줄 수 있습니다.
| 등록점 | 자료형 | SDK | 에디터 화면 |
|---|---|---|---|
AddMenuItem | NpMenuItem | 1.0 | 메뉴 막대 |
AddQuickAddItem | NpQuickAdd | 1.0 | 메인 툴바 「추가」 |
AddCommand | NpCommand | 1.0 | 「콘솔」 패널 |
AddImporter · AddExporter | NpFileFormat | 1.0 | 파일 › 가져오기: … · 빌드 › 내보내기: … |
AddContentTab | NpContentTab | 1.0 | 아래 영역 패널(창 메뉴) |
AddMode | NpMode | 1.0 | 모드 줄 · 도구 패널 · 뷰포트 클릭 |
AddActorType | NpActorType | 1.0 | 아웃라이너 「타입」 칸 |
AddCellGuard | Func<int,int,int,bool> | 1.0 | 모든 블록 편집 |
AddSymmetry | Func<IReadOnlyList<NpBlock>, IEnumerable<NpBlock>> | 1.0 | 놓기 · 부수기 · 브러시 · 도형 |
AddShotScenario | NpShot | 1.0 | --np-shot=<id> |
AddShortcut | NpShortcut | 1.1 | 도구 › 단축키… |
AddSceneEvent | NpSceneEvent | 1.2 | 월드 로직 — 월드 로직 type 추가 |
공통 규칙입니다.
Id에는 에디터 안에서ext.<플러그인 id>.가 붙습니다. 플러그인 안에서만 겹치지 않으면 됩니다.Id(명령은Name, 액터 타입은Kind)가 비거나 실행 함수(Run·Spawn·Build)가null이면 등록 예외입니다. 그러면 플러그인 전체가 켜지지 않습니다.- 등록한 콜백에서 예외가 나면 그 플러그인이 격리됩니다.
Order는 같은 자리 안의 순서입니다. 작을수록 앞입니다. 기본값은 500 입니다.- 화면 글에 이모지 · 그림 문자를 넣지 않습니다. 메뉴 · 탭 · 모드 이름의 그림 문자는 화면에서 빠집니다. 아이콘은
Icon칸에 아이콘 이름(prefab·block·light·trigger·logic…)으로 줍니다.
AddMenuItem — 메뉴 항목
섹션 제목: “AddMenuItem — 메뉴 항목”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 | 메뉴 안 순서. 100 단위가 바뀌는 곳에 구분선이 생깁니다(예 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));- 「창」 메뉴 항목은 패널 목록 아래에 붙습니다.
- 내보내기 성격의 항목은 「빌드」에 둡니다.
AddQuickAddItem — 빠른 추가
섹션 제목: “AddQuickAddItem — 빠른 추가”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"));AddCommand — 콘솔 명령
섹션 제목: “AddCommand — 콘솔 명령”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 — 파일 형식
섹션 제목: “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한 번으로 묶습니다.
AddContentTab — 패널
섹션 제목: “AddContentTab — 패널”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)에 있습니다.
AddMode — 모드
섹션 제목: “AddMode — 모드”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개 뒤의 모드는 「더 보기」에 들어갑니다. 플러그인 모드는 보통 「더 보기」에 있습니다.
- 플러그인을 끌 때 그 모드를 쓰던 중이면 블록 브러시 모드로 돌아갑니다.
AddActorType — 액터 타입
섹션 제목: “AddActorType — 액터 타입”public sealed record NpActorType(string Kind, string Label, string Color = "#9a9a9a");| 칸 | 설명 |
|---|---|
Kind | 액터 kind 글. 내 이름을 앞에 붙입니다(예 "myroad.zone"). |
Label | 아웃라이너 「타입」 칸 글자 |
Color | 「타입」 칸 색 "#rrggbb". 못 읽으면 회색입니다. |
host.AddActorType(new NpActorType("myroad.zone", "도로 구역", "#44aaff"));- 그 kind 의 액터가 아웃라이너에 보일 때 글자와 색을 정합니다.
- SDK 에는 액터를 만드는 API 가 아직 없습니다.
AddCellGuard — 칸 지킴이
섹션 제목: “AddCellGuard — 칸 지킴이”void AddCellGuard(Func<int, int, int, bool> guard);true 를 돌려준 칸은 어떤 편집도 바꾸지 못합니다. 브러시 · 도형 · 채우기 · 콘솔 명령 · 다른 플러그인의 World.Edit 모두에 적용됩니다. 되돌리기 · 다시 실행은 검사하지 않습니다.
host.AddCellGuard((x, y, z) => y < 0); // y 0 아래는 못 바꿈- 여러 지킴이가 있으면 하나라도
true면 막습니다. - 칸마다 불립니다. 아주 가볍게 씁니다(사전 · 상자 비교 정도).
AddSymmetry — 대칭
섹션 제목: “AddSymmetry — 대칭”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"입니다.
AddShotScenario — 화면 검증
섹션 제목: “AddShotScenario — 화면 검증”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) |
예제와 실행 방법, 제한은 테스트와 디버그에 있습니다.
AddShortcut — 단축키
섹션 제목: “AddShortcut — 단축키”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 등)가 이깁니다.