콘텐츠로 이동

월드 로직 type 추가

SDK 1.2 부터 플러그인이 월드 로직에 새 트리거 · 조건 · 동작을 더할 수 있습니다. host.AddSceneEvent 로 등록한 type 은 내장 type 과 똑같이 다음 자리에 나옵니다.

나오는 곳보이는 것
월드 로직 폼트리거 · 조건 · 동작 메뉴의 Category 묶음, 칸 입력
로직 그래프노드와 핀
검사필수 칸 · 액터 kind · 수 범위 · enum 값
규칙 한 줄 설명Summary 틀
플레이 미리보기Simulate 처리 · 「발동」 단추
AI 연결(MCP)scene_rules section=schema 목록에 [<플러그인 id>] 표시

에디터 등록은 폼 · 검사 · MCP · 미리보기용입니다. 게임 서버에서 실제로 돌리려면 서버 플러그인 NP Scene 쪽에도 같은 type 을 등록해야 합니다. 아래 「서버 쪽」을 봅니다.

plugin.json 에 "sdk": "^1.2" 이상을 씁니다. 칸 종류 ActorList 를 쓰면 "^1.3" 입니다.

host.AddSceneEvent(new NpSceneEvent(NpSceneEventRole.Trigger, "myquest_npc_talk", "NPC 에게 말 걸 때", "퀘스트", new[]
{
new NpSceneField("npc", NpSceneFieldKind.Actor, Required: true, ActorKinds: new[] { "display", "text", "spawn" }, Label: "NPC"),
new NpSceneField("line", NpSceneFieldKind.Text, Label: "대사 id"),
},
Help: "플레이어가 NPC 와 대화를 시작할 때", Icon: "interact",
Summary: "플레이어가 NPC {npc} 에게 말을 걸면")); // Simulate 없음 = 「발동」 단추로만
host.AddSceneEvent(new NpSceneEvent(NpSceneEventRole.Action, "myquest_start", "퀘스트 시작", "퀘스트", new[]
{
new NpSceneField("quest", NpSceneFieldKind.Text, Required: true, Label: "퀘스트 id"),
},
Icon: "star", Summary: "퀘스트 {quest} 시작",
Simulate: s => { s.Log($"퀘스트 {s.Str("quest")} 시작 → {s.Player}"); return true; }));

이 type 을 쓴 규칙은 scene.json 에 이렇게 저장됩니다(규칙 일부).

{
"trigger": { "type": "myquest_npc_talk", "npc": "npc_guard" },
"actions": [ { "type": "myquest_start", "quest": "q1" } ]
}
public sealed record NpSceneEvent(NpSceneEventRole Role, string Type, string Label, string Category, IReadOnlyList<NpSceneField> Fields,
string Help = "", string Icon = "event", string Summary = "", Func<NpSceneSim, bool>? Simulate = null);
public enum NpSceneEventRole { Trigger, Condition, Action }
칸필수설명
Role예역할: Trigger(트리거) · Condition(조건) · Action(동작)
Type예저장 이름(JSON "type"). 내 이름을 앞에 붙입니다(아래 「이름 규칙」).
Label예폼 · 그래프에 보일 이름
Category아니요메뉴 묶음 이름. 비우면 플러그인 이름(plugin.json 의 name)
Fields예칸 목록(NpSceneField). 칸이 없으면 빈 배열
Help아니요설명(툴팁)
Icon아니요아이콘 이름(trigger · event · signal · interact · logic · star …). 기본 event, 모르는 이름이면 기본 그림
Summary아니요한 줄 설명 틀(아래 「Summary 틀」). 비우면 Label
Simulate아니요플레이 미리보기 처리(아래 「Simulate」)

플러그인 type 은 같은 묶음 안에서 내장 type 뒤에 나옵니다.

