불러오기와 수명
에디터는 플러그인 폴더를 훑어 plugin.json 을 찾습니다. 검사를 통과하고 켜져 있는 플러그인만 불러옵니다. 플러그인 코드에서 예외가 나면 그 플러그인만 끄고 에디터는 계속 돕니다. 이것을 격리라고 합니다.
불러오기 순서
섹션 제목: “불러오기 순서”- 폴더 훑기. 플러그인 폴더마다 바로 아래 하위 폴더를 이름 순으로 봅니다.
plugin.json이 있는 폴더만 플러그인입니다. - plugin.json 검사. 칸이 틀리면 「오류(파일)」입니다.
- SDK 판 비교.
sdk범위에 에디터 SDK 판이 들지 않으면 「버전 안 맞음」입니다. - 켜짐 확인. 사용자가 끈 플러그인은 「꺼짐」입니다.
- 첫 실행 경고. 플러그인을 처음 찾으면 경고 창이 한 번 나옵니다. 「믿을 수 있음 — 불러오기」를 누르면 기억하고 불러옵니다. 「이번엔 안 불러옴」을 누르면 이번에는 아무것도 불러오지 않습니다(「대기」로 남음).
- 로드 컨텍스트 만들기. 플러그인마다 따로 만듭니다. dll 은 바이트로 읽어 올리므로 파일을 잠그지 않습니다. 같은 폴더에
.pdb가 있으면 함께 올립니다. - 진입 클래스 만들기.
entry(없으면 첫INpEditorPlugin구현)를 인자 없는 생성자로 만듭니다. 클래스를 못 찾거나 만들지 못하면 「오류(격리됨)」입니다. Register(host)호출. 예외가 나면 아무것도 등록되지 않고 「오류(격리됨)」입니다.- 에디터에 붙이기. 메뉴 · 모드 · 패널 · 명령 등을 에디터에 붙입니다. 붙이는 중 예외가 나면 「오류(격리됨)」입니다. 이름이 이미 있는 명령 · 액터 타입은 그 항목만 붙지 않습니다.
- 켜짐. 출력 로그에
[플러그인 <id>] <판> 켜짐 — <등록 요약>이 남습니다.
에디터를 켤 때 110 을 한 번 합니다. 플러그인 관리자의 「다시 훑기」나 콘솔 10 을 다시 합니다. 이미 켜진 플러그인은 다시 읽지 않습니다.plugins reload 는 1
플러그인 관리자의 「상태」 칸과 콘솔 plugins 결과에 나옵니다.
| 상태 | 뜻 | 할 일 |
|---|---|---|
| 대기 | 찾았지만 아직 불러오지 않음(경고 확인 전 등) | 경고를 확인하거나 「다시 훑기」 |
| 켜짐 | 정상으로 돌고 있음 | — |
| 꺼짐 | 사용자가 「켜기」 체크를 끔 | 체크를 다시 켬 |
| 오류(파일) | plugin.json 이 틀림 · dll 없음 · 같은 id | 아래 상세 줄의 이유를 고침 |
| 버전 안 맞음 | sdk 범위가 에디터 SDK 를 포함하지 않음. 또는 에디터 SDK 에 없는 멤버를 써서 불러오다 실패 | sdk 를 고치거나 에디터를 업데이트 |
| 오류(격리됨) | 진입 클래스를 못 만듦 · Register 나 콜백에서 예외 → 그 플러그인만 내림 | 출력 로그의 예외를 고치고 「다시 읽기(선택)」 |
플러그인 관리자
섹션 제목: “플러그인 관리자”도구 › 플러그인 관리자… 로 엽니다.
| 칸 · 단추 | 하는 일 |
|---|---|
| 켜기 | 체크하면 켜고(불러옴), 끄면 내립니다. 상태는 plugins_state.json 에 남습니다. |
| 이름 · 버전 · 상태 | plugin.json 의 name · version 과 지금 상태입니다. |
| 등록 | 켜져 있으면 등록 요약(예 메뉴 1 · 빠른 추가 1), 아니면 오류 이유입니다. |
| 다시 훑기 | 폴더를 다시 봅니다. 새 플러그인을 찾고 없어진 폴더는 내립니다. |
| 다시 읽기(선택) | 고른 플러그인을 내리고 plugin.json · dll 을 다시 읽어 켭니다. 빌드한 뒤에 씁니다. |
| 폴더 열기 | 사용자 플러그인 폴더를 엽니다. |
끄기 · 다시 읽기 · 내리기
섹션 제목: “끄기 · 다시 읽기 · 내리기”다음 때 플러그인을 내립니다: 「켜기」 체크 끄기 · 「다시 읽기(선택)」 · 폴더 삭제 후 「다시 훑기」 · 오류 격리. 에디터를 끌 때는 내리기 과정 없이 프로세스가 끝납니다. 그래서 Dispose 에 저장처럼 꼭 해야 하는 일을 두지 않습니다.
- 에디터가 등록한 것을 모두 뗍니다(메뉴 · 모드 · 패널 · 명령 · 단축키 · 액터 타입 · 칸 지킴이 · 대칭). 그 플러그인 모드를 쓰던 중이면 블록 브러시 모드로 돌아갑니다.
- 진입 클래스가
IDisposable이면Dispose()를 부릅니다. - 월드 로직 type 을 등록부에서 뺍니다. 그 type 을 쓰던 규칙은 지우지 않고 「모르는 type」 주의로 남깁니다.
- 플레이 카메라를 쥐고 있었으면 놓습니다(원래 시점으로).
host.Draw로 그린 것을 지웁니다.- 로드 컨텍스트를 내립니다(collectible 언로드).
host.Settings 값은 파일에 남습니다. 다시 켜면 그대로 읽습니다.
Dispose 규칙
섹션 제목: “Dispose 규칙”| 해야 할 것 | 이유 |
|---|---|
정적 이벤트 · 타이머 · 스레드에 건 것은 Dispose 에서 풉니다. | 남아 있으면 로드 컨텍스트가 메모리에서 내려가지 않습니다. |
Task.Run 으로 돌린 작업은 Dispose 에서 멈추게 합니다(CancellationToken). | 끈 뒤에 월드를 건드리면 안 됩니다. |
host.WorldChanged · Committed · View.PlayTicked 같은 호스트 이벤트는 풀지 않아도 됩니다. | 내릴 때 호스트가 모두 버립니다. |
Dispose 에서 예외를 던지지 않습니다. | 출력 로그에 남고 내리기는 계속됩니다. |
public sealed class Plugin : INpEditorPlugin, IDisposable{ private readonly CancellationTokenSource _stop = new();
public void Register(INpEditorHost host) { // … 등록 … }
public void Dispose() => _stop.Cancel(); // 배경 작업 멈춤}실행 스레드
섹션 제목: “실행 스레드”- 모든 콜백(메뉴 · 명령 · 이벤트 ·
NpUi·Simulate·PlayTicked)은 에디터 메인 스레드에서 불립니다. - 오래 걸리는 계산은
Task.Run으로 뺍니다. World.Edit와Draw는 메인 스레드(콜백 안)에서만 부릅니다.
파일 위치
섹션 제목: “파일 위치”| 무엇 | 위치 |
|---|---|
| 사용자 플러그인 폴더 | %APPDATA%\NPEditor\plugins\<폴더>\ (도구 › 플러그인 폴더 열기) |
| 설치 폴더 플러그인 | <설치 폴더>\plugins\<폴더>\ |
| 더 볼 폴더 | 환경 변수 NP_EDITOR_PLUGINS (여러 개는 ; 로 구분) |
| 켜기 · 끄기 · 경고 확인 | %APPDATA%\NPEditor\plugins_state.json |
플러그인 설정(host.Settings) | %APPDATA%\NPEditor\plugin_settings\<id>.json |
| 사용자가 바꾼 단축키 | %APPDATA%\NPEditor\shortcuts.json |
| 큰 자기 데이터 | host.PluginDir(=plugin.json 이 있는 폴더) 아래 자기 파일 |
폴더는 위 표 순서로 훑습니다. 같은 id 는 먼저 찾은 것을 씁니다.
id 이름 공간
섹션 제목: “id 이름 공간”| 등록한 것 | 에디터 안 이름 | 겹치면 |
|---|---|---|
| 메뉴 · 빠른 추가 · 가져오기/내보내기 · 패널 · 모드 · 단축키 | ext.<플러그인 id>.<내 id> | 다른 플러그인 · 내장과 겹치지 않음 |
단축키 Mode | 내 모드 id 를 그대로 쓰면 에디터가 ext.<플러그인 id>. 를 붙임 | — |
| 콘솔 명령 이름 | 그대로(공백 없이) | 이미 있으면 그 명령만 붙지 않음(먼저 있던 것이 이김) |
| 월드 로직 type | 그대로(같은 역할 안에서) | 이미 있으면(내장 포함) 등록 예외 → 그 플러그인 격리 |
액터 타입 Kind | 그대로 | 이미 있으면 그 타입만 붙지 않음(먼저 있던 것이 이김) |
명령 이름 · 월드 로직 type · 액터 kind 앞에는 내 이름을 붙입니다(예 road_build, myquest_start, myroad.zone).
격리가 막지 못하는 것
섹션 제목: “격리가 막지 못하는 것”- 무한 루프 · 스택 넘침 · 프로세스 종료(
Environment.Exit) - 파일 · 네트워크 접근
플러그인은 에디터와 같은 권한으로 도는 코드입니다. 격리는 실수를 막는 장치이고 보안 경계가 아닙니다.