@alife-sdk/core 0.1.1 → 0.4.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/README.md +20 -2
- package/dist/ai/BehaviorTree.d.ts +174 -0
- package/dist/ai/BehaviorTree.d.ts.map +1 -0
- package/dist/ai/BehaviorTree.js +280 -0
- package/dist/ai/BehaviorTree.js.map +1 -0
- package/dist/ai/GOAPAction.d.ts +21 -1
- package/dist/ai/GOAPAction.d.ts.map +1 -1
- package/dist/ai/GOAPPlanner.d.ts +4 -3
- package/dist/ai/GOAPPlanner.d.ts.map +1 -1
- package/dist/ai/GOAPPlanner.js +31 -3
- package/dist/ai/GOAPPlanner.js.map +1 -1
- package/dist/ai/StateMachine.d.ts +38 -0
- package/dist/ai/StateMachine.d.ts.map +1 -1
- package/dist/ai/StateMachine.js +80 -0
- package/dist/ai/StateMachine.js.map +1 -1
- package/dist/ai/WorldState.d.ts +8 -0
- package/dist/ai/WorldState.d.ts.map +1 -1
- package/dist/ai/WorldState.js +14 -0
- package/dist/ai/WorldState.js.map +1 -1
- package/dist/ai/index.d.ts +5 -1
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +1 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/core/ReactiveQuery.d.ts +85 -0
- package/dist/core/ReactiveQuery.d.ts.map +1 -0
- package/dist/core/ReactiveQuery.js +154 -0
- package/dist/core/ReactiveQuery.js.map +1 -0
- package/dist/entity/EntityHandle.d.ts +76 -0
- package/dist/entity/EntityHandle.d.ts.map +1 -0
- package/dist/entity/EntityHandle.js +133 -0
- package/dist/entity/EntityHandle.js.map +1 -0
- package/dist/entity/index.d.ts +2 -0
- package/dist/entity/index.d.ts.map +1 -1
- package/dist/entity/index.js +1 -1
- package/dist/entity/index.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/registry/AIStateRegistry.d.ts +4 -0
- package/dist/registry/AIStateRegistry.d.ts.map +1 -1
- package/dist/registry/AIStateRegistry.js.map +1 -1
- package/package.json +1 -1
|
@@ -18,13 +18,51 @@ export type TransitionResult = {
|
|
|
18
18
|
readonly success: false;
|
|
19
19
|
readonly reason: 'not_allowed' | 'exit_guard' | 'enter_guard';
|
|
20
20
|
};
|
|
21
|
+
export interface StateTransitionEvent {
|
|
22
|
+
readonly from: string;
|
|
23
|
+
readonly to: string;
|
|
24
|
+
readonly timestamp: number;
|
|
25
|
+
}
|
|
21
26
|
export declare class StateMachine {
|
|
22
27
|
private currentStateId;
|
|
28
|
+
private previousStateId;
|
|
29
|
+
private stateEnterTime;
|
|
23
30
|
private readonly registry;
|
|
24
31
|
private readonly entity;
|
|
32
|
+
private readonly enterListeners;
|
|
33
|
+
private readonly exitListeners;
|
|
34
|
+
private readonly changeListeners;
|
|
35
|
+
private readonly historyLog;
|
|
25
36
|
constructor(entity: IEntity, registry: AIStateRegistry, initialState: string);
|
|
26
37
|
/** Current active state identifier. */
|
|
27
38
|
get state(): string;
|
|
39
|
+
/** Previous state identifier, or `null` if no transition has occurred yet. */
|
|
40
|
+
get previous(): string | null;
|
|
41
|
+
/** Milliseconds elapsed since entering the current state. */
|
|
42
|
+
get currentStateDuration(): number;
|
|
43
|
+
/** Returns `true` if the current state has the given tag. */
|
|
44
|
+
hasTag(tag: string): boolean;
|
|
45
|
+
/** Returns the metadata object of the current state, or `undefined`. */
|
|
46
|
+
get metadata(): Readonly<Record<string, unknown>> | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Subscribe to the moment the FSM enters `state`.
|
|
49
|
+
* @returns Unsubscribe function.
|
|
50
|
+
*/
|
|
51
|
+
onEnter(state: string, callback: (from: string | null) => void): () => void;
|
|
52
|
+
/**
|
|
53
|
+
* Subscribe to the moment the FSM exits `state`.
|
|
54
|
+
* @returns Unsubscribe function.
|
|
55
|
+
*/
|
|
56
|
+
onExit(state: string, callback: (to: string) => void): () => void;
|
|
57
|
+
/**
|
|
58
|
+
* Subscribe to any state change.
|
|
59
|
+
* @returns Unsubscribe function.
|
|
60
|
+
*/
|
|
61
|
+
onChange(callback: (from: string, to: string) => void): () => void;
|
|
62
|
+
/** Returns a snapshot of the transition history (oldest first). */
|
|
63
|
+
getHistory(): readonly StateTransitionEvent[];
|
|
64
|
+
/** Clears the transition history. */
|
|
65
|
+
clearHistory(): void;
|
|
28
66
|
/**
|
|
29
67
|
* Force transition to a new state.
|
|
30
68
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StateMachine.d.ts","sourceRoot":"","sources":["../../src/ai/StateMachine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,YAAY,GAAG,aAAa,CAAA;CAAE,CAAC;AAE/F,qBAAa,YAAY;IACvB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;
|
|
1
|
+
{"version":3,"file":"StateMachine.d.ts","sourceRoot":"","sources":["../../src/ai/StateMachine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,YAAY,GAAG,aAAa,CAAA;CAAE,CAAC;AAE/F,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IAEjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyD;IACxF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgD;IAC9E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiD;IACjF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8B;gBAE7C,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM;IAc5E,uCAAuC;IACvC,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,8EAA8E;IAC9E,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED,6DAA6D;IAC7D,IAAI,oBAAoB,IAAI,MAAM,CAEjC;IAMD,6DAA6D;IAC7D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAK5B,wEAAwE;IACxE,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAE5D;IAMD;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI;IAM3E;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAMjE;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IASlE,mEAAmE;IACnE,UAAU,IAAI,SAAS,oBAAoB,EAAE;IAI7C,qCAAqC;IACrC,YAAY,IAAI,IAAI;IAQpB;;;;;;OAMG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB;IAwC9C;;;;;;;;OAQG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAkB3B,iEAAiE;IACjE,OAAO,IAAI,IAAI;CAIhB"}
|
package/dist/ai/StateMachine.js
CHANGED
|
@@ -12,9 +12,15 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export class StateMachine {
|
|
14
14
|
constructor(entity, registry, initialState) {
|
|
15
|
+
this.previousStateId = null;
|
|
16
|
+
this.enterListeners = new Map();
|
|
17
|
+
this.exitListeners = new Map();
|
|
18
|
+
this.changeListeners = new Set();
|
|
19
|
+
this.historyLog = [];
|
|
15
20
|
this.entity = entity;
|
|
16
21
|
this.registry = registry;
|
|
17
22
|
this.currentStateId = initialState;
|
|
23
|
+
this.stateEnterTime = Date.now();
|
|
18
24
|
const definition = this.registry.get(this.currentStateId);
|
|
19
25
|
definition.handler.enter(this.entity);
|
|
20
26
|
}
|
|
@@ -25,6 +31,68 @@ export class StateMachine {
|
|
|
25
31
|
get state() {
|
|
26
32
|
return this.currentStateId;
|
|
27
33
|
}
|
|
34
|
+
/** Previous state identifier, or `null` if no transition has occurred yet. */
|
|
35
|
+
get previous() {
|
|
36
|
+
return this.previousStateId;
|
|
37
|
+
}
|
|
38
|
+
/** Milliseconds elapsed since entering the current state. */
|
|
39
|
+
get currentStateDuration() {
|
|
40
|
+
return Date.now() - this.stateEnterTime;
|
|
41
|
+
}
|
|
42
|
+
// -----------------------------------------------------------------------
|
|
43
|
+
// Tag queries
|
|
44
|
+
// -----------------------------------------------------------------------
|
|
45
|
+
/** Returns `true` if the current state has the given tag. */
|
|
46
|
+
hasTag(tag) {
|
|
47
|
+
const def = this.registry.tryGet(this.currentStateId);
|
|
48
|
+
return def?.tags?.includes(tag) ?? false;
|
|
49
|
+
}
|
|
50
|
+
/** Returns the metadata object of the current state, or `undefined`. */
|
|
51
|
+
get metadata() {
|
|
52
|
+
return this.registry.tryGet(this.currentStateId)?.metadata;
|
|
53
|
+
}
|
|
54
|
+
// -----------------------------------------------------------------------
|
|
55
|
+
// Event subscriptions
|
|
56
|
+
// -----------------------------------------------------------------------
|
|
57
|
+
/**
|
|
58
|
+
* Subscribe to the moment the FSM enters `state`.
|
|
59
|
+
* @returns Unsubscribe function.
|
|
60
|
+
*/
|
|
61
|
+
onEnter(state, callback) {
|
|
62
|
+
if (!this.enterListeners.has(state))
|
|
63
|
+
this.enterListeners.set(state, new Set());
|
|
64
|
+
this.enterListeners.get(state).add(callback);
|
|
65
|
+
return () => this.enterListeners.get(state)?.delete(callback);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Subscribe to the moment the FSM exits `state`.
|
|
69
|
+
* @returns Unsubscribe function.
|
|
70
|
+
*/
|
|
71
|
+
onExit(state, callback) {
|
|
72
|
+
if (!this.exitListeners.has(state))
|
|
73
|
+
this.exitListeners.set(state, new Set());
|
|
74
|
+
this.exitListeners.get(state).add(callback);
|
|
75
|
+
return () => this.exitListeners.get(state)?.delete(callback);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Subscribe to any state change.
|
|
79
|
+
* @returns Unsubscribe function.
|
|
80
|
+
*/
|
|
81
|
+
onChange(callback) {
|
|
82
|
+
this.changeListeners.add(callback);
|
|
83
|
+
return () => this.changeListeners.delete(callback);
|
|
84
|
+
}
|
|
85
|
+
// -----------------------------------------------------------------------
|
|
86
|
+
// History
|
|
87
|
+
// -----------------------------------------------------------------------
|
|
88
|
+
/** Returns a snapshot of the transition history (oldest first). */
|
|
89
|
+
getHistory() {
|
|
90
|
+
return [...this.historyLog];
|
|
91
|
+
}
|
|
92
|
+
/** Clears the transition history. */
|
|
93
|
+
clearHistory() {
|
|
94
|
+
this.historyLog.length = 0;
|
|
95
|
+
}
|
|
28
96
|
// -----------------------------------------------------------------------
|
|
29
97
|
// Transitions
|
|
30
98
|
// -----------------------------------------------------------------------
|
|
@@ -46,9 +114,21 @@ export class StateMachine {
|
|
|
46
114
|
return { success: false, reason: 'exit_guard' };
|
|
47
115
|
if (newDefinition.canEnter?.(this.entity, this.currentStateId) === false)
|
|
48
116
|
return { success: false, reason: 'enter_guard' };
|
|
117
|
+
const from = this.currentStateId;
|
|
118
|
+
// Exit
|
|
49
119
|
oldDefinition.handler.exit(this.entity);
|
|
120
|
+
this.exitListeners.get(from)?.forEach(cb => cb(newState));
|
|
121
|
+
// Advance state
|
|
122
|
+
this.previousStateId = from;
|
|
50
123
|
this.currentStateId = newState;
|
|
124
|
+
this.stateEnterTime = Date.now();
|
|
125
|
+
// Record history
|
|
126
|
+
this.historyLog.push({ from, to: newState, timestamp: this.stateEnterTime });
|
|
127
|
+
// Notify change listeners
|
|
128
|
+
this.changeListeners.forEach(cb => cb(from, newState));
|
|
129
|
+
// Enter
|
|
51
130
|
newDefinition.handler.enter(this.entity);
|
|
131
|
+
this.enterListeners.get(newState)?.forEach(cb => cb(from));
|
|
52
132
|
return { success: true };
|
|
53
133
|
}
|
|
54
134
|
// -----------------------------------------------------------------------
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StateMachine.js","sourceRoot":"","sources":["../../src/ai/StateMachine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"StateMachine.js","sourceRoot":"","sources":["../../src/ai/StateMachine.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/WorldState.d.ts
CHANGED
|
@@ -17,6 +17,14 @@ export type WorldStateValue = boolean | number | string;
|
|
|
17
17
|
export declare class WorldState {
|
|
18
18
|
private readonly properties;
|
|
19
19
|
set(key: string, value: WorldStateValue): void;
|
|
20
|
+
/**
|
|
21
|
+
* Create a WorldState from a plain object.
|
|
22
|
+
* Equivalent to calling set() for each key-value pair.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* const ws = WorldState.from({ hasAmmo: true, underFire: false, ammoCount: 3 });
|
|
26
|
+
*/
|
|
27
|
+
static from(props: Record<string, WorldStateValue>): WorldState;
|
|
20
28
|
get(key: string): WorldStateValue | undefined;
|
|
21
29
|
has(key: string): boolean;
|
|
22
30
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorldState.d.ts","sourceRoot":"","sources":["../../src/ai/WorldState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAExD,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsC;IAMjE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"WorldState.d.ts","sourceRoot":"","sources":["../../src/ai/WorldState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAExD,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsC;IAMjE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI;IAI9C;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,UAAU;IAY/D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAI7C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAQzB;;;;;;OAMG;IACH,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;IAQpC,0BAA0B;IAC1B,KAAK,IAAI,UAAU;IAQnB;;;;;;OAMG;IACH,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM;IAUrC;;;OAGG;IACH,YAAY,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU;IAQ7C,8EAA8E;IAC9E,IAAI,IAAI,gBAAgB,CAAC,MAAM,CAAC;CAGjC"}
|
package/dist/ai/WorldState.js
CHANGED
|
@@ -23,6 +23,20 @@ export class WorldState {
|
|
|
23
23
|
set(key, value) {
|
|
24
24
|
this.properties.set(key, value);
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a WorldState from a plain object.
|
|
28
|
+
* Equivalent to calling set() for each key-value pair.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const ws = WorldState.from({ hasAmmo: true, underFire: false, ammoCount: 3 });
|
|
32
|
+
*/
|
|
33
|
+
static from(props) {
|
|
34
|
+
const state = new WorldState();
|
|
35
|
+
for (const [key, value] of Object.entries(props)) {
|
|
36
|
+
state.set(key, value);
|
|
37
|
+
}
|
|
38
|
+
return state;
|
|
39
|
+
}
|
|
26
40
|
// -----------------------------------------------------------------------
|
|
27
41
|
// Accessors
|
|
28
42
|
// -----------------------------------------------------------------------
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorldState.js","sourceRoot":"","sources":["../../src/ai/WorldState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,OAAO,UAAU;IAAvB;QACmB,eAAU,GAAG,IAAI,GAAG,EAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"WorldState.js","sourceRoot":"","sources":["../../src/ai/WorldState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,OAAO,UAAU;IAAvB;QACmB,eAAU,GAAG,IAAI,GAAG,EAA2B,CAAC;IAkGnE,CAAC;IAhGC,0EAA0E;IAC1E,WAAW;IACX,0EAA0E;IAE1E,GAAG,CAAC,GAAW,EAAE,KAAsB;QACrC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,CAAC,KAAsC;QAChD,MAAM,KAAK,GAAG,IAAI,UAAU,EAAE,CAAC;QAC/B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,0EAA0E;IAC1E,YAAY;IACZ,0EAA0E;IAE1E,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,0EAA0E;IAC1E,qBAAqB;IACrB,0EAA0E;IAE1E;;;;;;OAMG;IACH,SAAS,CAAC,IAAgB;QACxB,KAAK,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC9C,IAAI,YAAY,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC;QAC/C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,0BAA0B;IAC1B,KAAK;QACH,MAAM,IAAI,GAAG,IAAI,UAAU,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACH,UAAU,CAAC,KAAiB;QAC1B,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YAC5C,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC;gBACvC,KAAK,EAAE,CAAC;YACV,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,OAAmB;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAC1B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YAC9C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8EAA8E;IAC9E,IAAI;QACF,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IAChC,CAAC;CACF"}
|
package/dist/ai/index.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
export { StateMachine } from './StateMachine';
|
|
2
|
-
export type { TransitionResult } from './StateMachine';
|
|
2
|
+
export type { TransitionResult, StateTransitionEvent } from './StateMachine';
|
|
3
3
|
export { MemoryBank, MemoryChannel } from './MemorySystem';
|
|
4
4
|
export type { MemoryRecord, IMemoryBankConfig, IMemoryInput } from './MemorySystem';
|
|
5
5
|
export { DangerManager, DangerType } from './DangerManager';
|
|
6
6
|
export type { IDangerEntry } from './DangerManager';
|
|
7
7
|
export { WorldState } from './WorldState';
|
|
8
|
+
export type { WorldStateValue } from './WorldState';
|
|
8
9
|
export { GOAPPlanner } from './GOAPPlanner';
|
|
9
10
|
export { GOAPAction, ActionStatus } from './GOAPAction';
|
|
11
|
+
export type { GOAPActionDef } from './GOAPAction';
|
|
12
|
+
export { Blackboard, Task, Condition, Sequence, Selector, Parallel, Inverter, AlwaysSucceed, AlwaysFail, Repeater, Cooldown } from './BehaviorTree';
|
|
13
|
+
export type { TaskStatus, ITreeNode, ParallelPolicy } from './BehaviorTree';
|
|
10
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/ai/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC3D,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACpJ,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/ai/index.js
CHANGED
|
@@ -5,4 +5,5 @@ export { DangerManager, DangerType } from './DangerManager';
|
|
|
5
5
|
export { WorldState } from './WorldState';
|
|
6
6
|
export { GOAPPlanner } from './GOAPPlanner';
|
|
7
7
|
export { GOAPAction, ActionStatus } from './GOAPAction';
|
|
8
|
+
export { Blackboard, Task, Condition, Sequence, Selector, Parallel, Inverter, AlwaysSucceed, AlwaysFail, Repeater, Cooldown } from './BehaviorTree';
|
|
8
9
|
//# sourceMappingURL=index.js.map
|
package/dist/ai/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReactiveQuery — observe when entities enter and exit a filtered set.
|
|
3
|
+
*
|
|
4
|
+
* Instead of polling the entire entity set every frame, a ReactiveQuery
|
|
5
|
+
* maintains a stable "matched" set and fires change notifications only when
|
|
6
|
+
* entities enter or leave the query (i.e. when the predicate result changes).
|
|
7
|
+
*
|
|
8
|
+
* Usage pattern:
|
|
9
|
+
* 1. Create a query with a predicate.
|
|
10
|
+
* 2. Subscribe to `onChange` to react to structural changes.
|
|
11
|
+
* 3. Call `update(allEntities)` each tick to re-evaluate.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const hostileQuery = new ReactiveQuery<IEntity>(
|
|
16
|
+
* (e) => e.isAlive && e.hasComponent('hostile')
|
|
17
|
+
* );
|
|
18
|
+
*
|
|
19
|
+
* hostileQuery.onChange(({ added, removed }) => {
|
|
20
|
+
* added.forEach(e => combatSystem.track(e));
|
|
21
|
+
* removed.forEach(e => combatSystem.untrack(e));
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Each tick:
|
|
25
|
+
* hostileQuery.update(world.entities());
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
/** Changes detected since the last `update()` call. */
|
|
29
|
+
export interface QueryChanges<T> {
|
|
30
|
+
/** Entities that newly matched the predicate this update. */
|
|
31
|
+
readonly added: readonly T[];
|
|
32
|
+
/** Entities that no longer match the predicate this update. */
|
|
33
|
+
readonly removed: readonly T[];
|
|
34
|
+
/** All entities currently matching the predicate after this update. */
|
|
35
|
+
readonly current: readonly T[];
|
|
36
|
+
}
|
|
37
|
+
/** Callback invoked when any entities are added or removed from the query. */
|
|
38
|
+
export type QueryChangeListener<T> = (changes: QueryChanges<T>) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Tracks which entities satisfy a predicate and fires change events when
|
|
41
|
+
* the matched set changes.
|
|
42
|
+
*/
|
|
43
|
+
export declare class ReactiveQuery<T> {
|
|
44
|
+
private readonly predicate;
|
|
45
|
+
private readonly matched;
|
|
46
|
+
private readonly listeners;
|
|
47
|
+
constructor(predicate: (entity: T) => boolean);
|
|
48
|
+
/**
|
|
49
|
+
* Re-evaluate the predicate against `allEntities`.
|
|
50
|
+
*
|
|
51
|
+
* Fires `onChange` listeners if the matched set changed.
|
|
52
|
+
* Call this once per tick from the owning system.
|
|
53
|
+
*/
|
|
54
|
+
update(allEntities: Iterable<T>): void;
|
|
55
|
+
/**
|
|
56
|
+
* Subscribe to change events. Called whenever entities enter or exit the
|
|
57
|
+
* matched set.
|
|
58
|
+
*
|
|
59
|
+
* @returns Unsubscribe function.
|
|
60
|
+
*/
|
|
61
|
+
onChange(listener: QueryChangeListener<T>): () => void;
|
|
62
|
+
/** All entities currently matching the predicate (stable snapshot). */
|
|
63
|
+
get current(): readonly T[];
|
|
64
|
+
/** Number of currently matched entities. */
|
|
65
|
+
get size(): number;
|
|
66
|
+
/** Return `true` if the entity is currently in the matched set. */
|
|
67
|
+
has(entity: T): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Manually add an entity to the matched set without re-evaluating the
|
|
70
|
+
* predicate. Fires `onChange` with the single addition.
|
|
71
|
+
*
|
|
72
|
+
* Useful when external code creates entities and knows they should match.
|
|
73
|
+
*/
|
|
74
|
+
track(entity: T): void;
|
|
75
|
+
/**
|
|
76
|
+
* Manually remove an entity from the matched set.
|
|
77
|
+
* Fires `onChange` with the single removal.
|
|
78
|
+
*
|
|
79
|
+
* Useful when entities are destroyed mid-tick.
|
|
80
|
+
*/
|
|
81
|
+
untrack(entity: T): void;
|
|
82
|
+
/** Remove all entities and clear all listeners. */
|
|
83
|
+
dispose(): void;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=ReactiveQuery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactiveQuery.d.ts","sourceRoot":"","sources":["../../src/core/ReactiveQuery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAMH,uDAAuD;AACvD,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;CAChC;AAED,8EAA8E;AAC9E,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAMxE;;;GAGG;AACH,qBAAa,aAAa,CAAC,CAAC;IAId,OAAO,CAAC,QAAQ,CAAC,SAAS;IAHtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqC;gBAElC,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO;IAM9D;;;;;OAKG;IACH,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI;IAwCtC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAStD,uEAAuE;IACvE,IAAI,OAAO,IAAI,SAAS,CAAC,EAAE,CAE1B;IAED,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,mEAAmE;IACnE,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO;IAIvB;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI;IAatB;;;;;OAKG;IACH,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI;IAaxB,mDAAmD;IACnD,OAAO,IAAI,IAAI;CAIhB"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReactiveQuery — observe when entities enter and exit a filtered set.
|
|
3
|
+
*
|
|
4
|
+
* Instead of polling the entire entity set every frame, a ReactiveQuery
|
|
5
|
+
* maintains a stable "matched" set and fires change notifications only when
|
|
6
|
+
* entities enter or leave the query (i.e. when the predicate result changes).
|
|
7
|
+
*
|
|
8
|
+
* Usage pattern:
|
|
9
|
+
* 1. Create a query with a predicate.
|
|
10
|
+
* 2. Subscribe to `onChange` to react to structural changes.
|
|
11
|
+
* 3. Call `update(allEntities)` each tick to re-evaluate.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const hostileQuery = new ReactiveQuery<IEntity>(
|
|
16
|
+
* (e) => e.isAlive && e.hasComponent('hostile')
|
|
17
|
+
* );
|
|
18
|
+
*
|
|
19
|
+
* hostileQuery.onChange(({ added, removed }) => {
|
|
20
|
+
* added.forEach(e => combatSystem.track(e));
|
|
21
|
+
* removed.forEach(e => combatSystem.untrack(e));
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Each tick:
|
|
25
|
+
* hostileQuery.update(world.entities());
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// ReactiveQuery
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Tracks which entities satisfy a predicate and fires change events when
|
|
33
|
+
* the matched set changes.
|
|
34
|
+
*/
|
|
35
|
+
export class ReactiveQuery {
|
|
36
|
+
constructor(predicate) {
|
|
37
|
+
this.predicate = predicate;
|
|
38
|
+
this.matched = new Set();
|
|
39
|
+
this.listeners = new Set();
|
|
40
|
+
}
|
|
41
|
+
// -------------------------------------------------------------------------
|
|
42
|
+
// Update
|
|
43
|
+
// -------------------------------------------------------------------------
|
|
44
|
+
/**
|
|
45
|
+
* Re-evaluate the predicate against `allEntities`.
|
|
46
|
+
*
|
|
47
|
+
* Fires `onChange` listeners if the matched set changed.
|
|
48
|
+
* Call this once per tick from the owning system.
|
|
49
|
+
*/
|
|
50
|
+
update(allEntities) {
|
|
51
|
+
const added = [];
|
|
52
|
+
const removed = [];
|
|
53
|
+
const nextMatched = new Set();
|
|
54
|
+
for (const entity of allEntities) {
|
|
55
|
+
if (this.predicate(entity)) {
|
|
56
|
+
nextMatched.add(entity);
|
|
57
|
+
if (!this.matched.has(entity)) {
|
|
58
|
+
added.push(entity);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const entity of this.matched) {
|
|
63
|
+
if (!nextMatched.has(entity)) {
|
|
64
|
+
removed.push(entity);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Commit new matched set
|
|
68
|
+
this.matched.clear();
|
|
69
|
+
for (const e of nextMatched)
|
|
70
|
+
this.matched.add(e);
|
|
71
|
+
if (added.length > 0 || removed.length > 0) {
|
|
72
|
+
const changes = {
|
|
73
|
+
added,
|
|
74
|
+
removed,
|
|
75
|
+
current: [...this.matched],
|
|
76
|
+
};
|
|
77
|
+
for (const listener of this.listeners) {
|
|
78
|
+
listener(changes);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// -------------------------------------------------------------------------
|
|
83
|
+
// Subscriptions
|
|
84
|
+
// -------------------------------------------------------------------------
|
|
85
|
+
/**
|
|
86
|
+
* Subscribe to change events. Called whenever entities enter or exit the
|
|
87
|
+
* matched set.
|
|
88
|
+
*
|
|
89
|
+
* @returns Unsubscribe function.
|
|
90
|
+
*/
|
|
91
|
+
onChange(listener) {
|
|
92
|
+
this.listeners.add(listener);
|
|
93
|
+
return () => this.listeners.delete(listener);
|
|
94
|
+
}
|
|
95
|
+
// -------------------------------------------------------------------------
|
|
96
|
+
// Accessors
|
|
97
|
+
// -------------------------------------------------------------------------
|
|
98
|
+
/** All entities currently matching the predicate (stable snapshot). */
|
|
99
|
+
get current() {
|
|
100
|
+
return [...this.matched];
|
|
101
|
+
}
|
|
102
|
+
/** Number of currently matched entities. */
|
|
103
|
+
get size() {
|
|
104
|
+
return this.matched.size;
|
|
105
|
+
}
|
|
106
|
+
/** Return `true` if the entity is currently in the matched set. */
|
|
107
|
+
has(entity) {
|
|
108
|
+
return this.matched.has(entity);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Manually add an entity to the matched set without re-evaluating the
|
|
112
|
+
* predicate. Fires `onChange` with the single addition.
|
|
113
|
+
*
|
|
114
|
+
* Useful when external code creates entities and knows they should match.
|
|
115
|
+
*/
|
|
116
|
+
track(entity) {
|
|
117
|
+
if (this.matched.has(entity))
|
|
118
|
+
return;
|
|
119
|
+
this.matched.add(entity);
|
|
120
|
+
const changes = {
|
|
121
|
+
added: [entity],
|
|
122
|
+
removed: [],
|
|
123
|
+
current: [...this.matched],
|
|
124
|
+
};
|
|
125
|
+
for (const listener of this.listeners) {
|
|
126
|
+
listener(changes);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Manually remove an entity from the matched set.
|
|
131
|
+
* Fires `onChange` with the single removal.
|
|
132
|
+
*
|
|
133
|
+
* Useful when entities are destroyed mid-tick.
|
|
134
|
+
*/
|
|
135
|
+
untrack(entity) {
|
|
136
|
+
if (!this.matched.has(entity))
|
|
137
|
+
return;
|
|
138
|
+
this.matched.delete(entity);
|
|
139
|
+
const changes = {
|
|
140
|
+
added: [],
|
|
141
|
+
removed: [entity],
|
|
142
|
+
current: [...this.matched],
|
|
143
|
+
};
|
|
144
|
+
for (const listener of this.listeners) {
|
|
145
|
+
listener(changes);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** Remove all entities and clear all listeners. */
|
|
149
|
+
dispose() {
|
|
150
|
+
this.matched.clear();
|
|
151
|
+
this.listeners.clear();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=ReactiveQuery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactiveQuery.js","sourceRoot":"","sources":["../../src/core/ReactiveQuery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EntityHandle — versioned entity references with use-after-free protection.
|
|
3
|
+
*
|
|
4
|
+
* Instead of holding raw entity references that can become dangling after an
|
|
5
|
+
* entity is destroyed, systems hold an EntityHandle. The handle encodes both
|
|
6
|
+
* the entity's slot index and a generation counter. When a slot is reused for
|
|
7
|
+
* a new entity the generation is bumped, making all old handles stale.
|
|
8
|
+
*
|
|
9
|
+
* Bit layout (fits in a JavaScript safe integer):
|
|
10
|
+
* [47..20] generation (28 bits, up to ~268 M versions per slot)
|
|
11
|
+
* [19.. 0] index (20 bits, up to ~1 M concurrent slots)
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const manager = new EntityHandleManager();
|
|
15
|
+
* const handle = manager.alloc('wolf-1');
|
|
16
|
+
* manager.resolve(handle); // → 'wolf-1'
|
|
17
|
+
* manager.free(handle);
|
|
18
|
+
* manager.resolve(handle); // → null (stale)
|
|
19
|
+
*/
|
|
20
|
+
/** Opaque numeric type that encodes (generation, index). */
|
|
21
|
+
export type EntityHandle = number & {
|
|
22
|
+
readonly __brand: 'EntityHandle';
|
|
23
|
+
};
|
|
24
|
+
/** Sentinel value for an absent or invalid handle. */
|
|
25
|
+
export declare const NULL_HANDLE: EntityHandle;
|
|
26
|
+
/** Pack (index, generation) into a single handle. */
|
|
27
|
+
export declare function makeHandle(index: number, generation: number): EntityHandle;
|
|
28
|
+
/** Extract the slot index from a handle. */
|
|
29
|
+
export declare function indexOf(handle: EntityHandle): number;
|
|
30
|
+
/** Extract the generation counter from a handle. */
|
|
31
|
+
export declare function genOf(handle: EntityHandle): number;
|
|
32
|
+
/** Return `true` if the handle is not the null sentinel. */
|
|
33
|
+
export declare function isValidHandle(handle: EntityHandle): boolean;
|
|
34
|
+
/** Human-readable description for logging / debugging. */
|
|
35
|
+
export declare function handleToString(handle: EntityHandle): string;
|
|
36
|
+
/**
|
|
37
|
+
* Central registry that owns the slot → entity-id mapping.
|
|
38
|
+
*
|
|
39
|
+
* Recycles freed slots so slot count stays bounded. Old handles pointing at
|
|
40
|
+
* recycled slots resolve to `null` because their stored generation no longer
|
|
41
|
+
* matches the slot's current generation.
|
|
42
|
+
*/
|
|
43
|
+
export declare class EntityHandleManager<TId = string> {
|
|
44
|
+
/** Generation counter per slot (index = slot index). */
|
|
45
|
+
private readonly generations;
|
|
46
|
+
/** Entity ID stored in each live slot. `null` = free. */
|
|
47
|
+
private readonly ids;
|
|
48
|
+
/** Slot indices available for reuse. */
|
|
49
|
+
private readonly freeList;
|
|
50
|
+
/** Next slot to allocate when freeList is empty. */
|
|
51
|
+
private nextSlot;
|
|
52
|
+
/**
|
|
53
|
+
* Allocate a new handle for the given entity id.
|
|
54
|
+
* @throws if the slot limit is exhausted.
|
|
55
|
+
*/
|
|
56
|
+
alloc(id: TId): EntityHandle;
|
|
57
|
+
/**
|
|
58
|
+
* Release a handle, incrementing the slot's generation.
|
|
59
|
+
* All existing handles pointing at this slot become stale.
|
|
60
|
+
*
|
|
61
|
+
* Does nothing (and does not throw) if the handle is already stale.
|
|
62
|
+
*/
|
|
63
|
+
free(handle: EntityHandle): void;
|
|
64
|
+
/**
|
|
65
|
+
* Resolve a handle to its entity id.
|
|
66
|
+
* Returns `null` if the handle is stale (entity was freed) or null.
|
|
67
|
+
*/
|
|
68
|
+
resolve(handle: EntityHandle): TId | null;
|
|
69
|
+
/**
|
|
70
|
+
* Return `true` if the handle points to a currently-live slot.
|
|
71
|
+
*/
|
|
72
|
+
isAlive(handle: EntityHandle): boolean;
|
|
73
|
+
/** Number of currently-live slots. */
|
|
74
|
+
get size(): number;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=EntityHandle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityHandle.d.ts","sourceRoot":"","sources":["../../src/entity/EntityHandle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAgBH,4DAA4D;AAC5D,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAA;CAAE,CAAC;AAEzE,sDAAsD;AACtD,eAAO,MAAM,WAAW,EAAQ,YAAY,CAAC;AAE7C,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY,CAG1E;AAED,4CAA4C;AAC5C,wBAAgB,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAEpD;AAED,oDAAoD;AACpD,wBAAgB,KAAK,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAElD;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAE3D;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAG3D;AAMD;;;;;;GAMG;AACH,qBAAa,mBAAmB,CAAC,GAAG,GAAG,MAAM;IAC3C,wDAAwD;IACxD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAC5C,yDAAyD;IACzD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAsB;IAC1C,wCAAwC;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,oDAAoD;IACpD,OAAO,CAAC,QAAQ,CAAK;IAErB;;;OAGG;IACH,KAAK,CAAC,EAAE,EAAE,GAAG,GAAG,YAAY;IAiB5B;;;;;OAKG;IACH,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAQhC;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,GAAG,GAAG,IAAI;IAOzC;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO;IAMtC,sCAAsC;IACtC,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|