@rpgm-tools/neo-angband-mod-sdk 0.25.0 → 0.26.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/dist/capabilities.d.ts +71 -1
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +156 -0
- package/dist/capabilities.js.map +1 -1
- package/package.json +1 -1
- package/src/capabilities.ts +160 -2
package/dist/capabilities.d.ts
CHANGED
|
@@ -131,6 +131,70 @@
|
|
|
131
131
|
* write to it succeeded or failed. Its own kind,
|
|
132
132
|
* like "display:replace" - there is nothing to
|
|
133
133
|
* range over, so it has no wildcard either.
|
|
134
|
+
* - "mod:install" - install a CONTENT mod from archive bytes the mod
|
|
135
|
+
* holds in memory, through the same door the
|
|
136
|
+
* player's own zip import uses. Its own kind, like
|
|
137
|
+
* "display:replace": there is nothing to range over,
|
|
138
|
+
* so it has no wildcard. Two things bound it, and
|
|
139
|
+
* they are what make it proportionate rather than
|
|
140
|
+
* total. An archive that ships CODE, or whose
|
|
141
|
+
* manifest asks for any capability, is refused - so
|
|
142
|
+
* this grants "may add records to my library", not
|
|
143
|
+
* "may write and deploy a program". And an install
|
|
144
|
+
* is not an ENABLE: what arrives is switched off, and
|
|
145
|
+
* the player is shown its own capability list before
|
|
146
|
+
* any of it runs, which is what stops one grant
|
|
147
|
+
* turning into every grant.
|
|
148
|
+
* - "mod:session" - load a CONTENT mod into this session only, so it
|
|
149
|
+
* composes into the game on the next reload and is
|
|
150
|
+
* gone with the window. Bounded exactly as
|
|
151
|
+
* "mod:install" is - code and capability requests are
|
|
152
|
+
* refused - and deliberately NOT the same grant, since
|
|
153
|
+
* an install waits to be switched on and this does not.
|
|
154
|
+
* `grantCovers` compares the action so neither can be
|
|
155
|
+
* sold under the other's sentence. It is a shorter
|
|
156
|
+
* LIFETIME for the archive and not a smaller reach for
|
|
157
|
+
* the records: what composes, composes.
|
|
158
|
+
* - "debug:spawn" - conjure an item or a creature into the live game the
|
|
159
|
+
* way the debug commands do. Its own kind and no
|
|
160
|
+
* wildcard, and here that is the whole point rather
|
|
161
|
+
* than a consequence of there being one string: this
|
|
162
|
+
* is the grant a player is most likely to want to
|
|
163
|
+
* check for by name, so it must never arrive as part
|
|
164
|
+
* of something broader. What it adds over what a
|
|
165
|
+
* plugin can already reach through `ctx.core` is not
|
|
166
|
+
* the ability - `wizCreateObj` and friends take a
|
|
167
|
+
* `debug` flag the CALLER supplies - but the mark: the
|
|
168
|
+
* character is asked about and flagged, through the
|
|
169
|
+
* game's own confirmation and before anything is
|
|
170
|
+
* placed, so "the debug commands cannot be scored"
|
|
171
|
+
* stays true with a mod in the picture.
|
|
172
|
+
* - "debug:wizard" - drive the WHOLE debug set, not just the two spawns:
|
|
173
|
+
* depth, experience, gold, stats, acquirement,
|
|
174
|
+
* mapping. Its own action beside "spawn" rather than a
|
|
175
|
+
* wider reading of it, because the two cost the player
|
|
176
|
+
* different things. Spawning happens to the character
|
|
177
|
+
* they are playing and costs them the score. This one
|
|
178
|
+
* refuses to run until the session has been cut loose
|
|
179
|
+
* from its save slot - one way, checked by the host on
|
|
180
|
+
* every call - so it costs them the session and leaves
|
|
181
|
+
* the character on disk untouched. Neither is a
|
|
182
|
+
* superset of the other and `grantCovers` compares the
|
|
183
|
+
* action, so one consent cannot buy both.
|
|
184
|
+
* - "ui:panel.mount" - draw with real HTML instead of the character grid: a
|
|
185
|
+
* panel of the mod's own, mounted on the page above
|
|
186
|
+
* the game. A THIRD "ui:" action, and the reason it is
|
|
187
|
+
* not a third region name is the same reason
|
|
188
|
+
* "ui:region.create" is not a seventh: the sentence a
|
|
189
|
+
* player agrees to is different. "replace" hands over
|
|
190
|
+
* something the game draws, "create" adds a rectangle
|
|
191
|
+
* of the game's own character grid, and this one puts
|
|
192
|
+
* a piece of web page on top - which can look like the
|
|
193
|
+
* game's own interface, style anything inside itself,
|
|
194
|
+
* and read what the player types into it. No wildcard,
|
|
195
|
+
* and "ui:*.replace" does not cover it: `grantCovers`
|
|
196
|
+
* compares the action, so a mod that may redraw the
|
|
197
|
+
* vitals still cannot mount a panel without asking.
|
|
134
198
|
* - "ui:region.create" - ADD a rectangle of your own to the player's screen
|
|
135
199
|
* (ModPlugin.regions), rather than take one of the
|
|
136
200
|
* game's. The only "ui:" capability whose ACTION is
|
|
@@ -176,10 +240,16 @@ export type ParsedCapability = {
|
|
|
176
240
|
} | {
|
|
177
241
|
kind: "ui";
|
|
178
242
|
region: string;
|
|
179
|
-
action: "replace" | "create";
|
|
243
|
+
action: "replace" | "create" | "mount";
|
|
180
244
|
} | {
|
|
181
245
|
kind: "backup";
|
|
182
246
|
action: "folder";
|
|
247
|
+
} | {
|
|
248
|
+
kind: "mod";
|
|
249
|
+
action: "install" | "session";
|
|
250
|
+
} | {
|
|
251
|
+
kind: "debug";
|
|
252
|
+
action: "spawn" | "wizard";
|
|
183
253
|
};
|
|
184
254
|
/**
|
|
185
255
|
* Parse and validate a capability string against the vocabulary above,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuNG;AAEH,OAAO,EAAY,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAE5D,qBAAa,eAAgB,SAAQ,KAAK;CAAG;AAE7C,2DAA2D;AAC3D,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,KAAK,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAA;CAAE,CAAC;AAyElD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CA2F7D;AAoFD;;;;GAIG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAC3C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAE1C,OAAO;IAUP;;;;;;;OAOG;IACH,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,YAAY,GAAG,aAAa;IAgB1D;;;;OAIG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAKzB;;;;;OAKG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAOxB,0EAA0E;IAC1E,kBAAkB,IAAI,OAAO;IAI7B,6DAA6D;IAC7D,iBAAiB,IAAI,OAAO;CAG7B"}
|
package/dist/capabilities.js
CHANGED
|
@@ -131,6 +131,70 @@
|
|
|
131
131
|
* write to it succeeded or failed. Its own kind,
|
|
132
132
|
* like "display:replace" - there is nothing to
|
|
133
133
|
* range over, so it has no wildcard either.
|
|
134
|
+
* - "mod:install" - install a CONTENT mod from archive bytes the mod
|
|
135
|
+
* holds in memory, through the same door the
|
|
136
|
+
* player's own zip import uses. Its own kind, like
|
|
137
|
+
* "display:replace": there is nothing to range over,
|
|
138
|
+
* so it has no wildcard. Two things bound it, and
|
|
139
|
+
* they are what make it proportionate rather than
|
|
140
|
+
* total. An archive that ships CODE, or whose
|
|
141
|
+
* manifest asks for any capability, is refused - so
|
|
142
|
+
* this grants "may add records to my library", not
|
|
143
|
+
* "may write and deploy a program". And an install
|
|
144
|
+
* is not an ENABLE: what arrives is switched off, and
|
|
145
|
+
* the player is shown its own capability list before
|
|
146
|
+
* any of it runs, which is what stops one grant
|
|
147
|
+
* turning into every grant.
|
|
148
|
+
* - "mod:session" - load a CONTENT mod into this session only, so it
|
|
149
|
+
* composes into the game on the next reload and is
|
|
150
|
+
* gone with the window. Bounded exactly as
|
|
151
|
+
* "mod:install" is - code and capability requests are
|
|
152
|
+
* refused - and deliberately NOT the same grant, since
|
|
153
|
+
* an install waits to be switched on and this does not.
|
|
154
|
+
* `grantCovers` compares the action so neither can be
|
|
155
|
+
* sold under the other's sentence. It is a shorter
|
|
156
|
+
* LIFETIME for the archive and not a smaller reach for
|
|
157
|
+
* the records: what composes, composes.
|
|
158
|
+
* - "debug:spawn" - conjure an item or a creature into the live game the
|
|
159
|
+
* way the debug commands do. Its own kind and no
|
|
160
|
+
* wildcard, and here that is the whole point rather
|
|
161
|
+
* than a consequence of there being one string: this
|
|
162
|
+
* is the grant a player is most likely to want to
|
|
163
|
+
* check for by name, so it must never arrive as part
|
|
164
|
+
* of something broader. What it adds over what a
|
|
165
|
+
* plugin can already reach through `ctx.core` is not
|
|
166
|
+
* the ability - `wizCreateObj` and friends take a
|
|
167
|
+
* `debug` flag the CALLER supplies - but the mark: the
|
|
168
|
+
* character is asked about and flagged, through the
|
|
169
|
+
* game's own confirmation and before anything is
|
|
170
|
+
* placed, so "the debug commands cannot be scored"
|
|
171
|
+
* stays true with a mod in the picture.
|
|
172
|
+
* - "debug:wizard" - drive the WHOLE debug set, not just the two spawns:
|
|
173
|
+
* depth, experience, gold, stats, acquirement,
|
|
174
|
+
* mapping. Its own action beside "spawn" rather than a
|
|
175
|
+
* wider reading of it, because the two cost the player
|
|
176
|
+
* different things. Spawning happens to the character
|
|
177
|
+
* they are playing and costs them the score. This one
|
|
178
|
+
* refuses to run until the session has been cut loose
|
|
179
|
+
* from its save slot - one way, checked by the host on
|
|
180
|
+
* every call - so it costs them the session and leaves
|
|
181
|
+
* the character on disk untouched. Neither is a
|
|
182
|
+
* superset of the other and `grantCovers` compares the
|
|
183
|
+
* action, so one consent cannot buy both.
|
|
184
|
+
* - "ui:panel.mount" - draw with real HTML instead of the character grid: a
|
|
185
|
+
* panel of the mod's own, mounted on the page above
|
|
186
|
+
* the game. A THIRD "ui:" action, and the reason it is
|
|
187
|
+
* not a third region name is the same reason
|
|
188
|
+
* "ui:region.create" is not a seventh: the sentence a
|
|
189
|
+
* player agrees to is different. "replace" hands over
|
|
190
|
+
* something the game draws, "create" adds a rectangle
|
|
191
|
+
* of the game's own character grid, and this one puts
|
|
192
|
+
* a piece of web page on top - which can look like the
|
|
193
|
+
* game's own interface, style anything inside itself,
|
|
194
|
+
* and read what the player types into it. No wildcard,
|
|
195
|
+
* and "ui:*.replace" does not cover it: `grantCovers`
|
|
196
|
+
* compares the action, so a mod that may redraw the
|
|
197
|
+
* vitals still cannot mount a panel without asking.
|
|
134
198
|
* - "ui:region.create" - ADD a rectangle of your own to the player's screen
|
|
135
199
|
* (ModPlugin.regions), rather than take one of the
|
|
136
200
|
* game's. The only "ui:" capability whose ACTION is
|
|
@@ -190,6 +254,34 @@ const UI_RE = /^ui:(\*|messages|sidebar|status|menu|screen)\.replace$/;
|
|
|
190
254
|
* the note there.
|
|
191
255
|
*/
|
|
192
256
|
const UI_CREATE_RE = /^ui:region\.create$/;
|
|
257
|
+
/**
|
|
258
|
+
* `ui:panel.mount` - draw with real HTML instead of the character grid.
|
|
259
|
+
*
|
|
260
|
+
* A THIRD ACTION, for the same reason `create` was a second one rather than a
|
|
261
|
+
* seventh region name: what the player is agreeing to is a different sentence.
|
|
262
|
+
* `replace` hands a mod something the game already draws. `create` gives it a
|
|
263
|
+
* rectangle of the game's own character grid, painted with the same seven
|
|
264
|
+
* methods every other surface has. This one puts a piece of WEB PAGE above the
|
|
265
|
+
* game - arbitrary markup and styling, which can be made to look exactly like
|
|
266
|
+
* the game's own interface, and which can hold a real text field and read what
|
|
267
|
+
* is typed into it.
|
|
268
|
+
*
|
|
269
|
+
* NO WILDCARD, for the reason `create` has none: a mod either may mount panels
|
|
270
|
+
* or it may not, and there is no set of panel names to range over, because a
|
|
271
|
+
* panel does not exist until the mod asks for one.
|
|
272
|
+
*
|
|
273
|
+
* AND `ui:*.replace` MUST NOT COVER IT. `grantCovers` compares the action as
|
|
274
|
+
* well as the region, so this holds by construction rather than by a rule
|
|
275
|
+
* somebody has to remember - which is exactly what that comparison was added
|
|
276
|
+
* for when `create` arrived.
|
|
277
|
+
*
|
|
278
|
+
* WHAT IT IS NOT. It is not a containment boundary and this module's header
|
|
279
|
+
* already says why: a plugin's code runs in the page's own realm and can reach
|
|
280
|
+
* the document with no capability at all. What this grant buys is a panel the
|
|
281
|
+
* HOST owns - one it can place, stack, take the keyboard back from and close -
|
|
282
|
+
* plus a sentence the player reads before any of it happens.
|
|
283
|
+
*/
|
|
284
|
+
const UI_MOUNT_RE = /^ui:panel\.mount$/;
|
|
193
285
|
const STATE_RE = /^state:(\*|[a-z][a-z0-9-]*)\.read$/;
|
|
194
286
|
const NETWORK_RE = /^network:(\*|[a-zA-Z0-9.-]+)$/;
|
|
195
287
|
/** The override domains ModRegistryHost gates, plus the "*" wildcard. */
|
|
@@ -220,6 +312,45 @@ export function parseCapability(cap) {
|
|
|
220
312
|
if (cap === "backup:folder") {
|
|
221
313
|
return { kind: "backup", action: "folder" };
|
|
222
314
|
}
|
|
315
|
+
/* "mod:install": hand archive bytes to the same install door the player's own
|
|
316
|
+
* zip import uses. Its own kind and no wildcard, same reasoning as the two
|
|
317
|
+
* above. What bounds it is not this grammar - it is that the door refuses an
|
|
318
|
+
* archive carrying code or asking for capabilities, and that installing is not
|
|
319
|
+
* enabling, so what arrives is consented to on its own terms before it runs. */
|
|
320
|
+
if (cap === "mod:install") {
|
|
321
|
+
return { kind: "mod", action: "install" };
|
|
322
|
+
}
|
|
323
|
+
/* "mod:session": load content into THIS session only, without it joining the
|
|
324
|
+
* library. Bounded the same way `mod:install` is - the door refuses an archive
|
|
325
|
+
* carrying code or asking for capabilities - and separated from it because the
|
|
326
|
+
* two say different things to a player. An install arrives switched OFF and waits
|
|
327
|
+
* to be turned on; a session load is on for the rest of the session as soon as
|
|
328
|
+
* the game reloads. That is more, not less, so it cannot be sold under the other
|
|
329
|
+
* one's sentence: `grantCovers` compares the action for exactly that reason. */
|
|
330
|
+
if (cap === "mod:session") {
|
|
331
|
+
return { kind: "mod", action: "session" };
|
|
332
|
+
}
|
|
333
|
+
/* "debug:spawn": put an item or a creature into the live game the way the debug
|
|
334
|
+
* commands do, marking the character the way they do. Its own kind and no
|
|
335
|
+
* wildcard on purpose - a player asking "which of my mods can conjure things"
|
|
336
|
+
* must be able to read the answer off one line, and no broader grant may ever
|
|
337
|
+
* carry this one along. */
|
|
338
|
+
if (cap === "debug:spawn") {
|
|
339
|
+
return { kind: "debug", action: "spawn" };
|
|
340
|
+
}
|
|
341
|
+
/* "debug:wizard": drive the whole debug set - depth, experience, gold, stats,
|
|
342
|
+
* acquirement, mapping - and not just the two spawns. A separate string from
|
|
343
|
+
* "debug:spawn" because it is a separate bargain, not a bigger helping of the
|
|
344
|
+
* same one. Spawning acts on the character the player is actually playing, after
|
|
345
|
+
* the game's own question, and leaves them playing it. This one refuses to run
|
|
346
|
+
* at all until the session has been cut loose from its save slot, one way, so
|
|
347
|
+
* the character it happens to has already stopped being written to disk. Neither
|
|
348
|
+
* is a superset of the other - one costs the score, the other costs the session -
|
|
349
|
+
* so `grantCovers` compares the action and neither can be sold under the other's
|
|
350
|
+
* sentence. */
|
|
351
|
+
if (cap === "debug:wizard") {
|
|
352
|
+
return { kind: "debug", action: "wizard" };
|
|
353
|
+
}
|
|
223
354
|
const ui = UI_RE.exec(cap);
|
|
224
355
|
if (ui) {
|
|
225
356
|
return { kind: "ui", region: ui[1], action: "replace" };
|
|
@@ -230,6 +361,12 @@ export function parseCapability(cap) {
|
|
|
230
361
|
if (UI_CREATE_RE.test(cap)) {
|
|
231
362
|
return { kind: "ui", region: "region", action: "create" };
|
|
232
363
|
}
|
|
364
|
+
/* `panel` is the region NAME, on the same terms `region` is above: the literal
|
|
365
|
+
* the author wrote, so one `kind: "ui"` arm keeps one shape. `action` is what
|
|
366
|
+
* separates the three, and `grantCovers` compares it. */
|
|
367
|
+
if (UI_MOUNT_RE.test(cap)) {
|
|
368
|
+
return { kind: "ui", region: "panel", action: "mount" };
|
|
369
|
+
}
|
|
233
370
|
const event = EVENT_RE.exec(cap);
|
|
234
371
|
if (event) {
|
|
235
372
|
return { kind: "event", name: event[1] };
|
|
@@ -278,6 +415,25 @@ function grantCovers(grant, request) {
|
|
|
278
415
|
/* Exact match only, same reasoning as "display" - there is exactly one
|
|
279
416
|
* backup capability and no wildcard grant could ever cover it. */
|
|
280
417
|
return grant.kind === "backup";
|
|
418
|
+
case "mod":
|
|
419
|
+
/* THE ACTION IS COMPARED, not just the kind, and it is the #261 lesson
|
|
420
|
+
* applied before it could be re-learned here: `mod:install` puts a pack in
|
|
421
|
+
* the library switched off, `mod:session` switches one on for the rest of the
|
|
422
|
+
* session. Neither is a superset of the other, there is no wildcard, and a
|
|
423
|
+
* kind-only comparison would have let one consent buy both. */
|
|
424
|
+
return grant.kind === "mod" && grant.action === request.action;
|
|
425
|
+
case "debug":
|
|
426
|
+
/* Exact match only, and here that is the point rather than a consequence:
|
|
427
|
+
* this is the grant a player is most likely to check for by name, so it
|
|
428
|
+
* must never be reachable through anything wider.
|
|
429
|
+
*
|
|
430
|
+
* THE ACTION IS COMPARED, and until "debug:wizard" landed it was not - which
|
|
431
|
+
* was invisible while "spawn" was the only action, and would have been a real
|
|
432
|
+
* hole the moment a second one existed: a player who agreed to a mod
|
|
433
|
+
* conjuring one monster would have been granting it the depth jumps, the
|
|
434
|
+
* experience and the acquirement too. Exactly the #261 lesson, caught by
|
|
435
|
+
* adding the second action rather than by shipping it. */
|
|
436
|
+
return grant.kind === "debug" && grant.action === request.action;
|
|
281
437
|
case "ui":
|
|
282
438
|
/* Per region, with one wildcard. A `display` grant is NOT accepted here
|
|
283
439
|
* and a `ui` grant is not accepted above: owning the dungeon and owning
|
package/dist/capabilities.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpgm-tools/neo-angband-mod-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Pack schemas, validation, and tooling for Neo Angband mods (content packs, tile packs, scripted plugins)",
|
|
5
5
|
"license": "GPL-2.0-only",
|
|
6
6
|
"author": "neostryder (RPGM Tools)",
|
package/src/capabilities.ts
CHANGED
|
@@ -131,6 +131,70 @@
|
|
|
131
131
|
* write to it succeeded or failed. Its own kind,
|
|
132
132
|
* like "display:replace" - there is nothing to
|
|
133
133
|
* range over, so it has no wildcard either.
|
|
134
|
+
* - "mod:install" - install a CONTENT mod from archive bytes the mod
|
|
135
|
+
* holds in memory, through the same door the
|
|
136
|
+
* player's own zip import uses. Its own kind, like
|
|
137
|
+
* "display:replace": there is nothing to range over,
|
|
138
|
+
* so it has no wildcard. Two things bound it, and
|
|
139
|
+
* they are what make it proportionate rather than
|
|
140
|
+
* total. An archive that ships CODE, or whose
|
|
141
|
+
* manifest asks for any capability, is refused - so
|
|
142
|
+
* this grants "may add records to my library", not
|
|
143
|
+
* "may write and deploy a program". And an install
|
|
144
|
+
* is not an ENABLE: what arrives is switched off, and
|
|
145
|
+
* the player is shown its own capability list before
|
|
146
|
+
* any of it runs, which is what stops one grant
|
|
147
|
+
* turning into every grant.
|
|
148
|
+
* - "mod:session" - load a CONTENT mod into this session only, so it
|
|
149
|
+
* composes into the game on the next reload and is
|
|
150
|
+
* gone with the window. Bounded exactly as
|
|
151
|
+
* "mod:install" is - code and capability requests are
|
|
152
|
+
* refused - and deliberately NOT the same grant, since
|
|
153
|
+
* an install waits to be switched on and this does not.
|
|
154
|
+
* `grantCovers` compares the action so neither can be
|
|
155
|
+
* sold under the other's sentence. It is a shorter
|
|
156
|
+
* LIFETIME for the archive and not a smaller reach for
|
|
157
|
+
* the records: what composes, composes.
|
|
158
|
+
* - "debug:spawn" - conjure an item or a creature into the live game the
|
|
159
|
+
* way the debug commands do. Its own kind and no
|
|
160
|
+
* wildcard, and here that is the whole point rather
|
|
161
|
+
* than a consequence of there being one string: this
|
|
162
|
+
* is the grant a player is most likely to want to
|
|
163
|
+
* check for by name, so it must never arrive as part
|
|
164
|
+
* of something broader. What it adds over what a
|
|
165
|
+
* plugin can already reach through `ctx.core` is not
|
|
166
|
+
* the ability - `wizCreateObj` and friends take a
|
|
167
|
+
* `debug` flag the CALLER supplies - but the mark: the
|
|
168
|
+
* character is asked about and flagged, through the
|
|
169
|
+
* game's own confirmation and before anything is
|
|
170
|
+
* placed, so "the debug commands cannot be scored"
|
|
171
|
+
* stays true with a mod in the picture.
|
|
172
|
+
* - "debug:wizard" - drive the WHOLE debug set, not just the two spawns:
|
|
173
|
+
* depth, experience, gold, stats, acquirement,
|
|
174
|
+
* mapping. Its own action beside "spawn" rather than a
|
|
175
|
+
* wider reading of it, because the two cost the player
|
|
176
|
+
* different things. Spawning happens to the character
|
|
177
|
+
* they are playing and costs them the score. This one
|
|
178
|
+
* refuses to run until the session has been cut loose
|
|
179
|
+
* from its save slot - one way, checked by the host on
|
|
180
|
+
* every call - so it costs them the session and leaves
|
|
181
|
+
* the character on disk untouched. Neither is a
|
|
182
|
+
* superset of the other and `grantCovers` compares the
|
|
183
|
+
* action, so one consent cannot buy both.
|
|
184
|
+
* - "ui:panel.mount" - draw with real HTML instead of the character grid: a
|
|
185
|
+
* panel of the mod's own, mounted on the page above
|
|
186
|
+
* the game. A THIRD "ui:" action, and the reason it is
|
|
187
|
+
* not a third region name is the same reason
|
|
188
|
+
* "ui:region.create" is not a seventh: the sentence a
|
|
189
|
+
* player agrees to is different. "replace" hands over
|
|
190
|
+
* something the game draws, "create" adds a rectangle
|
|
191
|
+
* of the game's own character grid, and this one puts
|
|
192
|
+
* a piece of web page on top - which can look like the
|
|
193
|
+
* game's own interface, style anything inside itself,
|
|
194
|
+
* and read what the player types into it. No wildcard,
|
|
195
|
+
* and "ui:*.replace" does not cover it: `grantCovers`
|
|
196
|
+
* compares the action, so a mod that may redraw the
|
|
197
|
+
* vitals still cannot mount a panel without asking.
|
|
134
198
|
* - "ui:region.create" - ADD a rectangle of your own to the player's screen
|
|
135
199
|
* (ModPlugin.regions), rather than take one of the
|
|
136
200
|
* game's. The only "ui:" capability whose ACTION is
|
|
@@ -163,8 +227,10 @@ export type ParsedCapability =
|
|
|
163
227
|
| { kind: "network"; host: string }
|
|
164
228
|
| { kind: "registry"; domain: string }
|
|
165
229
|
| { kind: "display"; action: "replace" }
|
|
166
|
-
| { kind: "ui"; region: string; action: "replace" | "create" }
|
|
167
|
-
| { kind: "backup"; action: "folder" }
|
|
230
|
+
| { kind: "ui"; region: string; action: "replace" | "create" | "mount" }
|
|
231
|
+
| { kind: "backup"; action: "folder" }
|
|
232
|
+
| { kind: "mod"; action: "install" | "session" }
|
|
233
|
+
| { kind: "debug"; action: "spawn" | "wizard" };
|
|
168
234
|
|
|
169
235
|
const EVENT_RE = /^event:([a-z][a-z0-9-]*)$/;
|
|
170
236
|
/**
|
|
@@ -203,6 +269,34 @@ const UI_RE = /^ui:(\*|messages|sidebar|status|menu|screen)\.replace$/;
|
|
|
203
269
|
* the note there.
|
|
204
270
|
*/
|
|
205
271
|
const UI_CREATE_RE = /^ui:region\.create$/;
|
|
272
|
+
/**
|
|
273
|
+
* `ui:panel.mount` - draw with real HTML instead of the character grid.
|
|
274
|
+
*
|
|
275
|
+
* A THIRD ACTION, for the same reason `create` was a second one rather than a
|
|
276
|
+
* seventh region name: what the player is agreeing to is a different sentence.
|
|
277
|
+
* `replace` hands a mod something the game already draws. `create` gives it a
|
|
278
|
+
* rectangle of the game's own character grid, painted with the same seven
|
|
279
|
+
* methods every other surface has. This one puts a piece of WEB PAGE above the
|
|
280
|
+
* game - arbitrary markup and styling, which can be made to look exactly like
|
|
281
|
+
* the game's own interface, and which can hold a real text field and read what
|
|
282
|
+
* is typed into it.
|
|
283
|
+
*
|
|
284
|
+
* NO WILDCARD, for the reason `create` has none: a mod either may mount panels
|
|
285
|
+
* or it may not, and there is no set of panel names to range over, because a
|
|
286
|
+
* panel does not exist until the mod asks for one.
|
|
287
|
+
*
|
|
288
|
+
* AND `ui:*.replace` MUST NOT COVER IT. `grantCovers` compares the action as
|
|
289
|
+
* well as the region, so this holds by construction rather than by a rule
|
|
290
|
+
* somebody has to remember - which is exactly what that comparison was added
|
|
291
|
+
* for when `create` arrived.
|
|
292
|
+
*
|
|
293
|
+
* WHAT IT IS NOT. It is not a containment boundary and this module's header
|
|
294
|
+
* already says why: a plugin's code runs in the page's own realm and can reach
|
|
295
|
+
* the document with no capability at all. What this grant buys is a panel the
|
|
296
|
+
* HOST owns - one it can place, stack, take the keyboard back from and close -
|
|
297
|
+
* plus a sentence the player reads before any of it happens.
|
|
298
|
+
*/
|
|
299
|
+
const UI_MOUNT_RE = /^ui:panel\.mount$/;
|
|
206
300
|
const STATE_RE = /^state:(\*|[a-z][a-z0-9-]*)\.read$/;
|
|
207
301
|
const NETWORK_RE = /^network:(\*|[a-zA-Z0-9.-]+)$/;
|
|
208
302
|
/** The override domains ModRegistryHost gates, plus the "*" wildcard. */
|
|
@@ -235,6 +329,45 @@ export function parseCapability(cap: string): ParsedCapability {
|
|
|
235
329
|
if (cap === "backup:folder") {
|
|
236
330
|
return { kind: "backup", action: "folder" };
|
|
237
331
|
}
|
|
332
|
+
/* "mod:install": hand archive bytes to the same install door the player's own
|
|
333
|
+
* zip import uses. Its own kind and no wildcard, same reasoning as the two
|
|
334
|
+
* above. What bounds it is not this grammar - it is that the door refuses an
|
|
335
|
+
* archive carrying code or asking for capabilities, and that installing is not
|
|
336
|
+
* enabling, so what arrives is consented to on its own terms before it runs. */
|
|
337
|
+
if (cap === "mod:install") {
|
|
338
|
+
return { kind: "mod", action: "install" };
|
|
339
|
+
}
|
|
340
|
+
/* "mod:session": load content into THIS session only, without it joining the
|
|
341
|
+
* library. Bounded the same way `mod:install` is - the door refuses an archive
|
|
342
|
+
* carrying code or asking for capabilities - and separated from it because the
|
|
343
|
+
* two say different things to a player. An install arrives switched OFF and waits
|
|
344
|
+
* to be turned on; a session load is on for the rest of the session as soon as
|
|
345
|
+
* the game reloads. That is more, not less, so it cannot be sold under the other
|
|
346
|
+
* one's sentence: `grantCovers` compares the action for exactly that reason. */
|
|
347
|
+
if (cap === "mod:session") {
|
|
348
|
+
return { kind: "mod", action: "session" };
|
|
349
|
+
}
|
|
350
|
+
/* "debug:spawn": put an item or a creature into the live game the way the debug
|
|
351
|
+
* commands do, marking the character the way they do. Its own kind and no
|
|
352
|
+
* wildcard on purpose - a player asking "which of my mods can conjure things"
|
|
353
|
+
* must be able to read the answer off one line, and no broader grant may ever
|
|
354
|
+
* carry this one along. */
|
|
355
|
+
if (cap === "debug:spawn") {
|
|
356
|
+
return { kind: "debug", action: "spawn" };
|
|
357
|
+
}
|
|
358
|
+
/* "debug:wizard": drive the whole debug set - depth, experience, gold, stats,
|
|
359
|
+
* acquirement, mapping - and not just the two spawns. A separate string from
|
|
360
|
+
* "debug:spawn" because it is a separate bargain, not a bigger helping of the
|
|
361
|
+
* same one. Spawning acts on the character the player is actually playing, after
|
|
362
|
+
* the game's own question, and leaves them playing it. This one refuses to run
|
|
363
|
+
* at all until the session has been cut loose from its save slot, one way, so
|
|
364
|
+
* the character it happens to has already stopped being written to disk. Neither
|
|
365
|
+
* is a superset of the other - one costs the score, the other costs the session -
|
|
366
|
+
* so `grantCovers` compares the action and neither can be sold under the other's
|
|
367
|
+
* sentence. */
|
|
368
|
+
if (cap === "debug:wizard") {
|
|
369
|
+
return { kind: "debug", action: "wizard" };
|
|
370
|
+
}
|
|
238
371
|
const ui = UI_RE.exec(cap);
|
|
239
372
|
if (ui) {
|
|
240
373
|
return { kind: "ui", region: ui[1] as string, action: "replace" };
|
|
@@ -245,6 +378,12 @@ export function parseCapability(cap: string): ParsedCapability {
|
|
|
245
378
|
if (UI_CREATE_RE.test(cap)) {
|
|
246
379
|
return { kind: "ui", region: "region", action: "create" };
|
|
247
380
|
}
|
|
381
|
+
/* `panel` is the region NAME, on the same terms `region` is above: the literal
|
|
382
|
+
* the author wrote, so one `kind: "ui"` arm keeps one shape. `action` is what
|
|
383
|
+
* separates the three, and `grantCovers` compares it. */
|
|
384
|
+
if (UI_MOUNT_RE.test(cap)) {
|
|
385
|
+
return { kind: "ui", region: "panel", action: "mount" };
|
|
386
|
+
}
|
|
248
387
|
const event = EVENT_RE.exec(cap);
|
|
249
388
|
if (event) {
|
|
250
389
|
return { kind: "event", name: event[1] as string };
|
|
@@ -300,6 +439,25 @@ function grantCovers(grant: ParsedCapability, request: ParsedCapability): boolea
|
|
|
300
439
|
/* Exact match only, same reasoning as "display" - there is exactly one
|
|
301
440
|
* backup capability and no wildcard grant could ever cover it. */
|
|
302
441
|
return grant.kind === "backup";
|
|
442
|
+
case "mod":
|
|
443
|
+
/* THE ACTION IS COMPARED, not just the kind, and it is the #261 lesson
|
|
444
|
+
* applied before it could be re-learned here: `mod:install` puts a pack in
|
|
445
|
+
* the library switched off, `mod:session` switches one on for the rest of the
|
|
446
|
+
* session. Neither is a superset of the other, there is no wildcard, and a
|
|
447
|
+
* kind-only comparison would have let one consent buy both. */
|
|
448
|
+
return grant.kind === "mod" && grant.action === request.action;
|
|
449
|
+
case "debug":
|
|
450
|
+
/* Exact match only, and here that is the point rather than a consequence:
|
|
451
|
+
* this is the grant a player is most likely to check for by name, so it
|
|
452
|
+
* must never be reachable through anything wider.
|
|
453
|
+
*
|
|
454
|
+
* THE ACTION IS COMPARED, and until "debug:wizard" landed it was not - which
|
|
455
|
+
* was invisible while "spawn" was the only action, and would have been a real
|
|
456
|
+
* hole the moment a second one existed: a player who agreed to a mod
|
|
457
|
+
* conjuring one monster would have been granting it the depth jumps, the
|
|
458
|
+
* experience and the acquirement too. Exactly the #261 lesson, caught by
|
|
459
|
+
* adding the second action rather than by shipping it. */
|
|
460
|
+
return grant.kind === "debug" && grant.action === request.action;
|
|
303
461
|
case "ui":
|
|
304
462
|
/* Per region, with one wildcard. A `display` grant is NOT accepted here
|
|
305
463
|
* and a `ui` grant is not accepted above: owning the dungeon and owning
|