@helixdev/helix-manifest 0.2.1-staging.8 → 0.3.0-staging.12
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/README.md +3 -3
- package/dist/schema/manifest-v0.3.schema.json +386 -0
- package/dist/src/index.d.ts +432 -5
- package/dist/src/index.js +2200 -5
- package/dist/src/index.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/schema/manifest-v0.3.schema.json +386 -0
package/README.md
CHANGED
|
@@ -29,11 +29,11 @@ const manifest = result.manifest; // NormalizedHelixManifest — defaults applie
|
|
|
29
29
|
|
|
30
30
|
### Exports
|
|
31
31
|
|
|
32
|
-
- `validateManifest(data)` / `parseManifest(json)` — validate + normalize a world `helix.json` (v0.1).
|
|
32
|
+
- `validateManifest(data)` / `parseManifest(json)` — validate + normalize a world `helix.json` (v0.1–v0.3).
|
|
33
33
|
- `validatePackageEnvelope(data)` / `parsePackageEnvelope(json)` — validate the `system | ability | asset-pack` envelope (v1, reserved for the ability-packaging milestone).
|
|
34
34
|
- Bundle rules: `BUNDLE_MAX_FILES`, `BUNDLE_MAX_FILE_BYTES`, `BUNDLE_MAX_TOTAL_BYTES`, `BUNDLE_PATH_PATTERN`, `bundleContentType(path)`, `bundleLimitsForKind(kind)`, `classifyBundleFile(kind, path)`, `ASSET_EXTENSIONS`.
|
|
35
|
-
- Vocabulary + federation maps: `BUNDLE_KINDS`, `MAIN_BACKEND_PACKAGE_TYPE`, `MAIN_BACKEND_CONTENT_RATING`, `PERMISSIONS_V01`, `MANIFEST_FILENAME`, `MANIFEST_VERSIONS`.
|
|
36
|
-
- Types: `HelixManifest`, `NormalizedHelixManifest`, `HelixBundleKind`, `HelixContentRating`, `HelixPermission`, `HelixPackageEnvelope`, `NormalizedPackageEnvelope`.
|
|
35
|
+
- Vocabulary + federation maps: `BUNDLE_KINDS`, `MAIN_BACKEND_PACKAGE_TYPE`, `MAIN_BACKEND_CONTENT_RATING`, `PERMISSIONS_V01`, `PERMISSIONS_V03`, `PLATFORM_MAX_PLAYERS_PER_ROOM`, `MANIFEST_FILENAME`, `MANIFEST_VERSIONS`.
|
|
36
|
+
- Types: `HelixManifest`, `NormalizedHelixManifest`, `HelixBundleKind`, `HelixContentRating`, `HelixPermission`, `HelixMultiplayerConfig`, `HelixPackageEnvelope`, `NormalizedPackageEnvelope`.
|
|
37
37
|
|
|
38
38
|
## Develop
|
|
39
39
|
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://helix.dev/schemas/manifest-v0.3.schema.json",
|
|
4
|
+
"title": "HELIX Instant World Manifest v0.3",
|
|
5
|
+
"description": "v0.2 plus multiplayer. A v0.3 world may request the 'multiplayer' permission and declare a 'multiplayer' block; cross-field rules (enforced in @hypersoniclabs/helix-manifest, in both the CLI and the API) tie maxPlayers, the multiplayer/voice permissions, and requiresAuth together. Lives at the bundle root as helix.json.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["helixVersion", "title", "slug", "entry"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"helixVersion": {
|
|
11
|
+
"const": "0.3",
|
|
12
|
+
"description": "Manifest schema version. Determines which fields and permissions are available."
|
|
13
|
+
},
|
|
14
|
+
"title": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"minLength": 1,
|
|
17
|
+
"maxLength": 80,
|
|
18
|
+
"description": "Display name shown on the world page and in listings."
|
|
19
|
+
},
|
|
20
|
+
"slug": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"pattern": "^[a-z0-9](?:[a-z0-9-]{1,48})[a-z0-9]$",
|
|
23
|
+
"description": "URL identifier (3-50 chars: lowercase letters, digits, hyphens; no leading/trailing hyphen). Globally unique; the world's play URL is /g/{slug}."
|
|
24
|
+
},
|
|
25
|
+
"entry": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"pattern": "^(?!/)(?!.*\\.\\.)[A-Za-z0-9._/-]+\\.html$",
|
|
28
|
+
"description": "Bundle-relative path to the HTML entry point (e.g. index.html). Must not be absolute or contain '..'."
|
|
29
|
+
},
|
|
30
|
+
"maxPlayers": {
|
|
31
|
+
"type": "integer",
|
|
32
|
+
"minimum": 1,
|
|
33
|
+
"maximum": 100,
|
|
34
|
+
"default": 1,
|
|
35
|
+
"description": "Player slots per room. 1 = single-player. Any value > 1 requires the 'multiplayer' permission (cross-field rule)."
|
|
36
|
+
},
|
|
37
|
+
"permissions": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"uniqueItems": true,
|
|
40
|
+
"default": [],
|
|
41
|
+
"items": {
|
|
42
|
+
"enum": ["auth.profile", "multiplayer", "voice.room", "voice.proximity"]
|
|
43
|
+
},
|
|
44
|
+
"description": "Platform capabilities the world requests, OAuth-scope style. Worlds get nothing by default. auth.profile: read the player's id, username, display name. multiplayer: join authoritative Colyseus rooms and replicate player state (implies requiresAuth — guests are single-player only). voice.room / voice.proximity: RESERVED for the Phase-2 voice layer (LiveKit); declarable now, each implies multiplayer. wallet.*, inventory.* land in later versions."
|
|
45
|
+
},
|
|
46
|
+
"multiplayer": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"description": "Multiplayer tuning. Present only on multiplayer worlds; the platform owns the tick rate and room lifecycle, so there is no tickRate knob here.",
|
|
50
|
+
"properties": {
|
|
51
|
+
"interestRadius": {
|
|
52
|
+
"type": "number",
|
|
53
|
+
"exclusiveMinimum": 0,
|
|
54
|
+
"description": "Advisory: meters within which a player is relevant to another (drives later StateView interest management). Advisory in v1 — the room may ignore it until interest management ships."
|
|
55
|
+
},
|
|
56
|
+
"minPlayers": {
|
|
57
|
+
"type": "integer",
|
|
58
|
+
"minimum": 1,
|
|
59
|
+
"description": "Minimum players the world needs to be playable (default 1 = single-player-safe). When 1, the single-player gate warns (strict = error) if a phase transition is gated on playerCount >= 2. Set >= 2 to declare a multiplayer-only world."
|
|
60
|
+
},
|
|
61
|
+
"authoritative": {
|
|
62
|
+
"type": "boolean",
|
|
63
|
+
"default": true,
|
|
64
|
+
"description": "Whether the room server owns authoritative state and validates inputs (Tier 1). Reserved as a seam; v1 only ships the authoritative model, so leave true."
|
|
65
|
+
},
|
|
66
|
+
"state": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"additionalProperties": false,
|
|
69
|
+
"description": "Declared per-world custom state (Tier 2 Phase 1.3): room-level + per-player vars the room realizes as @colyseus/schema and interprets. Each entry is a var name -> VarType.",
|
|
70
|
+
"properties": {
|
|
71
|
+
"roomVars": { "$ref": "#/$defs/varDecls" },
|
|
72
|
+
"playerVars": { "$ref": "#/$defs/varDecls" }
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"entities": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"propertyNames": { "pattern": "^[a-z0-9][a-z0-9-]{0,31}$" },
|
|
78
|
+
"additionalProperties": { "$ref": "#/$defs/entityDecl" },
|
|
79
|
+
"description": "Declared entity archetypes (Tier 2 Phase 4, spec §9): kind name -> { vars }. The room realizes per-kind @colyseus/schema + syncs the entities collection; spawnEntity/destroyEntity + entitySpawn/entityDestroy reference a kind. Kind names are lowercase (matching a ref's `of: entity:<kind>`)."
|
|
80
|
+
},
|
|
81
|
+
"states": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"required": ["initial", "phases"],
|
|
85
|
+
"description": "Declared room-scoped state machine (Tier 2 Phase 3, spec §8): `initial` is the phase the room starts in; `phases` is the closed set transitionTo may move between. stateEnter/stateExit rule events fire on a transition; timeInState reads seconds spent in the current phase. `initial` must be one of `phases` (checked at publish).",
|
|
86
|
+
"properties": {
|
|
87
|
+
"initial": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" },
|
|
88
|
+
"phases": { "type": "array", "minItems": 1, "items": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "uniqueItems": true },
|
|
89
|
+
"joinPolicy": {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"propertyNames": { "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" },
|
|
92
|
+
"additionalProperties": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"properties": {
|
|
96
|
+
"joinable": { "type": "boolean" },
|
|
97
|
+
"onLateJoin": { "type": "array", "items": { "$ref": "#/$defs/ruleEffect" } }
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"description": "Per-phase late-join policy (spec §8): phase name -> { joinable, onLateJoin }. When the current phase is joinable:false, a joiner runs onLateJoin (bind self) instead of the normal playerJoin rules (spectate-until-next-round). Absent / joinable:true → spawn in."
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"timers": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"propertyNames": { "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" },
|
|
107
|
+
"additionalProperties": { "$ref": "#/$defs/timerDecl" },
|
|
108
|
+
"description": "Declared timers (Tier 2 Phase 3, spec §8): name -> {}. startTimer arms one (deadline reified into synced state), timerElapsed fires once when it passes, cancelTimer clears it, timerRemaining reads the seconds left. Room-scoped in Phase 3.2; the per-player keyed form arrives in 3.3."
|
|
109
|
+
},
|
|
110
|
+
"zones": {
|
|
111
|
+
"type": "array",
|
|
112
|
+
"items": { "$ref": "#/$defs/zone" },
|
|
113
|
+
"description": "Declared static spatial zones (Tier 2 Phase 2.3, spec §6): box/sphere volumes the room tests members against each tick, edge-firing zoneEnter/zoneExit/zoneInside. The no-physics primitive behind checkpoints, kill-planes, finish lines, pickup radii."
|
|
114
|
+
},
|
|
115
|
+
"events": {
|
|
116
|
+
"type": "object",
|
|
117
|
+
"propertyNames": { "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" },
|
|
118
|
+
"additionalProperties": { "$ref": "#/$defs/eventDecl" },
|
|
119
|
+
"description": "Declared server→client broadcast events (Tier 2 Phase 2.5, spec §10.3): name -> { payload schema }. A `broadcast` rule-effect sends one; clients receive it via room.onMessage(name)."
|
|
120
|
+
},
|
|
121
|
+
"actions": {
|
|
122
|
+
"type": "object",
|
|
123
|
+
"propertyNames": { "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" },
|
|
124
|
+
"additionalProperties": { "$ref": "#/$defs/actionDecl" },
|
|
125
|
+
"description": "Declared client→server actions (Tier 2 Phase 2.5b, spec §11.5): name -> { typed arg schema }. A client sends ActionMessage{name,args}; the room validates args against the schema before firing {on:action,name} rules."
|
|
126
|
+
},
|
|
127
|
+
"rules": {
|
|
128
|
+
"type": "array",
|
|
129
|
+
"items": { "$ref": "#/$defs/rule" },
|
|
130
|
+
"description": "Declared behavior rules (Tier 2 Phase 2, spec §7): when/if/then the room evaluates each tick in declared order. Phase-2.1 subset = the tick event + the add effect; later slices widen the grammar."
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"supportsMobile": {
|
|
135
|
+
"type": "boolean",
|
|
136
|
+
"default": false,
|
|
137
|
+
"description": "Whether the world is playable on mobile browsers. Surfaces as a compatibility tag."
|
|
138
|
+
},
|
|
139
|
+
"requiresAuth": {
|
|
140
|
+
"type": "boolean",
|
|
141
|
+
"default": false,
|
|
142
|
+
"description": "When true, players must log in before playing — the shell's login overlay offers no guest option, and the world page shows an 'Account required' badge. Forced true when the 'multiplayer' permission is requested (multiplayer requires login)."
|
|
143
|
+
},
|
|
144
|
+
"contentRating": {
|
|
145
|
+
"enum": ["unrated", "everyone", "teen", "mature"],
|
|
146
|
+
"default": "unrated",
|
|
147
|
+
"description": "Self-declared content rating. Curated review may override. 'unrated' worlds may be restricted from public listing. Maps 1:1 onto the main backend's ContentRating (Everyone/Teen/Mature); 'unrated' has no counterpart there and must be rated before catalog federation."
|
|
148
|
+
},
|
|
149
|
+
"kind": {
|
|
150
|
+
"enum": ["world"],
|
|
151
|
+
"default": "world",
|
|
152
|
+
"description": "What this bundle is. v0.3 worlds still set 'world'; the reserved vocabulary (system, ability, asset-pack) uses the package envelope, not this manifest."
|
|
153
|
+
},
|
|
154
|
+
"version": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-[0-9A-Za-z.-]+)?$",
|
|
157
|
+
"default": "1.0.0",
|
|
158
|
+
"description": "Semver identity for catalog federation with the main backend (its packages version by semver). Worlds may ignore it — builds number independently — but every stored manifest carries one so a future sync is a column map, not a translation layer."
|
|
159
|
+
},
|
|
160
|
+
"systems": {
|
|
161
|
+
"type": "object",
|
|
162
|
+
"additionalProperties": { "type": "string", "minLength": 1 },
|
|
163
|
+
"propertyNames": { "pattern": "^[a-z0-9](?:[a-z0-9-]{1,48})[a-z0-9]$" },
|
|
164
|
+
"description": "Pinned system dependencies as slug -> semver range (e.g. { \"humanoid-character\": \"^0.1\" }). Resolved to exact active versions at build; the resolved code + asset-pack CDN bases are baked into the immutable build. The asset-pack is discovered via the system's own envelope, not pinned here."
|
|
165
|
+
},
|
|
166
|
+
"abilities": {
|
|
167
|
+
"type": "object",
|
|
168
|
+
"additionalProperties": { "type": "string", "minLength": 1 },
|
|
169
|
+
"propertyNames": { "pattern": "^[a-z0-9](?:[a-z0-9-]{1,48})[a-z0-9]$" },
|
|
170
|
+
"description": "Pinned ability dependencies as slug -> semver range (e.g. { \"fly\": \"^0.1\", \"swim\": \"^0.1\" }). Resolved to exact active versions at build; each ability's code + asset CDN base are baked into the immutable build."
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"$defs": {
|
|
174
|
+
"varDecls": {
|
|
175
|
+
"type": "object",
|
|
176
|
+
"propertyNames": { "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" },
|
|
177
|
+
"additionalProperties": { "$ref": "#/$defs/varType" },
|
|
178
|
+
"description": "Declared custom-state vars: name -> VarType. Names are identifiers (a letter then up to 31 letters/digits/underscores)."
|
|
179
|
+
},
|
|
180
|
+
"varType": {
|
|
181
|
+
"description": "A declared variable's type + constraints (Tier 2 declared state, spec §5). Discriminated on `type`.",
|
|
182
|
+
"oneOf": [
|
|
183
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "default"], "properties": { "type": { "const": "number" }, "default": { "type": "number" }, "min": { "type": "number" }, "max": { "type": "number" }, "integer": { "type": "boolean" } } },
|
|
184
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "default"], "properties": { "type": { "const": "string" }, "default": { "type": "string" }, "maxLen": { "type": "integer", "minimum": 1 }, "enum": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true } } },
|
|
185
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "default"], "properties": { "type": { "const": "boolean" }, "default": { "type": "boolean" } } },
|
|
186
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "default"], "properties": { "type": { "const": "vec3" }, "default": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 } } },
|
|
187
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "of"], "properties": { "type": { "const": "ref" }, "of": { "type": "string", "pattern": "^(player|entity:[a-z0-9][a-z0-9-]*)$" }, "default": { "type": "null" } } },
|
|
188
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "of", "maxLen"], "properties": { "type": { "const": "list" }, "of": { "enum": ["number", "string", "boolean"] }, "maxLen": { "type": "integer", "minimum": 1 } }, "description": "Tier 2 Phase 4.5.13: a bounded ordered array of one scalar element type (roomVars/playerVars only). Starts empty; append/clear mutate, listLength/listAt read." },
|
|
189
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "keys"], "properties": { "type": { "const": "counterMap" }, "keys": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true } }, "description": "Tier 2 Phase 4.5.13: a closed-enum-keyed number map (each key 0 at start; roomVars/playerVars only). addCount/clear mutate, count reads." }
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
"timerDecl": {
|
|
193
|
+
"description": "A declared timer (spec §8). Room-scoped by default ({}); `keyed:'player'` makes it per-player — startTimer/cancelTimer/timerRemaining then require a `key` (a player ref) and timerElapsed binds `self`. Keyed-ness is a static property of the declared name (every use must agree).",
|
|
194
|
+
"type": "object",
|
|
195
|
+
"additionalProperties": false,
|
|
196
|
+
"properties": { "keyed": { "type": "string", "pattern": "^(player|entity:[a-z0-9][a-z0-9-]{0,31})$" } }
|
|
197
|
+
},
|
|
198
|
+
"entityDecl": {
|
|
199
|
+
"description": "A declared entity archetype (spec §9): per-kind custom `vars` (the §5 vocabulary), realized once per live entity, + optional server-authoritative `motion` (Phase 4.2) + attached `zone` (Phase 4.4) + single-owner `ownerLifecycle` (Phase 4.6a) + `authority`/`maxSpeed` (Phase 4.6b). shared-host modes arrive in 4.8.",
|
|
200
|
+
"type": "object",
|
|
201
|
+
"additionalProperties": false,
|
|
202
|
+
"properties": {
|
|
203
|
+
"vars": { "$ref": "#/$defs/varDecls" },
|
|
204
|
+
"motion": { "$ref": "#/$defs/entityMotion" },
|
|
205
|
+
"zone": { "$ref": "#/$defs/attachedZone" },
|
|
206
|
+
"authority": { "enum": ["server", "owner"], "description": "spec §9: 'server' (default) = deterministic kinematics, cheat-proof, never freezes; 'owner' = client-simulated + relayed, spoofable. An 'owner' kind MUST declare maxSpeed and is subject to the publish-time cross-player firewall." },
|
|
207
|
+
"maxSpeed": { "type": "number", "exclusiveMinimum": 0, "description": "spec §9 (required for authority:'owner'): metres/second — the movement-plausibility ceiling the room gates the entity's client-relayed transform against. Ignored on a server-authoritative kind." },
|
|
208
|
+
"shared": { "type": "boolean", "description": "spec §9 (Phase 4.8): the entity belongs to the game, not a player — one client is its simulation host. Requires authority:'owner' + ownerLifecycle:'hostMigrate'; the server assigns the least-loaded client at spawn and re-elects on the host's leave (involuntary host migration)." },
|
|
209
|
+
"idleTimeout": { "type": "number", "exclusiveMinimum": 0, "description": "spec §9 (Phase 4.8, optional): seconds a shared entity may stay hostless (frozen, no eligible host) before despawning." },
|
|
210
|
+
"ownerLifecycle": { "enum": ["despawnWithOwner", "persist", "migrateToServer", "hostMigrate"], "description": "spec §9. Single-owner: despawnWithOwner (default) destroys on the owner's leave; persist leaves it frozen; migrateToServer freezes during a transient grace absence + the same owner resumes. Shared: hostMigrate (requires shared:true) — re-elect the least-loaded client on the host's leave." },
|
|
211
|
+
"transferPolicy": { "enum": ["fixed", "request", "takeover"], "description": "spec §9 (Phase 4.5.12): voluntary ownership transfer policy the requestOwnership/takeover verbs honor. 'fixed' (default) = the verbs no-op. 'request' = grant only when unowned (polite pickup). 'takeover' = also steal from a live owner. Requires authority:'owner'. A transfer bumps the entity's authorityEpoch + fires ownershipChanged." }
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
"attachedZone": {
|
|
215
|
+
"description": "An entity-attached zone (spec §6/§9, Phase 4.4): a box/sphere volume centered on the entity's transform (+ optional offset), no id/center. Overlap fires zoneEnter/zoneExit/zoneInside addressed by the entity kind, binding self + source. tracks defaults to 'players'; 'entity:<kind>' makes self an entity of that kind (4.5.8).",
|
|
216
|
+
"type": "object",
|
|
217
|
+
"oneOf": [
|
|
218
|
+
{ "type": "object", "additionalProperties": false, "required": ["shape", "size"], "properties": { "shape": { "const": "box" }, "size": { "type": "array", "items": { "type": "number", "exclusiveMinimum": 0 }, "minItems": 3, "maxItems": 3 }, "offset": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 }, "tracks": { "type": "string", "pattern": "^(players|entity:[a-z0-9][a-z0-9-]*)$" }, "requireDwell": { "type": "number", "minimum": 0 }, "debounce": { "type": "number", "minimum": 0 } } },
|
|
219
|
+
{ "type": "object", "additionalProperties": false, "required": ["shape", "radius"], "properties": { "shape": { "const": "sphere" }, "radius": { "type": "number", "exclusiveMinimum": 0 }, "offset": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 }, "tracks": { "type": "string", "pattern": "^(players|entity:[a-z0-9][a-z0-9-]*)$" }, "requireDwell": { "type": "number", "minimum": 0 }, "debounce": { "type": "number", "minimum": 0 } } }
|
|
220
|
+
]
|
|
221
|
+
},
|
|
222
|
+
"entityMotion": {
|
|
223
|
+
"description": "Server-authoritative deterministic kinematics (spec §9, Phase 4.2/4.6a): static (no motion), linear (constant velocity, m/s), orbit (a circle in the XZ plane about center at speed rad/s), or seek (step straight toward the position of the member in the entity's own ref var `target`, at `speed` m/s — simple cheat-proof homing). waypoints arrives on demand.",
|
|
224
|
+
"oneOf": [
|
|
225
|
+
{ "type": "object", "additionalProperties": false, "required": ["type"], "properties": { "type": { "const": "static" } } },
|
|
226
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "velocity"], "properties": { "type": { "const": "linear" }, "velocity": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 } } },
|
|
227
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "center", "radius", "speed"], "properties": { "type": { "const": "orbit" }, "center": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 }, "radius": { "type": "number", "exclusiveMinimum": 0 }, "speed": { "type": "number" } } },
|
|
228
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "target", "speed"], "properties": { "type": { "const": "seek" }, "target": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "speed": { "type": "number", "exclusiveMinimum": 0 } } },
|
|
229
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "points", "speed"], "properties": { "type": { "const": "waypoints" }, "points": { "type": "array", "minItems": 2, "items": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 } }, "speed": { "type": "number", "exclusiveMinimum": 0 }, "loop": { "type": "boolean" } } }
|
|
230
|
+
]
|
|
231
|
+
},
|
|
232
|
+
"zone": {
|
|
233
|
+
"description": "A declared static spatial volume (spec §6), discriminated on `shape`. center is [x,y,z] in meters; a box uses full `size` [x,y,z], a sphere uses `radius`. tracks defaults to 'players'; 'entity:<kind>' tests entities of that kind, binding self = the entering entity (4.5.8). requireDwell/debounce (seconds) harden against teleport-touch and are enforced from Phase 2.4.",
|
|
234
|
+
"type": "object",
|
|
235
|
+
"required": ["id", "shape", "center"],
|
|
236
|
+
"properties": {
|
|
237
|
+
"id": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" },
|
|
238
|
+
"tracks": { "type": "string", "pattern": "^(players|entity:[a-z0-9][a-z0-9-]*)$" },
|
|
239
|
+
"requireDwell": { "type": "number", "minimum": 0 },
|
|
240
|
+
"debounce": { "type": "number", "minimum": 0 }
|
|
241
|
+
},
|
|
242
|
+
"oneOf": [
|
|
243
|
+
{ "type": "object", "additionalProperties": false, "required": ["id", "shape", "center", "size"], "properties": { "id": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "shape": { "const": "box" }, "center": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 }, "size": { "type": "array", "items": { "type": "number", "exclusiveMinimum": 0 }, "minItems": 3, "maxItems": 3 }, "tracks": { "type": "string", "pattern": "^(players|entity:[a-z0-9][a-z0-9-]*)$" }, "requireDwell": { "type": "number", "minimum": 0 }, "debounce": { "type": "number", "minimum": 0 } } },
|
|
244
|
+
{ "type": "object", "additionalProperties": false, "required": ["id", "shape", "center", "radius"], "properties": { "id": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "shape": { "const": "sphere" }, "center": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 }, "radius": { "type": "number", "exclusiveMinimum": 0 }, "tracks": { "type": "string", "pattern": "^(players|entity:[a-z0-9][a-z0-9-]*)$" }, "requireDwell": { "type": "number", "minimum": 0 }, "debounce": { "type": "number", "minimum": 0 } } }
|
|
245
|
+
]
|
|
246
|
+
},
|
|
247
|
+
"rule": {
|
|
248
|
+
"type": "object",
|
|
249
|
+
"additionalProperties": false,
|
|
250
|
+
"required": ["when", "then"],
|
|
251
|
+
"properties": {
|
|
252
|
+
"when": { "$ref": "#/$defs/ruleEvent" },
|
|
253
|
+
"if": { "$ref": "#/$defs/expr" },
|
|
254
|
+
"then": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/ruleEffect" } }
|
|
255
|
+
},
|
|
256
|
+
"description": "A behavior rule: when <event> [if <cond>] then [<effect>...]. The optional `if` is a pure condition expression."
|
|
257
|
+
},
|
|
258
|
+
"expr": {
|
|
259
|
+
"description": "A pure condition/value expression (spec §7.3). Phase-2.2: scalar literals, room/self var reads, comparison + arithmetic + logic, playerCount, random. Phase-2.4a: a vec3 literal {vec3:[x,y,z]}, the self.position built-in (via var), the distance op, and ref-addressed reads {ref,var}. Depth + node count are capped at publish (MULTIPLAYER_CAPS).",
|
|
260
|
+
"oneOf": [
|
|
261
|
+
{ "type": "number" },
|
|
262
|
+
{ "type": "string" },
|
|
263
|
+
{ "type": "boolean" },
|
|
264
|
+
{ "type": "object", "additionalProperties": false, "required": ["vec3"], "properties": { "vec3": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 } } },
|
|
265
|
+
{ "type": "object", "additionalProperties": false, "required": ["var"], "properties": { "var": { "type": "string", "pattern": "^((room|self)\\.[A-Za-z][A-Za-z0-9_]{0,31}|action\\.args\\.[A-Za-z][A-Za-z0-9_]{0,31})$" } } },
|
|
266
|
+
{ "$ref": "#/$defs/refLvalue" },
|
|
267
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "a", "b"], "properties": { "op": { "enum": ["distance", "==", "!=", "<", "<=", ">", ">=", "+", "-", "*", "/"] }, "a": { "$ref": "#/$defs/expr" }, "b": { "$ref": "#/$defs/expr" } } },
|
|
268
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "of"], "properties": { "op": { "enum": ["and", "or"] }, "of": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/expr" } } } },
|
|
269
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "of"], "properties": { "op": { "const": "not" }, "of": { "$ref": "#/$defs/expr" } } },
|
|
270
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "scope", "agg"], "properties": { "op": { "const": "aggregate" }, "scope": { "type": "string", "pattern": "^(players|zone:[A-Za-z][A-Za-z0-9_]{0,31}|entities:[a-z0-9][a-z0-9-]{0,31})$" }, "agg": { "enum": ["count", "sum", "min", "max", "avg"] }, "field": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "where": { "$ref": "#/$defs/expr" }, "as": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" } } },
|
|
271
|
+
{ "type": "object", "additionalProperties": false, "required": ["op"], "properties": { "op": { "enum": ["playerCount", "random", "timeInState"] } } },
|
|
272
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "timer"], "properties": { "op": { "const": "timerRemaining" }, "timer": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "key": { "oneOf": [{ "type": "string", "minLength": 1 }, { "$ref": "#/$defs/refExpr" }] } } },
|
|
273
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "list"], "properties": { "op": { "const": "listLength" }, "list": { "type": "string", "pattern": "^(room|self)\\.[A-Za-z][A-Za-z0-9_]{0,31}$" } } },
|
|
274
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "list", "index"], "properties": { "op": { "const": "listAt" }, "list": { "type": "string", "pattern": "^(room|self)\\.[A-Za-z][A-Za-z0-9_]{0,31}$" }, "index": { "$ref": "#/$defs/expr" } } },
|
|
275
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "map", "key"], "properties": { "op": { "const": "count" }, "map": { "type": "string", "pattern": "^(room|self)\\.[A-Za-z][A-Za-z0-9_]{0,31}$" }, "key": { "type": "string", "minLength": 1 } } },
|
|
276
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "min", "max"], "properties": { "op": { "const": "randomPoint" }, "min": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 }, "max": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 } }, "description": "Tier 2: a uniformly-random vec3 inside the [min,max] box (each component independent). Server-evaluated each call; vec3-typed. Use for random spawns: respawn to {op:randomPoint,min,max}." }
|
|
277
|
+
]
|
|
278
|
+
},
|
|
279
|
+
"refOp": {
|
|
280
|
+
"description": "A ref-RETURNING op (spec §7.3): nearestPlayer (nearest to a point) / aggregate argmax|argmin (the member with the max/min field). Its argmax|argmin agg is disjoint from expr's scalar aggs, so it composes into oneOfs without overlap.",
|
|
281
|
+
"oneOf": [
|
|
282
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "from"], "properties": { "op": { "const": "nearestPlayer" }, "from": { "$ref": "#/$defs/expr" } } },
|
|
283
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "from"], "properties": { "op": { "const": "nearestEntity" }, "from": { "$ref": "#/$defs/expr" }, "kind": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{0,31}$" } } },
|
|
284
|
+
{ "type": "object", "additionalProperties": false, "required": ["op", "scope", "agg", "field"], "properties": { "op": { "const": "aggregate" }, "scope": { "type": "string", "pattern": "^(players|zone:[A-Za-z][A-Za-z0-9_]{0,31}|entities:[a-z0-9][a-z0-9-]{0,31})$" }, "agg": { "enum": ["argmax", "argmin"] }, "field": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "where": { "$ref": "#/$defs/expr" }, "as": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" } } }
|
|
285
|
+
]
|
|
286
|
+
},
|
|
287
|
+
"refExpr": {
|
|
288
|
+
"description": "The object forms of a Ref (spec §7.1): a ref-typed var read {var} (deref a stored ref), or a ref-returning op (refOp). A bare string Ref (a bound name) is handled separately by each slot.",
|
|
289
|
+
"oneOf": [
|
|
290
|
+
{ "type": "object", "additionalProperties": false, "required": ["var"], "properties": { "var": { "type": "string", "pattern": "^((room|self)\\.[A-Za-z][A-Za-z0-9_]{0,31}|action\\.args\\.[A-Za-z][A-Za-z0-9_]{0,31})$" } } },
|
|
291
|
+
{ "$ref": "#/$defs/refOp" }
|
|
292
|
+
]
|
|
293
|
+
},
|
|
294
|
+
"payloadValue": {
|
|
295
|
+
"description": "A broadcast payload field value (spec §10.3): a value Expr, or a ref-returning op (a Ref serializes to its playerKey on the wire). The two sides don't overlap — refOp's argmax|argmin agg is disjoint from expr's scalar aggs, and nearestPlayer isn't an expr.",
|
|
296
|
+
"oneOf": [{ "$ref": "#/$defs/expr" }, { "$ref": "#/$defs/refOp" }]
|
|
297
|
+
},
|
|
298
|
+
"fieldType": {
|
|
299
|
+
"description": "A field type in an events payload (spec §10.3): the §5 type vocabulary with NO default (the value is rule-computed + sent, never stored). A ref field declares `of`.",
|
|
300
|
+
"type": "object",
|
|
301
|
+
"additionalProperties": false,
|
|
302
|
+
"required": ["type"],
|
|
303
|
+
"properties": { "type": { "enum": ["number", "string", "boolean", "vec3", "ref"] }, "of": { "type": "string", "pattern": "^(player|entity:[a-z0-9][a-z0-9-]*)$" } }
|
|
304
|
+
},
|
|
305
|
+
"eventDecl": {
|
|
306
|
+
"description": "A declared server→client broadcast event (spec §10.3): an optional typed payload (field name -> fieldType).",
|
|
307
|
+
"type": "object",
|
|
308
|
+
"additionalProperties": false,
|
|
309
|
+
"properties": { "payload": { "type": "object", "propertyNames": { "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "additionalProperties": { "$ref": "#/$defs/fieldType" } } }
|
|
310
|
+
},
|
|
311
|
+
"actionArgType": {
|
|
312
|
+
"description": "A client-action argument type (spec §11.5): the §5 type vocabulary WITH validation constraints but NO default (the client supplies the value). A ref arg declares `of`. The room validates a client's args against this before any {on:action} rule fires.",
|
|
313
|
+
"oneOf": [
|
|
314
|
+
{ "type": "object", "additionalProperties": false, "required": ["type"], "properties": { "type": { "const": "number" }, "min": { "type": "number" }, "max": { "type": "number" }, "integer": { "type": "boolean" } } },
|
|
315
|
+
{ "type": "object", "additionalProperties": false, "required": ["type"], "properties": { "type": { "const": "string" }, "maxLen": { "type": "integer", "minimum": 1 }, "enum": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true } } },
|
|
316
|
+
{ "type": "object", "additionalProperties": false, "required": ["type"], "properties": { "type": { "const": "boolean" } } },
|
|
317
|
+
{ "type": "object", "additionalProperties": false, "required": ["type"], "properties": { "type": { "const": "vec3" } } },
|
|
318
|
+
{ "type": "object", "additionalProperties": false, "required": ["type", "of"], "properties": { "type": { "const": "ref" }, "of": { "type": "string", "pattern": "^(player|entity:[a-z0-9][a-z0-9-]*)$" } } }
|
|
319
|
+
]
|
|
320
|
+
},
|
|
321
|
+
"actionDecl": {
|
|
322
|
+
"description": "A declared client→server action (spec §11.5): an optional typed arg schema (arg name -> actionArgType).",
|
|
323
|
+
"type": "object",
|
|
324
|
+
"additionalProperties": false,
|
|
325
|
+
"properties": { "args": { "type": "object", "propertyNames": { "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "additionalProperties": { "$ref": "#/$defs/actionArgType" } } }
|
|
326
|
+
},
|
|
327
|
+
"refLvalue": {
|
|
328
|
+
"description": "A ref-addressed var slot (spec §7.1): a member's var. `ref` is a bound name (self/other) or a Ref object (refExpr); `var` is a bare identifier (a declared player var or the `position` built-in).",
|
|
329
|
+
"type": "object",
|
|
330
|
+
"additionalProperties": false,
|
|
331
|
+
"required": ["ref", "var"],
|
|
332
|
+
"properties": { "ref": { "oneOf": [{ "type": "string", "minLength": 1 }, { "$ref": "#/$defs/refExpr" }] }, "var": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" } }
|
|
333
|
+
},
|
|
334
|
+
"lvalue": {
|
|
335
|
+
"description": "An effect target (spec §7.1/§7.4): a string scope-path \"room.<var>\"/\"self.<var>\", or the ref-addressed write-through-ref {ref,var}.",
|
|
336
|
+
"oneOf": [{ "type": "string", "minLength": 1 }, { "$ref": "#/$defs/refLvalue" }]
|
|
337
|
+
},
|
|
338
|
+
"ruleEvent": {
|
|
339
|
+
"description": "The rule trigger (spec §7.2). tick = room-level; playerJoin/playerLeave bind `self`; zoneEnter/zoneExit/zoneInside bind `self` and reference a declared zone (§6); playerContact binds `self`+`other` and fires once per entering pair within `radius`. More events arrive in later slices. (Phase 4.9) An unrecognized `on` passes the schema (the permissive branch below); a reserved future event then publishes with a warning + a typo hard-errors with did-you-mean — classified cross-field in checkFutureNodes. Keep the permissive-branch enum in sync with KNOWN_EVENTS in index.ts.",
|
|
340
|
+
"type": "object",
|
|
341
|
+
"required": ["on"],
|
|
342
|
+
"anyOf": [
|
|
343
|
+
{ "oneOf": [
|
|
344
|
+
{ "type": "object", "additionalProperties": false, "required": ["on"], "properties": { "on": { "const": "tick" }, "everyN": { "type": "integer", "minimum": 1 } } },
|
|
345
|
+
{ "type": "object", "additionalProperties": false, "required": ["on"], "properties": { "on": { "const": "playerJoin" } } },
|
|
346
|
+
{ "type": "object", "additionalProperties": false, "required": ["on"], "properties": { "on": { "const": "playerLeave" } } },
|
|
347
|
+
{ "type": "object", "additionalProperties": false, "required": ["on"], "properties": { "on": { "enum": ["playerDisconnect", "playerReconnect"] } } },
|
|
348
|
+
{ "type": "object", "additionalProperties": false, "required": ["on", "zone"], "properties": { "on": { "enum": ["zoneEnter", "zoneExit", "zoneInside"] }, "zone": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" } } },
|
|
349
|
+
{ "type": "object", "additionalProperties": false, "required": ["on", "radius"], "properties": { "on": { "const": "playerContact" }, "radius": { "type": "number", "exclusiveMinimum": 0 } } },
|
|
350
|
+
{ "type": "object", "additionalProperties": false, "required": ["on", "name"], "properties": { "on": { "const": "action" }, "name": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" } } },
|
|
351
|
+
{ "type": "object", "additionalProperties": false, "required": ["on", "phase"], "properties": { "on": { "enum": ["stateEnter", "stateExit"] }, "phase": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" } } },
|
|
352
|
+
{ "type": "object", "additionalProperties": false, "required": ["on", "timer"], "properties": { "on": { "const": "timerElapsed" }, "timer": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" } } },
|
|
353
|
+
{ "type": "object", "additionalProperties": false, "required": ["on", "kind"], "properties": { "on": { "enum": ["entitySpawn", "entityDestroy", "ownershipChanged"] }, "kind": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{0,31}$" } } },
|
|
354
|
+
{ "type": "object", "additionalProperties": false, "required": ["on", "scope", "var", "cmp", "value"], "properties": { "on": { "const": "varReached" }, "scope": { "enum": ["room", "self"] }, "var": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "cmp": { "enum": ["==", "!=", "<", "<=", ">", ">="] }, "value": { "type": ["number", "string", "boolean"] } } }
|
|
355
|
+
] },
|
|
356
|
+
{ "type": "object", "required": ["on"], "properties": { "on": { "type": "string", "not": { "enum": ["tick", "playerJoin", "playerLeave", "playerDisconnect", "playerReconnect", "zoneEnter", "zoneExit", "zoneInside", "playerContact", "action", "stateEnter", "stateExit", "timerElapsed", "entitySpawn", "entityDestroy", "ownershipChanged", "varReached"] } } } }
|
|
357
|
+
]
|
|
358
|
+
},
|
|
359
|
+
"ruleEffect": {
|
|
360
|
+
"description": "A then-effect (spec §7.4). add: number var += number. set: var ← expr value. setRef: ref var ← a bound ref name (e.g. \"self\") or null. Any target may be a ref-addressed {ref,var} write-through-ref (Phase 2.4a). teleport/respawn write a member's authoritative position (player is a Ref, to a vec3). forEachPlayer is the one bounded loop (single level, binds `as`, optional `where`). More effects arrive in later slices. (Phase 4.9) An unrecognized `do` passes the schema (the permissive branch below); a reserved future effect then publishes with a warning + a typo hard-errors with did-you-mean — classified cross-field in checkFutureNodes. Keep the permissive-branch enum in sync with KNOWN_EFFECTS in index.ts.",
|
|
361
|
+
"type": "object",
|
|
362
|
+
"required": ["do"],
|
|
363
|
+
"anyOf": [
|
|
364
|
+
{ "oneOf": [
|
|
365
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "target", "by"], "properties": { "do": { "const": "add" }, "target": { "$ref": "#/$defs/lvalue" }, "by": { "$ref": "#/$defs/expr" } } },
|
|
366
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "target", "to"], "properties": { "do": { "const": "set" }, "target": { "$ref": "#/$defs/lvalue" }, "to": { "$ref": "#/$defs/expr" } } },
|
|
367
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "target", "to"], "properties": { "do": { "const": "setRef" }, "target": { "$ref": "#/$defs/lvalue" }, "to": { "oneOf": [{ "type": "string" }, { "type": "null" }, { "$ref": "#/$defs/refExpr" }] } } },
|
|
368
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "player", "to"], "properties": { "do": { "enum": ["teleport", "respawn"] }, "player": { "oneOf": [{ "type": "string", "minLength": 1 }, { "$ref": "#/$defs/refExpr" }] }, "to": { "$ref": "#/$defs/expr" } } },
|
|
369
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "as", "then"], "properties": { "do": { "const": "forEachPlayer" }, "as": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "where": { "$ref": "#/$defs/expr" }, "then": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/ruleEffect" } } } },
|
|
370
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "kind", "as", "then"], "properties": { "do": { "const": "forEachEntity" }, "kind": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{0,31}$" }, "as": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "where": { "$ref": "#/$defs/expr" }, "then": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/ruleEffect" } } } },
|
|
371
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "phase"], "properties": { "do": { "const": "transitionTo" }, "phase": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" } } },
|
|
372
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "timer", "seconds"], "properties": { "do": { "const": "startTimer" }, "timer": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "seconds": { "type": "number", "exclusiveMinimum": 0 }, "key": { "oneOf": [{ "type": "string", "minLength": 1 }, { "$ref": "#/$defs/refExpr" }] } } },
|
|
373
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "timer"], "properties": { "do": { "const": "cancelTimer" }, "timer": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "key": { "oneOf": [{ "type": "string", "minLength": 1 }, { "$ref": "#/$defs/refExpr" }] } } },
|
|
374
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "event", "to"], "properties": { "do": { "const": "broadcast" }, "event": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "to": { "oneOf": [{ "type": "string", "minLength": 1 }, { "$ref": "#/$defs/refOp" }, { "type": "object", "additionalProperties": false, "required": ["team"], "properties": { "team": { "type": "string", "minLength": 3 } } }] }, "payload": { "type": "object", "propertyNames": { "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "additionalProperties": { "$ref": "#/$defs/payloadValue" } } } },
|
|
375
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "kind", "at"], "properties": { "do": { "const": "spawnEntity" }, "kind": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{0,31}$" }, "at": { "$ref": "#/$defs/expr" }, "vars": { "type": "object", "propertyNames": { "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" }, "additionalProperties": { "$ref": "#/$defs/payloadValue" } }, "bind": { "const": "spawned" } } },
|
|
376
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "entity"], "properties": { "do": { "const": "destroyEntity" }, "entity": { "oneOf": [{ "type": "string", "minLength": 1 }, { "$ref": "#/$defs/refExpr" }] } } },
|
|
377
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "entity", "to"], "properties": { "do": { "enum": ["requestOwnership", "takeover"] }, "entity": { "oneOf": [{ "type": "string", "minLength": 1 }, { "$ref": "#/$defs/refExpr" }] }, "to": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }, { "$ref": "#/$defs/refExpr" }] } } },
|
|
378
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "target", "value"], "properties": { "do": { "const": "append" }, "target": { "type": "string", "pattern": "^(room|self)\\.[A-Za-z][A-Za-z0-9_]{0,31}$" }, "value": { "$ref": "#/$defs/expr" } } },
|
|
379
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "target"], "properties": { "do": { "const": "clear" }, "target": { "type": "string", "pattern": "^(room|self)\\.[A-Za-z][A-Za-z0-9_]{0,31}$" } } },
|
|
380
|
+
{ "type": "object", "additionalProperties": false, "required": ["do", "target", "key", "by"], "properties": { "do": { "const": "addCount" }, "target": { "type": "string", "pattern": "^(room|self)\\.[A-Za-z][A-Za-z0-9_]{0,31}$" }, "key": { "type": "string", "minLength": 1 }, "by": { "$ref": "#/$defs/expr" } } }
|
|
381
|
+
] },
|
|
382
|
+
{ "type": "object", "required": ["do"], "properties": { "do": { "type": "string", "not": { "enum": ["add", "set", "setRef", "teleport", "respawn", "forEachPlayer", "forEachEntity", "broadcast", "transitionTo", "startTimer", "cancelTimer", "spawnEntity", "destroyEntity", "requestOwnership", "takeover", "append", "clear", "addCount"] } } } }
|
|
383
|
+
]
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|