Skip to content

Commit d667039

Browse files
committed
fix
1 parent 7788e92 commit d667039

1 file changed

Lines changed: 83 additions & 19 deletions

File tree

docs/PRDs/4-17-script-api-expansion-prd.md

Lines changed: 83 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
## Overview
66

77
- **Context & Goals**:
8+
89
- Unify and expand the scripting API to cover common gameplay needs while preserving sandbox safety and performance budgets.
910
- Align `src/game/scripts/script-api.d.ts` with runtime implementations in `src/core/lib/scripting/ScriptAPI.ts` and generate types from source to prevent drift.
1011
- Introduce additional subsystems to scripts: Events, Audio, Timers/Scheduling, Queries/Tags, Entity Ops (spawn/destroy/enable), and Input Actions.
@@ -19,13 +20,16 @@
1920
## Proposed Solution
2021

2122
- **High‑level Summary**:
23+
2224
- Standardize API naming and surface; add a thin compatibility layer for existing global names to avoid breaking changes.
2325
- Add new script APIs: Event, Audio, Timer/Scheduler, Query/Tags, Prefab/EntityOps, and enrich Three helpers (raycast utils, visibility, animate already returns Promises).
2426
- Replace mock input with real Input Service, add action querying, and basic gamepad support passthrough.
2527
- Introduce typed parameters via Zod schemas and optional codegen for editor UX and script hints.
2628
- Generate `script-api.d.ts` from runtime TypeScript sources during dev (via existing Vite plugin) to keep types in sync.
29+
- Add first‑class Entity References in Script component UI and an `Entities` API to safely resolve and manipulate referenced entities.
2730