public sealed record NpSceneField(string Name, NpSceneFieldKind Kind, bool Required = false, string Default = "", string[]? Options = null,
string[]? ActorKinds = null, string Label = "", string Help = "", double? Min = null, double? Max = null);
칸설명
NameJSON 키
Kind칸 종류(아래 표)
Required필수면 비었을 때 검사 오류
Default기본값 글. 형식은 아래 표의 「Default 쓰는 법」
OptionsEnum 선택지
ActorKindsActor 칸이 받는 액터 kind(trigger · blocker · display · text · spawn · camera · path …). 비우면 아무 kind
Label · Help폼에 보일 이름 · 설명
Min · MaxNumber 범위(검사)
종류폼 입력JSON 값Default 쓰는 법SDK
Text글글"안녕"1.2
Number수수"1.5"1.2
Bool체크true / false"true"1.2
Enum선택지 중 하나(Options)글"none"1.2
Actor액터 고르기(ActorKinds 만)액터 id 글액터 id1.2
Anim애니메이션 클립클립 id 글클립 id1.2
SoundEvent소리 이벤트"ns:path" 글"minecraft:block.note_block.bell"1.2
Block블록 상태글(minecraft:stone)블록 상태 글1.2
Item아이템글(minecraft:diamond_sword)아이템 id1.2
Particle파티클글(minecraft:happy_villager)파티클 id1.2
Effect포션 효과글(minecraft:speed)효과 id1.2
Vec3x · y · z[x, y, z]"[0,1,0]"1.2
Signal신호 이름글신호 이름1.2
Flag씬 플래그 이름글플래그 이름1.2
ActorList액터 여러 개액터 id 배열"cam_a,cam_b"1.3

내장 type 만 쓰는 칸 종류가 더 있습니다: 플래그 값 · 함수(로직 함수 부르기) · 블록 칸 목록(Cells). 이 종류는 아직 SDK 에 없습니다. 칸 보이기 조건 · 정수만 · #태그 허용 같은 칸 옵션도 아직 SDK 에 없습니다.

플러그인 type 도 로직 그래프에 노드로 나옵니다. 핀 규칙은 내장 type 과 같습니다.

핀이을 수 있는 수
실행 핀(흰 화살표)한 사슬. 노드 하나 뒤에 노드 하나
함수 핀(노란 마름모)핀 하나에 선 하나. 이미 이은 핀에 다시 이으면 바뀝니다.
데이터 핀(보라 동그라미, 액터 참조)액터 하나를 여러 칸에 이을 수 있습니다.
  • Actor 칸은 데이터 핀을 받습니다. 그래프에서 액터를 이으면 ActorKinds 를 검사합니다.
  • 함수 핀은 내장 「함수 부르기」 류에만 있습니다. 플러그인 type 은 함수 핀을 만들 수 없습니다.

0.9.11 부터: 함수를 부르는 노란 핀은 핀마다 선 하나입니다.

규칙 목록의 한 줄 설명에 쓰는 틀입니다. {칸 이름} 자리에 그 칸 값이 들어갑니다.

틀칸 값보이는 글
퀘스트 {quest} 시작quest = q1퀘스트 q1 시작
플레이어가 NPC {npc} 에게 말을 걸면npc = npc_guard(이름 「경비병」)플레이어가 NPC 「경비병」 에게 말을 걸면
퀘스트 {quest} 가 {state} 일 때state 비어 있음, Default = none퀘스트 q1 가 none 일 때
  • Actor 칸은 액터 id 대신 「액터 이름」으로 바뀝니다.
  • 값이 없는 칸은 Default 로 채웁니다.

Simulate 는 플레이 · 시뮬레이트 중에 규칙을 미리 돌리는 함수입니다. 게임 서버에서는 쓰지 않습니다.

역할Simulate 가 하는 일반환값없으면
Trigger미리보기 한 틱마다 그 type 규칙마다 한 번 불림. 일으키려면 s.Fire(player)무시월드 로직 · 플레이 패널의 「발동」 단추로만 일으킴
Condition참 · 거짓 판정true = 참늘 참으로 봄
Action동작 실행(기록 · 내 상태 바꾸기)무시기록만 남김

Simulate 에서 예외가 나면 그 플러그인이 격리됩니다. 그 플러그인의 type 은 모두 빠지고, 미리보기는 계속 돕니다.

Simulate 가 받는 값입니다.

