콘텐츠로 이동

등록점

등록점은 플러그인이 에디터에 기능을 붙이는 자리입니다. 모두 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, 액터 타입은 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메뉴 안 순서. 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));
  • 「창」 메뉴 항목은 패널 목록 아래에 붙습니다.
  • 내보내기 성격의 항목은 「빌드」에 둡니다.
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 — 파일 형식

섹션 제목: “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");
칸설명
Kind액터 kind 글. 내 이름을 앞에 붙입니다(예 "myroad.zone").
Label아웃라이너 「타입」 칸 글자
Color「타입」 칸 색 "#rrggbb". 못 읽으면 회색입니다.
host.AddActorType(new NpActorType("myroad.zone", "도로 구역", "#44aaff"));
  • 그 kind 의 액터가 아웃라이너에 보일 때 글자와 색을 정합니다.
  • SDK 에는 액터를 만드는 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 등)가 이깁니다.