2831
- **Architecture & Directory Structure**:
32+
2933
```
3034
src/core/lib/scripting/
3135
├── ScriptAPI.ts # Align + extend (source of truth)
@@ -38,7 +42,8 @@
3842
│ ├── AudioAPI.ts # play/stop, positional tie-ins
3943
│ ├── TimerAPI.ts # timers, nextTick, frame waiters
4044
│ ├── QueryAPI.ts # tags, raycasts, entity queries
41-
│ └── PrefabAPI.ts # spawn/destroy/enable/disable clones
45+
│ ├── PrefabAPI.ts # spawn/destroy/enable/disable clones
46+
│ └── EntitiesAPI.ts # resolve entity refs → safe IEntityScriptAPI
4247
└── ScriptExecutor.ts # Wire new APIs into context
4348
4449
src/plugins/
@@ -50,26 +55,33 @@
5055
## Implementation Plan
5156

5257
- **Phase 1: API Alignment (0.5 day)**
58+
5359
1. Normalize names across runtime and d.ts (keep `IEntityScriptAPI`, `IThreeJSAPI`, `IInputAPI`).
5460
2. Add `compatAliases.ts` to expose legacy global names (`IScriptEntityAPI`, `IScriptThreeAPI`) mapped to the canonical ones.
5561
3. Audit ScriptExecutor context construction; ensure new fields are optional and back‑compatible.
5662

5763
- **Phase 2: Input & Time (0.5 day)**
64+
5865
1. Replace mock Input in `ScriptSystem` with a real input provider (hook/service adapter).
5966
2. Add `input.actions.isPressed(name)` and basic gamepad passthrough.
6067
3. Expose `time: { time, deltaTime, frameCount }` and add `TimerAPI` with frame‑budgeted scheduler.
6168

6269
- **Phase 3: Events & Audio (0.75 day)**
70+
6371
1. `EventAPI` with `on`, `off`, `emit`, auto‑scoped to entity and auto‑cleanup on destroy.
6472
2. `AudioAPI` bridging `SoundManager`/Howler: `play(url, opts)`, `stop(handle)`, optional positional binding to current entity.
6573

6674
- **Phase 4: Query/Tags & Entity Ops (0.75 day)**
75+
6776
1. `QueryAPI`: tag queries, `findByTag(name)`, `raycastAll`, `raycastFirst`, world position helpers.
6877
2. `PrefabAPI`: `spawn(prefabId, overrides?)`, `destroy(entityId)`, `setActive(entityId, active)` with ECS checks.
78+
3. `EntitiesAPI`: resolve entity references to safe `IEntityScriptAPI`, lookup by id/name/tag, null‑safe wrappers.
6979

7080
- **Phase 5: Type Generation & Parameters (0.5 day)**
81+
7182
1. Generate `script-api.d.ts` from runtime interfaces (Vite dev). Add banner "AUTO‑GENERATED".
7283
2. Add optional `parametersSchema?: z.ZodSchema` per script; validate at runtime; expose typed `parameters`.
84+
3. Script component UI: add `EntityRef` and `EntityRef[]` parameter types with entity picker + drag‑drop from hierarchy. Persist `{ entityId?, guid?, path? }` for stability across reloads.
7385

7486
- **Phase 6: Tests & Docs (0.5 day)**
7587
1. Unit tests for each API and context lifecycle cleanup.
@@ -99,8 +111,23 @@
99111
## Technical Details
100112

101113
### Canonical interfaces (source of truth)
114+
102115
```ts
103116
// src/core/lib/scripting/ScriptAPI.ts (additions only)
117+
export interface IEntityRef {
118+
entityId?: number; // fast path when stable
119+
guid?: string; // stable id if available
120+
path?: string; // fallback scene path (e.g., Root/Enemy[2]/Weapon)
121+
}
122+
123+
export interface IEntitiesAPI {
124+
fromRef(ref: IEntityRef | number | string): IEntityScriptAPI | null; // accepts id/guid/path
125+
get(entityId: number): IEntityScriptAPI | null;
126+
findByName(name: string): IEntityScriptAPI[];
127+
findByTag(tag: string): IEntityScriptAPI[];
128+
exists(entityId: number): boolean;
129+
}
130+
104131
export interface IEventAPI {
105132
on<T extends string>(type: T, handler: (payload: unknown) => void): () => void;
106133
off<T extends string>(type: T, handler: (payload: unknown) => void): void;
@@ -141,11 +168,13 @@ export interface IScriptContext {
141168
timer: ITimerAPI;
142169
query: IQueryAPI;
143170
prefab: IPrefabAPI;
171+
entities: IEntitiesAPI; // NEW: resolve entity references safely
144172
parameters: Record<string, unknown>; // validated when schema provided
145173
}
146174
```
147175

148176
### Context wiring
177+
149178
```ts
150179
// src/core/lib/scripting/ScriptExecutor.ts (inside createScriptContext)
151180
return {
@@ -160,11 +189,13 @@ return {
160189
timer: createTimerAPI(entityId),
161190
query: createQueryAPI(entityId, () => getSceneRef()),
162191
prefab: createPrefabAPI(entityId),
192+
entities: createEntitiesAPI(),
163193
parameters,
164194
};
165195
```
166196

167197
### Event API
198+
168199
```ts
169200
// src/core/lib/scripting/apis/EventAPI.ts
170201
import { emitter } from '@/core/lib/events';
@@ -184,6 +215,7 @@ export function createEventAPI(entityId: number): IEventAPI {
184215
```
185216

186217
### Timer API (frame‑budgeted)
218+
187219
```ts
188220
// src/core/lib/scripting/apis/TimerAPI.ts
189221
export function createTimerAPI(entityId: number): ITimerAPI {
@@ -193,18 +225,36 @@ export function createTimerAPI(entityId: number): ITimerAPI {
193225
```
194226

195227
### Query API
228+
196229
```ts
197230
// src/core/lib/scripting/apis/QueryAPI.ts
198231
export function createQueryAPI(entityId: number, getScene: () => THREE.Scene | null): IQueryAPI {
199232
return {
200233
findByTag: (tag) => /* map tag registry refs to entityIds */ [],
201-
raycastFirst: (o, d) => createThreeJSAPI(entityId, () => null, getScene).raycast(o, d)[0] ?? null,
234+
raycastFirst: (o, d) =>
235+
createThreeJSAPI(entityId, () => null, getScene).raycast(o, d)[0] ?? null,
202236
raycastAll: (o, d) => createThreeJSAPI(entityId, () => null, getScene).raycast(o, d),
203237
};
204238
}
205239
```
206240

241+
### Entities API
242+
243+
```ts
244+
// src/core/lib/scripting/apis/EntitiesAPI.ts
245+
export function createEntitiesAPI(): IEntitiesAPI {
246+
return {
247+
fromRef: (ref) => /* resolve via entityId → guid → path; wrap with createEntityAPI */ null,
248+
get: (id) => /* wrap or null */ null,
249+
findByName: (name) => /* search registry by name */ [],
250+
findByTag: (tag) => /* reuse Query/Tags mapping, but return IEntityScriptAPI */ [],
251+
exists: (id) => /* check ECS/componentRegistry */ false,
252+
};
253+
}
254+
```
255+
207256
### DTS generation (dev only)
257+
208258
```ts
209259
// src/core/lib/scripting/adapters/dtsGenerator.ts
210260
// Walk ScriptAPI.ts interfaces, emit a global ambient d.ts mirroring names and JSDoc.
@@ -247,9 +297,22 @@ function onStart() {
247297
}
248298
```
249299

300+
```ts
301+
// 4) Entity references from Script component UI
302+
// Assume parameters.target is of type EntityRef (set via Inspector picker)
303+
function onStart() {
304+
const target = entities.fromRef(parameters.target as EntityRef);
305+
if (target) {
306+
const [x, y, z] = target.transform.position;
307+
target.transform.setPosition(x, y + 1, z);
308+
}
309+
}
310+
```
311+
250312
## Testing Strategy
251313

252314
- **Unit Tests**:
315+
253316
- EventAPI: subscribe/emit/off, auto‑cleanup on destroy.
254317
- TimerAPI: timeout/interval/nextTick/waitFrames; frame budget adherence.
255318
- AudioAPI: play/stop, positional attach when entity has mesh.
@@ -263,14 +326,15 @@ function onStart() {
263326

264327
## Edge Cases
265328

266-
| Edge Case | Remediation |
267-
| --- | --- |
268-
| Long‑running timers blocking frame | Scheduler slices work; cap per‑frame callbacks; warn via console.
269-
| Event listener leak | Auto‑unsubscribe on `onDestroy`; scoped off function set.
270-
| Missing scene/mesh for raycast/audio attach | Return empty results/no‑op, log debug message.
271-
| d.ts drift from runtime | Generate from source each dev boot; add CI check.
272-
| Old global names used in scripts | Provide alias globals; deprecate with console warnings.
273-
| Input device differences | Fallbacks for unsupported gamepad features; document mapping.
329+
| Edge Case | Remediation |
330+
| ------------------------------------------- | ----------------------------------------------------------------- |
331+
| Long‑running timers blocking frame | Scheduler slices work; cap per‑frame callbacks; warn via console. |
332+
| Event listener leak | Auto‑unsubscribe on `onDestroy`; scoped off function set. |
333+
| Missing scene/mesh for raycast/audio attach | Return empty results/no‑op, log debug message. |
334+
| d.ts drift from runtime | Generate from source each dev boot; add CI check. |
335+
| Old global names used in scripts | Provide alias globals; deprecate with console warnings. |
336+
| Input device differences | Fallbacks for unsupported gamepad features; document mapping. |
337+
| Stale/missing entity reference | `entities.fromRef()` returns null; show warning; optional repair. |
274338

275339
## Sequence Diagram
276340

@@ -294,13 +358,14 @@ sequenceDiagram
294358

295359
## Risks & Mitigations
296360

297-
| Risk | Mitigation |
298-
| --- | --- |
299-
| Security surface increases | Keep whitelist, proxies; no direct Three mutables; scoped events.
300-
| Performance regressions | Frame budgets in scheduler; Promises resolved via RAF; benchmarks.
301-
| Breaking changes in API names | Provide compatibility aliases; deprecation warnings; migration doc.
302-
| Type drift | Automated d.ts generation and CI check.
303-
| Input provider variability | Feature detection; no‑op stubs when not supported.
361+
| Risk | Mitigation |
362+
| ----------------------------- | ------------------------------------------------------------------- |
363+
| Security surface increases | Keep whitelist, proxies; no direct Three mutables; scoped events. |
364+
| Performance regressions | Frame budgets in scheduler; Promises resolved via RAF; benchmarks. |
365+
| Breaking changes in API names | Provide compatibility aliases; deprecation warnings; migration doc. |
366+
| Type drift | Automated d.ts generation and CI check. |
367+
| Input provider variability | Feature detection; no‑op stubs when not supported. |
368+
| Entity identity across reload | Store guid/path fallback; runtime repair to current entityId. |
304369

305370
## Timeline
306371

@@ -319,6 +384,7 @@ sequenceDiagram
319384
- Input in scripts uses real provider; action querying works.
320385
- Back‑compat names continue to work with deprecation notices.
321386
- Unit and integration tests pass; CI check prevents d.ts drift.
387+
- Script component UI supports `EntityRef` parameters with picker/drag‑drop; scripts resolve refs via `entities.fromRef()`.
322388

323389
## Conclusion
324390

@@ -331,5 +397,3 @@ This plan establishes a consistent, ergonomic, and safe Script API that covers e
331397
- Three.js + R3F for scene access; entity mesh registration via `ThreeJSEntityRegistry`.
332398
- Zod for parameter validation; TS path aliases per `tsconfig`.
333399
- No physics API expansion in this phase; add later if required by `2-10-physics-system.md`.
334-
335-

0 commit comments

Comments
 (0)