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 +1 -1
- package/bin/mcp.js +2 -2
- package/lib/manifest.js +38 -4
- package/package.json +1 -1
- package/skill/ITEM_FORMAT.md +27 -4
- package/skill/SKILL.md +8 -6
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
|
|
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)
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|
package/skill/ITEM_FORMAT.md
CHANGED
|
@@ -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
|
-
| `
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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
|
|
96
|
+
## Editing an item that is already imported
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|