Adding world logic types
Since SDK 1.2, plugins can add new triggers, conditions and actions to World logic. Types registered with host.AddSceneEvent appear in the following places, exactly like built-in types.
| Where | What appears |
|---|---|
| World logic form | The Category group in the trigger · condition · action menus, field inputs |
| Logic graph | Nodes and pins |
| Check | Required fields · actor kinds · number ranges · enum values |
| One-line rule summary | Summary template |
| Play preview | Simulate handling · Fire button |
| AI connection (MCP) | Marked [<plugin id>] in the scene_rules section=schema list |
Registration in the editor is for the form, checks, MCP and preview. To actually run the type on the game server, you must also register the same type in the server plugin NP Scene. See “Server side” below.
Write "sdk": "^1.2" or later in plugin.json. If you use the field kind ActorList, use "^1.3".
Quick example
Section titled “Quick example”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} 에게 말을 걸면")); // no Simulate = fired only with the Fire button
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; }));A rule that uses these types is saved in scene.json like this (part of a rule).
{ "trigger": { "type": "myquest_npc_talk", "npc": "npc_guard" }, "actions": [ { "type": "myquest_start", "quest": "q1" } ]}NpSceneEvent
Section titled “NpSceneEvent”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 }| Field | Required | Description |
|---|---|---|
Role | Yes | Role: Trigger · Condition · Action |
Type | Yes | Saved name (JSON "type"). Start it with your own name (see “Naming rules” below). |
Label | Yes | Name shown in the form and graph |
Category | No | Menu group name. If empty, the plugin name (name in plugin.json) |
Fields | Yes | Field list (NpSceneField). An empty array if there are no fields |
Help | No | Description (tooltip) |
Icon | No | Icon name (trigger · event · signal · interact · logic · star …). Default event; an unknown name uses the default image |
Summary | No | One-line summary template (see “Summary templates” below). If empty, Label |
Simulate | No | Play preview handling (see “Simulate” below) |
Plugin types appear after the built-in types within the same group.
NpSceneField
Section titled “NpSceneField”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);| Field | Description |
|---|---|
Name | JSON key |
Kind | Field kind (table below) |
Required | If required, an empty value is a check error |
Default | Default value string. The format is in the “How to write Default” column of the table below |
Options | Enum choices |
ActorKinds | Actor kinds an Actor field accepts (trigger · blocker · display · text · spawn · camera · path …). If empty, any kind |
Label · Help | Name and description shown in the form |
Min · Max | Number range (checked) |
Field kinds: NpSceneFieldKind
Section titled “Field kinds: NpSceneFieldKind”| Kind | Form input | JSON value | How to write Default | SDK |
|---|---|---|---|---|
Text | Text | string | "안녕" | 1.2 |
Number | Number | number | "1.5" | 1.2 |
Bool | Checkbox | true / false | "true" | 1.2 |
Enum | One of the choices (Options) | string | "none" | 1.2 |
Actor | Actor picker (ActorKinds only) | actor id string | actor id | 1.2 |
Anim | Animation clip | clip id string | clip id | 1.2 |
SoundEvent | Sound event | "ns:path" string | "minecraft:block.note_block.bell" | 1.2 |
Block | Block state | string (minecraft:stone) | block state string | 1.2 |
Item | Item | string (minecraft:diamond_sword) | item id | 1.2 |
Particle | Particle | string (minecraft:happy_villager) | particle id | 1.2 |
Effect | Potion effect | string (minecraft:speed) | effect id | 1.2 |
Vec3 | x · y · z | [x, y, z] | "[0,1,0]" | 1.2 |
Signal | Signal name | string | signal name | 1.2 |
Flag | Scene flag name | string | flag name | 1.2 |
ActorList | Multiple actors | array of actor ids | "cam_a,cam_b" | 1.3 |
There are more field kinds used only by built-in types: flag value · function (call a logic function) · block cell list (
Cells). These kinds are not in the SDK yet. Field options such as visibility conditions, integers only and allowing#tagsare not in the SDK yet either.
In the Logic graph
Section titled “In the Logic graph”Plugin types also appear as nodes in the Logic graph. The pin rules are the same as for built-in types.
| Pin | How many connections |
|---|---|
| Exec pin (white arrow) | One chain. One node after each node |
| Function pin (yellow diamond) | One wire per pin. Connecting an already-connected pin replaces the wire. |
| Data pin (purple circle, actor reference) | One actor can be connected to multiple fields. |
Actorfields accept data pins. When you connect an actor in the graph,ActorKindsis checked.- Function pins exist only on built-in Call function types. Plugin types cannot create function pins.
Since 0.9.11: the yellow pins that call functions take one wire per pin.
Summary templates
Section titled “Summary templates”The template used for the one-line summary in the rule list. Each {field name} placeholder is replaced with that field’s value.
| Template | Field value | Displayed text |
|---|---|---|
퀘스트 {quest} 시작 | quest = q1 | 퀘스트 q1 시작 |
플레이어가 NPC {npc} 에게 말을 걸면 | npc = npc_guard (name “경비병”) | 플레이어가 NPC 「경비병」 에게 말을 걸면 |
퀘스트 {quest} 가 {state} 일 때 | state empty, Default = none | 퀘스트 q1 가 none 일 때 |
Actorfields are replaced with the actor name instead of the actor id.- Fields without a value are filled with
Default.
Simulate — Play preview
Section titled “Simulate — Play preview”Simulate is a function that runs rules ahead of time during Play and Simulate. It is not used on the game server.
| Role | What Simulate does | Return value | If omitted |
|---|---|---|---|
| Trigger | Called once per rule of that type on every preview tick. To fire, call s.Fire(player) | Ignored | Fired only with the Fire button in the World logic and Play panels |
| Condition | True/false decision | true = true | Always treated as true |
| Action | Runs the action (logging · changing your own state) | Ignored | Only a log entry is left |
If Simulate throws, the plugin is isolated. All of that plugin’s types are removed, and the preview keeps running.
NpSceneSim
Section titled “NpSceneSim”The value Simulate receives.
| Member | Type | Description |
|---|---|---|
Role · Type | NpSceneEventRole · string | The role and type being handled |
RuleId | string | Rule id |
Player | string? | Who triggered it. null when handling a trigger |
Players | IReadOnlyList<string> | Current players |
Now | double | Preview time (seconds) |
Args | IReadOnlyDictionary<string, string> | Node field values (all strings) |
Str(string key, string fallback = "") | string | Field value as a string |
Num(string key, double fallback = 0) | double | Field value as a number |
Bool(string key, bool fallback = false) | bool | Field value "true" / "false" |
PositionOf(string player) | NpVec3? | The player’s foot position. null if none |
Log(string text) | void | One line in the preview log ([<id>] …) |
Fire(string? player) | void | (Trigger handling only) Fires this rule. Does nothing in other roles |
String forms of Args values:
| Field value | Args string |
|---|---|
| String | As-is |
| Number | "1.5" (dot decimal, culture-independent) |
| True/false | "true" / "false" |
Array · object (Vec3 · ActorList) | JSON string ("[0,1,0]"). For ActorList, values saved as comma-separated strings also arrive as-is. |
Example: a self-firing trigger
Section titled “Example: a self-firing trigger”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; }));- This function is called on every preview tick. Keep it light.
- Filters (once only · cooldown and so on) and conditions are checked by the editor after
Fire.
Example: a condition and action that remember state
Section titled “Example: a condition and action that remember state”private readonly Dictionary<string, string> _quests = new(); // player:quest → state
// Condition: quest stateSimulate: s => Get(s.Player, s.Str("quest")) == s.Str("state", "none")
// Action: start questSimulate: s => { _quests[(s.Player ?? "") + ":" + s.Str("quest")] = "active"; s.Log("시작"); return true; }
// clear in a new worldhost.WorldChanged += _ => _quests.Clear();The full code is in Sample: event_example.
Naming rules
Section titled “Naming rules”| Rule | Why |
|---|---|
Prefix Type with your own name (myquest_start). | If the same type already exists in the same role (including built-ins), registration throws → the plugin is isolated |
Use lowercase letters, digits and _. | Easier to match with the server plugin and JSON. |
Never change a Type once used. | Saved rules use that name. |
Do not use built-in type names (cinematic · enter · sound …). | If it collides with a built-in, the plugin does not enable. The built-in list is in World logic type list. |
When the plugin is disabled
Section titled “When the plugin is disabled”- The plugin’s types are removed from the menus and the graph palette.
- Rules that use those types are not deleted. They get an Unknown type warning, and their field values are kept.
- In the preview, a rule with an unknown trigger is skipped, an unknown condition is false, and an unknown action is skipped on its own.
Server side
Section titled “Server side”The server plugin NP Scene (Paper) runs the rules in game. Editor plugins do not run on the server.
- Write a handler for the same type as a Paper plugin.
- Get NP Scene’s
SceneEventsApifrom Bukkit’sServicesManagerand register the type. - Match the field names and value formats (JSON) exactly with the editor-side
NpSceneField.
The server skips unknown types with a single warning (trigger = rule skipped, condition = false, action = only that action skipped). So the map still opens without a server handler, but that rule does not run.