itemscore-helper 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js CHANGED
@@ -20,7 +20,7 @@ const SETUP_PROMPT = [
20
20
  "",
21
21
  "2. Read " + LLMS_URL + " so you know the ItemsCore item format and every method available.",
22
22
  "",
23
- "3. Tell me you are ready, then ask what item I want. Build a valid .import file (clean item JSON), validate it, and tell me to drop it in plugins/ItemsCore/imports/ and run /ic import in-game. The item stays fully editable in the in-game editor.",
23
+ "3. Tell me you are ready, then ask what item I want. Build the clean item JSON, validate it with the validate_item tool, and save it as a file ending in .import (for example flame_sword.import) - never .item. Tell me to drop it in plugins/ItemsCore/imports/ and run /ic import in-game. To change an item I already imported, edit the JSON and import it again with the same name (it overwrites the existing item). The item stays fully editable in the in-game editor.",
24
24
  ].join("\n")
25
25
 
26
26
  function copyDir(src, dest) {
package/bin/mcp.js CHANGED
@@ -24,7 +24,7 @@ async function main() {
24
24
  { name: "itemscore", version: String(idx.manifest.pluginVersion || "1") },
25
25
  {
26
26
  instructions:
27
- "ItemsCore is a Minecraft (Bukkit/Spigot) plugin that lets server owners build custom RPG items with no Java. Use these tools to look up the scripting API, then author a clean item JSON (get_item_schema / generate_item_template), validate it (validate_item), and tell the user to drop it in plugins/ItemsCore/imports/ and run /ic import <file>. Items authored this way stay editable in the in-game GUI." +
27
+ "ItemsCore is a Minecraft (Bukkit/Spigot) plugin that lets server owners build custom RPG items with no Java. Use these tools to look up the scripting API, then author a clean item JSON (get_item_schema / generate_item_template) and validate it (validate_item). IMPORTANT: save the file with a .import extension, for example flame_sword.import - never .item (.item is the plugin's own saved-item format, the user must not author that). Tell the user to drop the .import file in plugins/ItemsCore/imports/ and run /ic import <name>. To change an item that is already imported, build the updated JSON with the SAME name and import it again - it overwrites the existing item and keeps its stats and recipe (run /ic export <name> first to get the current JSON if you do not have it). Items authored this way stay editable in the in-game GUI." +
28
28
  (idx.isBundled
29
29
  ? " (Using the bundled API snapshot. To match this server's exact API including addon methods, run /ic exportapi and point this server at the generated plugins/ItemsCore/itemscore-api.json via --manifest or the ITEMSCORE_API env var.)"
30
30
  : " (Using the live API manifest at " + idx.source + ".)"),
@@ -106,7 +106,7 @@ async function main() {
106
106
  {
107
107
  title: "Validate an ItemsCore item",
108
108
  description:
109
- "Validate a clean item JSON object against the ItemsCore schema. Reports errors (must fix) and warnings (likely fine). Run this before giving the user a .import file.",
109
+ "Validate a clean item JSON object against the ItemsCore schema (unknown methods, wrong argument count, wrong argument order/type). Reports errors (must fix) and warnings (likely fine). Run this before saving. Save the result as a .import file (for example flame_sword.import), never .item.",
110
110
  inputSchema: { item: z.unknown().describe("The clean item JSON object to validate") },
111
111
  },
112
112
  async ({ item }) => jsonResult(M.validateItem(idx, item))
package/lib/manifest.js CHANGED
@@ -289,6 +289,33 @@ function validateItem(idx, item) {
289
289
  }
290
290
  }
291
291
 
292
+ if (item.events !== undefined && item.customEvents === undefined) {
293
+ warnings.push('the custom-event field is named "customEvents", not "events"; rename it so the plugin reads it')
294
+ }
295
+
296
+ if (item.customEvents !== undefined) {
297
+ if (!Array.isArray(item.customEvents)) errors.push("customEvents must be an array")
298
+ else {
299
+ item.customEvents.forEach((ce, ci) => {
300
+ const cp = "customEvents[" + ci + "]"
301
+ if (typeof ce !== "object" || ce === null) {
302
+ errors.push(cp + " must be an object")
303
+ return
304
+ }
305
+ if (typeof ce.event !== "string" || ce.event.length === 0) {
306
+ errors.push(cp + '.event is required and must be a full Bukkit event class name, e.g. "org.bukkit.event.block.BlockBreakEvent"')
307
+ } else if (!ce.event.includes(".")) {
308
+ warnings.push(cp + '.event "' + ce.event + '" should be a fully-qualified class name like org.bukkit.event.block.BlockBreakEvent')
309
+ }
310
+ if (ce.cooldown !== undefined && typeof ce.cooldown !== "string" && typeof ce.cooldown !== "number") {
311
+ warnings.push(cp + '.cooldown should be a duration like "5s", "1m", "1h" or a number of seconds')
312
+ }
313
+ if (!Array.isArray(ce.steps)) errors.push(cp + ".steps must be an array")
314
+ else ce.steps.forEach((s, si) => validateStep(idx, s, cp + ".steps[" + si + "]", errors, warnings))
315
+ })
316
+ }
317
+ }
318
+
292
319
  if (item.stats !== undefined && !Array.isArray(item.stats)) errors.push("stats must be an array when present")
293
320
  if (item.lore !== undefined && !Array.isArray(item.lore)) errors.push("lore must be an array of strings when present")
294
321
  if (item.enchantments !== undefined && !Array.isArray(item.enchantments)) errors.push("enchantments must be an array when present")
@@ -314,8 +341,15 @@ function itemSchema(idx) {
314
341
  customModelData: "number",
315
342
  skullOwner: "string - player name for PLAYER_HEAD skins",
316
343
  stats: "object[] - stat modifiers (see editor)",
317
- actions: "Action[] - the behavior graph",
318
- events: "object[] - custom event definitions",
344
+ actions: "Action[] - the built-in trigger behavior graph",
345
+ customEvents: "object[] - react to ANY Bukkit event by its full class name (see customEvent)",
346
+ },
347
+ customEvent: {
348
+ event:
349
+ 'string (required) - the FULL Bukkit event class name, e.g. "org.bukkit.event.block.BlockBreakEvent", "org.bukkit.event.entity.EntityDeathEvent". The plugin checks it exists on import.',
350
+ cooldown: "optional - per-player reuse delay, same format as an action cooldown",
351
+ steps:
352
+ "Step[] - same step format as an action. Variables available: player (the player from the event), item, event (the fired Bukkit event), plus core/particles/values/api.",
319
353
  },
320
354
  action: {
321
355
  trigger: "string (required) - one of: " + idx.TRIGGER_NAMES.join(", "),
@@ -354,7 +388,7 @@ function generateItemTemplate(kind) {
354
388
  actions: [
355
389
  { trigger: "rightAction", needBlock: "BOTH", steps: [{ call: "core.broadcastMessage", args: ["A wand was cast!"], operatorToNext: "END" }] },
356
390
  ],
357
- events: [],
391
+ customEvents: [],
358
392
  }
359
393
  }
360
394
  return {
@@ -369,7 +403,7 @@ function generateItemTemplate(kind) {
369
403
  actions: [
370
404
  { trigger: "leftAction", needBlock: "BOTH", steps: [{ call: "core.broadcastMessage", args: ["Hello from my custom sword!"], operatorToNext: "END" }] },
371
405
  ],
372
- events: [],
406
+ customEvents: [],
373
407
  }
374
408
  }
375
409
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itemscore-helper",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "One command sets up any AI (Claude, Codex, Cursor, Gemini, and others) to build and edit custom Minecraft items for the ItemsCore plugin. Auto-detects your AI tools and connects a local MCP server that runs entirely on your machine.",
5
5
  "bin": {
6
6
  "itemscore-helper": "bin/cli.js",
@@ -18,8 +18,8 @@ This is the offline reference for the item format ItemsCore imports. When the li
18
18
  | `customModelData` | number | Resource-pack model id |
19
19
  | `skullOwner` | string | Player name, for `PLAYER_HEAD` skins |
20
20
  | `stats` | object[] | Stat modifiers (authored in the editor; preserved on re-import) |
21
- | `actions` | Action[] | The behavior graph (see below) |
22
- | `events` | object[] | Custom event definitions |
21
+ | `actions` | Action[] | The built-in trigger behavior graph (see below) |
22
+ | `customEvents` | object[] | React to ANY Bukkit event by its full class name (see Custom events) |
23
23
 
24
24
  ## Action
25
25
 
@@ -112,6 +112,29 @@ For most items, every step uses `END`. Operators other than `END` build a single
112
112
  | `landLocation` | arrowLandAction |
113
113
  | `lastLocation` | projectile hit events |
114
114
 
115
+ ## Custom events
116
+
117
+ `actions` only cover the built-in triggers above. To react to ANY other Bukkit event, add a `customEvents` entry with the event's full class name. The plugin checks the class exists on import and blocks the import if it does not.
118
+
119
+ ```json
120
+ "customEvents": [
121
+ {
122
+ "event": "org.bukkit.event.block.BlockBreakEvent",
123
+ "steps": [
124
+ { "call": "core.sendColorMessage", "args": [ { "var": "player" }, "&aYou broke a block while holding this!" ], "operatorToNext": "END" }
125
+ ]
126
+ }
127
+ ]
128
+ ```
129
+
130
+ | Field | Type | Notes |
131
+ |---|---|---|
132
+ | `event` | string, required | The FULL Bukkit event class, e.g. `org.bukkit.event.block.BlockBreakEvent`, `org.bukkit.event.entity.EntityDeathEvent`, `org.bukkit.event.player.PlayerInteractEvent` |
133
+ | `cooldown` | duration string | Optional, same format as an action cooldown |
134
+ | `steps` | Step[] | Same step format as an action (below) |
135
+
136
+ Variables in a custom event step: `player` (the player the plugin finds on the event), `item`, `event` (the fired event itself - read its data with calls like `{ "call": "event.getBlock", "args": [] }`), plus `core` / `particles` / `values` / `api`. A custom event only fires while the player has the item (held, worn, or anywhere in the inventory for talismans). Use the exact class name; do not guess the package.
137
+
115
138
  ## Bukkit objects expose their full Spigot API
116
139
 
117
140
  `player`, `shooter`, `victim`, `arrow`, `event`, and any entity, block, world, location, or `ItemStack` returned by a method are real Bukkit/Spigot objects. You can call any standard Spigot method on them in a step, not only the ItemsCore methods:
@@ -160,7 +183,7 @@ Always confirm a method's exact name and parameters with `get_method` / `search_
160
183
  ]
161
184
  }
162
185
  ],
163
- "events": []
186
+ "customEvents": []
164
187
  }