멤버형식설명
Role · TypeNpSceneEventRole · string지금 처리하는 역할 · type
RuleIdstring규칙 id
Playerstring?트리거한 사람. 트리거 처리에서는 null
PlayersIReadOnlyList<string>지금 플레이어들
Nowdouble미리보기 시각(초)
ArgsIReadOnlyDictionary<string, string>노드 칸 값(모두 글)
Str(string key, string fallback = "")string칸 값 글
Num(string key, double fallback = 0)double칸 값 수
Bool(string key, bool fallback = false)bool칸 값 "true" / "false"
PositionOf(string player)NpVec3?플레이어 발 위치. 없으면 null
Log(string text)void미리보기 기록 한 줄([<id>] …)
Fire(string? player)void(Trigger 처리만) 이 규칙을 일으킴. 다른 역할에서는 아무 일 없음

Args 값의 글 꼴입니다.

칸 값Args 글
글그대로
수"1.5" (점 소수, 문화권 무관)
참 · 거짓"true" / "false"
배열 · 객체(Vec3 · ActorList)JSON 글("[0,1,0]"). ActorList 는 쉼표 글로 저장된 것도 그대로 옵니다.
host.AddSceneEvent(new NpSceneEvent(NpSceneEventRole.Trigger, "myquest_near_spawn", "스폰 가까이 올 때", "퀘스트", new[]
{
new NpSceneField("r", NpSceneFieldKind.Number, Default: "5", Label: "반경", Min: 1, Max: 64),
},
Summary: "스폰 {r}칸 안에 들어오면",
Simulate: s =>
{
foreach (var p in s.Players)
if (s.PositionOf(p) is { } pos && Math.Abs(pos.X) + Math.Abs(pos.Z) < s.Num("r", 5)) s.Fire(p);
return true;
}));
  • 이 함수는 미리보기 틱마다 불립니다. 가볍게 씁니다.
  • 거르기(한 번만 · 쿨다운 등)와 조건은 Fire 뒤에 에디터가 봅니다.

예: 상태를 기억하는 조건 · 동작

섹션 제목: “예: 상태를 기억하는 조건 · 동작”
private readonly Dictionary<string, string> _quests = new(); // 플레이어:퀘스트 → 상태
// 조건: 퀘스트 상태
Simulate: s => Get(s.Player, s.Str("quest")) == s.Str("state", "none")
// 동작: 퀘스트 시작
Simulate: s => { _quests[(s.Player ?? "") + ":" + s.Str("quest")] = "active"; s.Log("시작"); return true; }
// 새 월드에서는 비움
host.WorldChanged += _ => _quests.Clear();

전체 코드는 예제: event_example에 있습니다.

규칙이유
Type 앞에 내 이름을 붙입니다(myquest_start).같은 역할에 같은 type 이 이미 있으면(내장 포함) 등록 예외 → 그 플러그인 격리
소문자 · 숫자 · _ 로 씁니다.서버 플러그인 · JSON 과 맞추기 쉽습니다.
한 번 쓴 Type 은 바꾸지 않습니다.저장된 규칙이 그 이름을 씁니다.
내장 type 이름(cinematic · enter · sound …)을 쓰지 않습니다.내장과 겹치면 플러그인이 켜지지 않습니다. 내장 목록은 월드 로직 type 목록에 있습니다.
  • 그 플러그인의 type 이 메뉴 · 그래프 팔레트에서 빠집니다.
  • 그 type 을 쓰는 규칙은 지워지지 않습니다. 「모르는 type」 주의가 되고 칸 값은 그대로 남습니다.
  • 미리보기에서 모르는 트리거는 그 규칙을 건너뛰고, 모르는 조건은 거짓, 모르는 동작은 그 동작만 건너뜁니다.

게임에서 규칙을 실행하는 것은 서버 플러그인 NP Scene(Paper)입니다. 에디터 플러그인은 서버에서 돌지 않습니다.

  1. 같은 type 의 처리기를 Paper 플러그인으로 만듭니다.
  2. Bukkit ServicesManager 에서 NP Scene 의 SceneEventsApi 를 받아 그 type 을 등록합니다.
  3. 칸 이름과 값 형식(JSON)을 에디터 쪽 NpSceneField 와 똑같이 맞춥니다.

서버가 모르는 type 은 경고 한 번과 함께 건너뜁니다(트리거 = 규칙 건너뜀, 조건 = 거짓, 동작 = 그 동작만 건너뜀). 그래서 서버 처리기가 없어도 맵은 열리지만 그 규칙은 돌지 않습니다.