165
188
  ```
166
189
 
@@ -184,7 +207,7 @@ Always confirm a method's exact name and parameters with `get_method` / `search_
184
207
  ]
185
208
  }
186
209
  ],
187
- "events": []
210
+ "customEvents": []
188
211
  }
189
212
  ```
190
213
 
package/skill/SKILL.md CHANGED
@@ -53,7 +53,7 @@ Minimal shape:
53
53
  ]
54
54
  }
55
55
  ],
56
- "events": []
56
+ "customEvents": []
57
57
  }
58
58
  ```
59
59
 
@@ -64,6 +64,7 @@ Rules:
64
64
  - Each step has a `call`, an `args` array, and `operatorToNext`. `call` is `core.method`, `particles.method`, a Bukkit call like `player.getLocation`, or a bare variable name to read it.
65
65
  - An arg is a JSON literal, `{ "var": "player" }` to pass a variable, or a nested `{ "call": ..., "args": ... }` to pass one method's result into another.
66
66
  - `operatorToNext` joins a step to the next one. Use `END` to end a statement. Other values (`ADD`, `EQUALS`, `AND`, ...) build expressions and conditions. See `ITEM_FORMAT.md`.
67
+ - `actions` only cover the built-in triggers. To react to any OTHER Bukkit event, add a `customEvents` entry with the event's full class name (e.g. `org.bukkit.event.block.BlockBreakEvent`) and the same `steps` format - variables are `player`, `item`, `event`. The import is blocked if the class name is not found on the server, so use the exact fully-qualified name.
67
68
 
68
69
  Find the exact method you need with `search_methods` / `get_method` before using it. Do not invent method names.
69
70
 
@@ -86,17 +87,18 @@ Run `validate_item` on your JSON. Fix every entry under `errors` before continui
86
87
 
87
88
  ## Step 4: Hand it to the user
88
89
 
89
- 1. Save the JSON as `<name>.import` (or `<name>.json`).
90
+ 1. Save the JSON with a `.import` extension, for example `flame_sword.import`. **Always use `.import`, never `.item`.** `.item` is the plugin's own internal saved-item format - if you name the file `.item` the user will be confused and the import flow will not pick it up correctly. (The file is plain JSON; the `.import` extension just marks that it belongs in the imports folder.)
90
91
  2. Tell the user to put the file in `plugins/ItemsCore/imports/` on their server.
91
92
  3. Tell them to run `/ic import <name>` in-game.
92
93
 
93
94
  On success the plugin replies: `Imported <name> (N action(s)). It is now live and GUI-editable.` The item is immediately usable and can be opened in the editor with `/itemeditor <name>`.
94
95
 
95
- ## Editing an existing item
96
+ ## Editing an item that is already imported
96
97
 
97
- 1. User runs `/ic export <name>` in-game. The plugin writes `plugins/ItemsCore/exports/<name>.json`.
98
- 2. User shares that file with you. Edit the clean JSON.
99
- 3. Validate, then re-import the same way. Importing by the same `name` updates the item and preserves its stats, recipe, and attributes.
98
+ Items are identified by their `name`. **To change an item that already exists, do not start over - update it in place:**
99
+
100
+ - **Behavior / actions (abilities, particles, projectiles, stats):** build the updated clean JSON with the **same `name`** and import it again exactly like a new item (save as `<name>.import`, `/ic import <name>`). Importing over an existing name **overwrites** the item and keeps its stats, recipe, and attributes. If you do not already have the item's JSON, have the user run `/ic export <name>` first - the plugin writes `plugins/ItemsCore/exports/<name>.json`, which you edit and re-import. Do **not** hand-edit the stored `plugins/ItemsCore/items/<name>.item` file for behavior changes: its `code` and `actions` are machine-encoded (strings as char codes, shared YAML anchors) and editing them by hand will corrupt the item.
101
+ - **Cosmetic only (display name, lore, material, custom model data, skull owner):** these top fields *are* plain text in the stored item file. You may edit `plugins/ItemsCore/items/<name>.item` directly and tell the user to run `/ic reload <name>` to apply it. For anything touching `code` or `actions`, use the re-import flow above instead.
100
102
 
101
103
  If the item is a **legacy code-only item** (made before this format, so the editor cannot open it), have the user run `/ic adopt <name>` first. That converts its code into GUI actions; then export, edit, and re-import as above.
102
104