@phalanx-engine/ecs 0.1.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.
Files changed (89) hide show
  1. package/README.md +666 -0
  2. package/dist/Component.d.ts +7 -0
  3. package/dist/Component.d.ts.map +1 -0
  4. package/dist/Component.js +7 -0
  5. package/dist/Entity.d.ts +25 -0
  6. package/dist/Entity.d.ts.map +1 -0
  7. package/dist/Entity.js +58 -0
  8. package/dist/EntityManager.d.ts +36 -0
  9. package/dist/EntityManager.d.ts.map +1 -0
  10. package/dist/EntityManager.js +218 -0
  11. package/dist/EventBus.d.ts +15 -0
  12. package/dist/EventBus.d.ts.map +1 -0
  13. package/dist/EventBus.js +69 -0
  14. package/dist/GameSystem.d.ts +23 -0
  15. package/dist/GameSystem.d.ts.map +1 -0
  16. package/dist/GameSystem.js +43 -0
  17. package/dist/GameWorld.d.ts +69 -0
  18. package/dist/GameWorld.d.ts.map +1 -0
  19. package/dist/GameWorld.js +237 -0
  20. package/dist/IAbilitySystem.d.ts +18 -0
  21. package/dist/IAbilitySystem.d.ts.map +1 -0
  22. package/dist/IAbilitySystem.js +1 -0
  23. package/dist/IPhysicsWorld.d.ts +21 -0
  24. package/dist/IPhysicsWorld.d.ts.map +1 -0
  25. package/dist/IPhysicsWorld.js +1 -0
  26. package/dist/ISystemLifecycleHooks.d.ts +18 -0
  27. package/dist/ISystemLifecycleHooks.d.ts.map +1 -0
  28. package/dist/ISystemLifecycleHooks.js +12 -0
  29. package/dist/ITickFrameProvider.d.ts +24 -0
  30. package/dist/ITickFrameProvider.d.ts.map +1 -0
  31. package/dist/ITickFrameProvider.js +1 -0
  32. package/dist/SoAComponent.d.ts +22 -0
  33. package/dist/SoAComponent.d.ts.map +1 -0
  34. package/dist/SoAComponent.js +59 -0
  35. package/dist/SoAComponentStore.d.ts +41 -0
  36. package/dist/SoAComponentStore.d.ts.map +1 -0
  37. package/dist/SoAComponentStore.js +253 -0
  38. package/dist/SoASchema.d.ts +22 -0
  39. package/dist/SoASchema.d.ts.map +1 -0
  40. package/dist/SoASchema.js +33 -0
  41. package/dist/SystemContext.d.ts +18 -0
  42. package/dist/SystemContext.d.ts.map +1 -0
  43. package/dist/SystemContext.js +18 -0
  44. package/dist/SystemRegistry.d.ts +20 -0
  45. package/dist/SystemRegistry.d.ts.map +1 -0
  46. package/dist/SystemRegistry.js +73 -0
  47. package/dist/TickFrameManager.d.ts +35 -0
  48. package/dist/TickFrameManager.d.ts.map +1 -0
  49. package/dist/TickFrameManager.js +130 -0
  50. package/dist/debug/DebugDataProvider.d.ts +26 -0
  51. package/dist/debug/DebugDataProvider.d.ts.map +1 -0
  52. package/dist/debug/DebugDataProvider.js +128 -0
  53. package/dist/debug/DebugPanel.d.ts +57 -0
  54. package/dist/debug/DebugPanel.d.ts.map +1 -0
  55. package/dist/debug/DebugPanel.js +482 -0
  56. package/dist/debug/index.d.ts +4 -0
  57. package/dist/debug/index.d.ts.map +1 -0
  58. package/dist/debug/index.js +2 -0
  59. package/dist/debug/types.d.ts +47 -0
  60. package/dist/debug/types.d.ts.map +1 -0
  61. package/dist/debug/types.js +1 -0
  62. package/dist/index.d.ts +27 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +15 -0
  65. package/dist/pool/EntityPool.d.ts +21 -0
  66. package/dist/pool/EntityPool.d.ts.map +1 -0
  67. package/dist/pool/EntityPool.js +86 -0
  68. package/dist/pool/IPoolable.d.ts +4 -0
  69. package/dist/pool/IPoolable.d.ts.map +1 -0
  70. package/dist/pool/IPoolable.js +1 -0
  71. package/dist/pool/IPoolableComponent.d.ts +7 -0
  72. package/dist/pool/IPoolableComponent.d.ts.map +1 -0
  73. package/dist/pool/IPoolableComponent.js +4 -0
  74. package/dist/pool/IPoolableEntity.d.ts +6 -0
  75. package/dist/pool/IPoolableEntity.d.ts.map +1 -0
  76. package/dist/pool/IPoolableEntity.js +1 -0
  77. package/dist/pool/ObjectPool.d.ts +20 -0
  78. package/dist/pool/ObjectPool.d.ts.map +1 -0
  79. package/dist/pool/ObjectPool.js +76 -0
  80. package/dist/pool/PoolManager.d.ts +20 -0
  81. package/dist/pool/PoolManager.d.ts.map +1 -0
  82. package/dist/pool/PoolManager.js +92 -0
  83. package/dist/pool/index.d.ts +10 -0
  84. package/dist/pool/index.d.ts.map +1 -0
  85. package/dist/pool/index.js +5 -0
  86. package/dist/pool/types.d.ts +31 -0
  87. package/dist/pool/types.d.ts.map +1 -0
  88. package/dist/pool/types.js +8 -0
  89. package/package.json +46 -0
@@ -0,0 +1,253 @@
1
+ import { TYPED_ARRAY_CONSTRUCTORS, } from './SoASchema';
2
+ const DEFAULT_INITIAL_CAPACITY = 256;
3
+ const GROWTH_FACTOR = 2;
4
+ const SHRINK_THRESHOLD_SHIFT = 2;
5
+ function setTypedArrayValue(arr, index, value) {
6
+ if (arr instanceof BigInt64Array) {
7
+ arr[index] = value;
8
+ }
9
+ else {
10
+ arr[index] = value;
11
+ }
12
+ }
13
+ function getTypedArrayValue(arr, index) {
14
+ return arr[index];
15
+ }
16
+ export class SoAComponentStore {
17
+ schema;
18
+ arrays;
19
+ _count = 0;
20
+ _capacity;
21
+ _minCapacity;
22
+ _autoShrink;
23
+ _fieldArrays = [];
24
+ _fieldIsBigInt = [];
25
+ entityToIndex = new Map();
26
+ indexToEntity = [];
27
+ _sortedEntityIds = [];
28
+ binarySearchInsertIndex(arr, value) {
29
+ let low = 0;
30
+ let high = arr.length;
31
+ while (low < high) {
32
+ const mid = (low + high) >>> 1;
33
+ if (arr[mid] < value) {
34
+ low = mid + 1;
35
+ }
36
+ else {
37
+ high = mid;
38
+ }
39
+ }
40
+ return low;
41
+ }
42
+ constructor(schema, initialCapacityOrOptions = DEFAULT_INITIAL_CAPACITY) {
43
+ const options = typeof initialCapacityOrOptions === 'number'
44
+ ? { initialCapacity: initialCapacityOrOptions }
45
+ : initialCapacityOrOptions;
46
+ const initialCapacity = Math.max(1, options.initialCapacity ?? DEFAULT_INITIAL_CAPACITY);
47
+ this.schema = schema;
48
+ this._capacity = initialCapacity;
49
+ this._minCapacity = initialCapacity;
50
+ this._autoShrink = options.autoShrink ?? false;
51
+ const arrays = {};
52
+ for (const fieldName of schema.fieldNames) {
53
+ const fieldType = schema.fieldTypes[fieldName];
54
+ const ArrayConstructor = TYPED_ARRAY_CONSTRUCTORS[fieldType];
55
+ arrays[fieldName] = new ArrayConstructor(initialCapacity);
56
+ }
57
+ this.arrays = arrays;
58
+ this.rebuildFieldCache();
59
+ }
60
+ rebuildFieldCache() {
61
+ const names = this.schema.fieldNames;
62
+ this._fieldArrays = new Array(names.length);
63
+ this._fieldIsBigInt = new Array(names.length);
64
+ for (let f = 0; f < names.length; f++) {
65
+ const arr = this.arrays[names[f]];
66
+ this._fieldArrays[f] = arr;
67
+ this._fieldIsBigInt[f] = arr instanceof BigInt64Array;
68
+ }
69
+ }
70
+ get count() {
71
+ return this._count;
72
+ }
73
+ get capacity() {
74
+ return this._capacity;
75
+ }
76
+ has(entityId) {
77
+ return this.entityToIndex.has(entityId);
78
+ }
79
+ indexOf(entityId) {
80
+ return this.entityToIndex.get(entityId) ?? -1;
81
+ }
82
+ entityAt(index) {
83
+ return index >= 0 && index < this._count ? this.indexToEntity[index] : -1;
84
+ }
85
+ entityIds() {
86
+ return this._sortedEntityIds;
87
+ }
88
+ add(entityId, values) {
89
+ if (this.entityToIndex.has(entityId)) {
90
+ throw new Error(`Entity ${entityId} already exists in SoA store`);
91
+ }
92
+ if (this._count >= this._capacity) {
93
+ this.grow();
94
+ }
95
+ const index = this._count++;
96
+ this.entityToIndex.set(entityId, index);
97
+ this.indexToEntity[index] = entityId;
98
+ const insertIdx = this.binarySearchInsertIndex(this._sortedEntityIds, entityId);
99
+ this._sortedEntityIds.splice(insertIdx, 0, entityId);
100
+ const names = this.schema.fieldNames;
101
+ for (let f = 0; f < this._fieldArrays.length; f++) {
102
+ const value = values[names[f]];
103
+ if (this._fieldIsBigInt[f]) {
104
+ this._fieldArrays[f][index] = value;
105
+ }
106
+ else {
107
+ this._fieldArrays[f][index] = value;
108
+ }
109
+ }
110
+ return index;
111
+ }
112
+ remove(entityId) {
113
+ const index = this.entityToIndex.get(entityId);
114
+ if (index === undefined) {
115
+ return false;
116
+ }
117
+ const lastIndex = this._count - 1;
118
+ if (index !== lastIndex) {
119
+ const lastEntityId = this.indexToEntity[lastIndex];
120
+ for (let f = 0; f < this._fieldArrays.length; f++) {
121
+ if (this._fieldIsBigInt[f]) {
122
+ const arr = this._fieldArrays[f];
123
+ arr[index] = arr[lastIndex];
124
+ }
125
+ else {
126
+ const arr = this._fieldArrays[f];
127
+ arr[index] = arr[lastIndex];
128
+ }
129
+ }
130
+ this.entityToIndex.set(lastEntityId, index);
131
+ this.indexToEntity[index] = lastEntityId;
132
+ }
133
+ this.entityToIndex.delete(entityId);
134
+ this.indexToEntity.length = lastIndex;
135
+ this._count = lastIndex;
136
+ const sortedIdx = this.binarySearchInsertIndex(this._sortedEntityIds, entityId);
137
+ if (this._sortedEntityIds[sortedIdx] === entityId) {
138
+ this._sortedEntityIds.splice(sortedIdx, 1);
139
+ }
140
+ this.maybeAutoShrink();
141
+ return true;
142
+ }
143
+ get(entityId) {
144
+ const index = this.entityToIndex.get(entityId);
145
+ if (index === undefined) {
146
+ return undefined;
147
+ }
148
+ const result = {};
149
+ for (const fieldName of this.schema.fieldNames) {
150
+ const arr = this.arrays[fieldName];
151
+ result[fieldName] = getTypedArrayValue(arr, index);
152
+ }
153
+ return result;
154
+ }
155
+ set(entityId, values) {
156
+ const index = this.entityToIndex.get(entityId);
157
+ if (index === undefined) {
158
+ return false;
159
+ }
160
+ for (const fieldName of this.schema.fieldNames) {
161
+ if (fieldName in values) {
162
+ const arr = this.arrays[fieldName];
163
+ const value = values[fieldName];
164
+ setTypedArrayValue(arr, index, value);
165
+ }
166
+ }
167
+ return true;
168
+ }
169
+ getField(entityId, fieldName) {
170
+ const index = this.entityToIndex.get(entityId);
171
+ if (index === undefined) {
172
+ return undefined;
173
+ }
174
+ const arr = this.arrays[fieldName];
175
+ return getTypedArrayValue(arr, index);
176
+ }
177
+ setField(entityId, fieldName, value) {
178
+ const index = this.entityToIndex.get(entityId);
179
+ if (index === undefined) {
180
+ return false;
181
+ }
182
+ const arr = this.arrays[fieldName];
183
+ setTypedArrayValue(arr, index, value);
184
+ return true;
185
+ }
186
+ clear() {
187
+ this._count = 0;
188
+ this.entityToIndex.clear();
189
+ this.indexToEntity.length = 0;
190
+ this._sortedEntityIds.length = 0;
191
+ for (const fieldName of this.schema.fieldNames) {
192
+ const arr = this.arrays[fieldName];
193
+ if (arr instanceof BigInt64Array) {
194
+ arr.fill(0n);
195
+ }
196
+ else {
197
+ arr.fill(0);
198
+ }
199
+ }
200
+ }
201
+ grow() {
202
+ const newCapacity = this._capacity * GROWTH_FACTOR;
203
+ this.resize(newCapacity);
204
+ }
205
+ resize(newCapacity) {
206
+ const copyLen = Math.min(this._count, newCapacity);
207
+ for (const fieldName of this.schema.fieldNames) {
208
+ const oldArray = this.arrays[fieldName];
209
+ const fieldType = this.schema.fieldTypes[fieldName];
210
+ const ArrayConstructor = TYPED_ARRAY_CONSTRUCTORS[fieldType];
211
+ const newArray = new ArrayConstructor(newCapacity);
212
+ if (oldArray instanceof BigInt64Array) {
213
+ newArray.set(oldArray.subarray(0, copyLen));
214
+ }
215
+ else {
216
+ newArray.set(oldArray.subarray(0, copyLen));
217
+ }
218
+ this.arrays[fieldName] = newArray;
219
+ }
220
+ this._capacity = newCapacity;
221
+ this.rebuildFieldCache();
222
+ }
223
+ maybeAutoShrink() {
224
+ if (!this._autoShrink)
225
+ return;
226
+ if (this._capacity <= this._minCapacity)
227
+ return;
228
+ if (this._count > this._capacity >>> SHRINK_THRESHOLD_SHIFT)
229
+ return;
230
+ const target = Math.max(this._minCapacity, this._count * GROWTH_FACTOR);
231
+ if (target < this._capacity) {
232
+ this.resize(target);
233
+ }
234
+ }
235
+ shrinkToFit(minCapacity = this._minCapacity) {
236
+ const target = Math.max(1, minCapacity, this._count);
237
+ if (target < this._capacity) {
238
+ this.resize(target);
239
+ }
240
+ return this._capacity;
241
+ }
242
+ forEachDense(callback) {
243
+ for (let i = 0; i < this._count; i++) {
244
+ callback(this.indexToEntity[i], i);
245
+ }
246
+ }
247
+ forEach(callback) {
248
+ for (const entityId of this._sortedEntityIds) {
249
+ const index = this.entityToIndex.get(entityId);
250
+ callback(entityId, index);
251
+ }
252
+ }
253
+ }
@@ -0,0 +1,22 @@
1
+ export type SoAFieldType = 'f32' | 'f64' | 'i32' | 'u32' | 'u8' | 'i64';
2
+ export declare const TYPED_ARRAY_CONSTRUCTORS: Record<SoAFieldType, new (length: number) => TypedArrayLike>;
3
+ export declare const FIELD_BYTE_SIZES: Record<SoAFieldType, number>;
4
+ export type TypedArrayLike = Float32Array | Float64Array | Int32Array | Uint32Array | Uint8Array | BigInt64Array;
5
+ export type SoASchemaDefinition = Record<string, SoAFieldType>;
6
+ export type SoAValueType<T extends SoAFieldType> = T extends 'i64' ? bigint : number;
7
+ export type SoAArrayType<T extends SoAFieldType> = T extends 'f32' ? Float32Array : T extends 'f64' ? Float64Array : T extends 'i32' ? Int32Array : T extends 'u32' ? Uint32Array : T extends 'u8' ? Uint8Array : T extends 'i64' ? BigInt64Array : never;
8
+ export type SoAFieldsOf<S extends SoASchemaDefinition> = {
9
+ [K in keyof S]: SoAValueType<S[K]>;
10
+ };
11
+ export type SoAArraysOf<S extends SoASchemaDefinition> = {
12
+ [K in keyof S]: SoAArrayType<S[K]>;
13
+ };
14
+ export interface SoASchema<S extends SoASchemaDefinition = SoASchemaDefinition> {
15
+ readonly definition: S;
16
+ readonly fieldNames: (keyof S & string)[];
17
+ readonly fieldTypes: S;
18
+ readonly type: symbol;
19
+ }
20
+ export declare function defineSoASchema<S extends SoASchemaDefinition>(definition: S, name?: string): SoASchema<S>;
21
+ export declare function calculateSchemaByteSize(schema: SoASchema): number;
22
+ //# sourceMappingURL=SoASchema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SoASchema.d.ts","sourceRoot":"","sources":["../src/SoASchema.ts"],"names":[],"mappings":"AAiCA,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;AAKxE,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,KAAK,cAAc,CAOjG,CAAC;AAKF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAOzD,CAAC;AAKF,MAAM,MAAM,cAAc,GACtB,YAAY,GACZ,YAAY,GACZ,UAAU,GACV,WAAW,GACX,UAAU,GACV,aAAa,CAAC;AAKlB,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAK/D,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,YAAY,IAC7C,CAAC,SAAS,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAKpC,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,YAAY,IAC7C,CAAC,SAAS,KAAK,GAAG,YAAY,GAC9B,CAAC,SAAS,KAAK,GAAG,YAAY,GAC9B,CAAC,SAAS,KAAK,GAAG,UAAU,GAC5B,CAAC,SAAS,KAAK,GAAG,WAAW,GAC7B,CAAC,SAAS,IAAI,GAAG,UAAU,GAC3B,CAAC,SAAS,KAAK,GAAG,aAAa,GAC/B,KAAK,CAAC;AAKR,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,mBAAmB,IAAI;KACtD,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACnC,CAAC;AAKF,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,mBAAmB,IAAI;KACtD,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACnC,CAAC;AAKF,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,mBAAmB,GAAG,mBAAmB;IAE5E,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAEvB,QAAQ,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC;IAE1C,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAEvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAkBD,wBAAgB,eAAe,CAAC,CAAC,SAAS,mBAAmB,EAC3D,UAAU,EAAE,CAAC,EACb,IAAI,CAAC,EAAE,MAAM,GACZ,SAAS,CAAC,CAAC,CAAC,CASd;AAMD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAOjE"}
@@ -0,0 +1,33 @@
1
+ export const TYPED_ARRAY_CONSTRUCTORS = {
2
+ f32: Float32Array,
3
+ f64: Float64Array,
4
+ i32: Int32Array,
5
+ u32: Uint32Array,
6
+ u8: Uint8Array,
7
+ i64: BigInt64Array,
8
+ };
9
+ export const FIELD_BYTE_SIZES = {
10
+ f32: 4,
11
+ f64: 8,
12
+ i32: 4,
13
+ u32: 4,
14
+ u8: 1,
15
+ i64: 8,
16
+ };
17
+ export function defineSoASchema(definition, name) {
18
+ const fieldNames = Object.keys(definition);
19
+ return {
20
+ definition,
21
+ fieldNames,
22
+ fieldTypes: definition,
23
+ type: Symbol(name ?? 'SoASchema'),
24
+ };
25
+ }
26
+ export function calculateSchemaByteSize(schema) {
27
+ let total = 0;
28
+ for (const fieldName of schema.fieldNames) {
29
+ const fieldType = schema.fieldTypes[fieldName];
30
+ total += FIELD_BYTE_SIZES[fieldType];
31
+ }
32
+ return total;
33
+ }
@@ -0,0 +1,18 @@
1
+ import type { EventBus } from './EventBus';
2
+ import type { EntityManager } from './EntityManager';
3
+ import type { GameSystem } from './GameSystem';
4
+ import type { IAbilitySystem } from './IAbilitySystem';
5
+ import type { IPhysicsWorld } from './IPhysicsWorld';
6
+ import type { PoolManager } from './pool/PoolManager';
7
+ export declare class SystemContext {
8
+ readonly eventBus: EventBus;
9
+ readonly entityManager: EntityManager;
10
+ private systemRegistry;
11
+ abilities: IAbilitySystem | undefined;
12
+ physics: IPhysicsWorld | undefined;
13
+ pools: PoolManager | null;
14
+ constructor(eventBus: EventBus, entityManager: EntityManager);
15
+ registerSystem(system: GameSystem): void;
16
+ getSystem<T extends GameSystem>(systemClass: new (...args: any[]) => T): T | undefined;
17
+ }
18
+ //# sourceMappingURL=SystemContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SystemContext.d.ts","sourceRoot":"","sources":["../src/SystemContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAoBtD,qBAAa,aAAa;IAExB,SAAgB,QAAQ,EAAE,QAAQ,CAAC;IAGnC,SAAgB,aAAa,EAAE,aAAa,CAAC;IAG7C,OAAO,CAAC,cAAc,CAAwC;IAQvD,SAAS,EAAE,cAAc,GAAG,SAAS,CAAa;IAQlD,OAAO,EAAE,aAAa,GAAG,SAAS,CAAa;IAO/C,KAAK,EAAE,WAAW,GAAG,IAAI,CAAQ;gBAGtC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,aAAa;IAUvB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAWxC,SAAS,CAAC,CAAC,SAAS,UAAU,EAAE,WAAW,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS;CAG9F"}
@@ -0,0 +1,18 @@
1
+ export class SystemContext {
2
+ eventBus;
3
+ entityManager;
4
+ systemRegistry = new Map();
5
+ abilities = undefined;
6
+ physics = undefined;
7
+ pools = null;
8
+ constructor(eventBus, entityManager) {
9
+ this.eventBus = eventBus;
10
+ this.entityManager = entityManager;
11
+ }
12
+ registerSystem(system) {
13
+ this.systemRegistry.set(system.constructor, system);
14
+ }
15
+ getSystem(systemClass) {
16
+ return this.systemRegistry.get(systemClass);
17
+ }
18
+ }
@@ -0,0 +1,20 @@
1
+ import { EntityManager } from './EntityManager';
2
+ import { EventBus } from './EventBus';
3
+ import { SystemContext } from './SystemContext';
4
+ import type { GameSystem } from './GameSystem';
5
+ export declare class SystemRegistry {
6
+ readonly eventBus: EventBus;
7
+ readonly entityManager: EntityManager;
8
+ private readonly context;
9
+ private tickSystems;
10
+ private frameSystems;
11
+ constructor(componentTypes?: symbol[]);
12
+ registerSystems(tickSystems: GameSystem[], frameSystems: GameSystem[]): void;
13
+ getAllSystems(): Set<GameSystem>;
14
+ addFrameSystem(system: GameSystem): void;
15
+ processAllTicks(tick: number): void;
16
+ updateAll(deltaTime: number): void;
17
+ getContext(): SystemContext;
18
+ dispose(): void;
19
+ }
20
+ //# sourceMappingURL=SystemRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SystemRegistry.d.ts","sourceRoot":"","sources":["../src/SystemRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAU/C,qBAAa,cAAc;IAEzB,SAAgB,QAAQ,EAAE,QAAQ,CAAC;IACnC,SAAgB,aAAa,EAAE,aAAa,CAAC;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IAMxC,OAAO,CAAC,WAAW,CAAoB;IAKvC,OAAO,CAAC,YAAY,CAAoB;gBAE5B,cAAc,CAAC,EAAE,MAAM,EAAE;IAwB9B,eAAe,CACpB,WAAW,EAAE,UAAU,EAAE,EACzB,YAAY,EAAE,UAAU,EAAE,GACzB,IAAI;IAoCA,aAAa,IAAI,GAAG,CAAC,UAAU,CAAC;IAShC,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAWxC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAanC,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAWlC,UAAU,IAAI,aAAa;IAO3B,OAAO,IAAI,IAAI;CAevB"}
@@ -0,0 +1,73 @@
1
+ import { EntityManager } from './EntityManager';
2
+ import { EventBus } from './EventBus';
3
+ import { SystemContext } from './SystemContext';
4
+ export class SystemRegistry {
5
+ eventBus;
6
+ entityManager;
7
+ context;
8
+ tickSystems = [];
9
+ frameSystems = [];
10
+ constructor(componentTypes) {
11
+ this.eventBus = new EventBus();
12
+ this.entityManager = new EntityManager();
13
+ if (componentTypes) {
14
+ this.entityManager.registerComponentTypes(componentTypes);
15
+ }
16
+ this.context = new SystemContext(this.eventBus, this.entityManager);
17
+ }
18
+ registerSystems(tickSystems, frameSystems) {
19
+ this.tickSystems = tickSystems;
20
+ this.frameSystems = frameSystems;
21
+ const allSystems = new Set([...tickSystems, ...frameSystems]);
22
+ for (const system of allSystems) {
23
+ this.context.registerSystem(system);
24
+ }
25
+ if (this.context.abilities) {
26
+ for (const system of this.context.abilities.tickSystems) {
27
+ if (this.tickSystems.includes(system)) {
28
+ continue;
29
+ }
30
+ this.tickSystems.push(system);
31
+ this.context.registerSystem(system);
32
+ }
33
+ }
34
+ const toInit = new Set([...this.tickSystems, ...this.frameSystems]);
35
+ for (const system of toInit) {
36
+ system.init(this.context);
37
+ }
38
+ }
39
+ getAllSystems() {
40
+ return new Set([...this.tickSystems, ...this.frameSystems]);
41
+ }
42
+ addFrameSystem(system) {
43
+ this.frameSystems.push(system);
44
+ this.context.registerSystem(system);
45
+ }
46
+ processAllTicks(tick) {
47
+ for (const system of this.tickSystems) {
48
+ if (system.enabled) {
49
+ system.processTick(tick);
50
+ }
51
+ }
52
+ }
53
+ updateAll(deltaTime) {
54
+ for (const system of this.frameSystems) {
55
+ if (system.enabled) {
56
+ system.update(deltaTime);
57
+ }
58
+ }
59
+ }
60
+ getContext() {
61
+ return this.context;
62
+ }
63
+ dispose() {
64
+ for (let i = this.tickSystems.length - 1; i >= 0; i--) {
65
+ this.tickSystems[i].dispose();
66
+ }
67
+ for (let i = this.frameSystems.length - 1; i >= 0; i--) {
68
+ this.frameSystems[i].dispose();
69
+ }
70
+ this.eventBus?.clearAll();
71
+ this.entityManager?.clear();
72
+ }
73
+ }
@@ -0,0 +1,35 @@
1
+ import type { ITickFrameProvider, TickHandler, FrameHandler, Unsubscribe, PauseHandler } from './ITickFrameProvider';
2
+ export interface TickFrameManagerConfig {
3
+ tickRate?: number;
4
+ maxFrameTime?: number;
5
+ }
6
+ export declare class TickFrameManager implements ITickFrameProvider {
7
+ private tickRate;
8
+ private tickDuration;
9
+ private maxFrameTime;
10
+ private tickCallbacks;
11
+ private frameCallbacks;
12
+ private pauseHandlers;
13
+ private resumeHandlers;
14
+ private currentTick;
15
+ private accumulator;
16
+ private lastTime;
17
+ private isRunning;
18
+ private _isPaused;
19
+ private rafId;
20
+ constructor(config?: TickFrameManagerConfig);
21
+ onTick(callback: TickHandler): Unsubscribe;
22
+ onFrame(callback: FrameHandler): Unsubscribe;
23
+ start(): void;
24
+ stop(): void;
25
+ private loop;
26
+ requestPause(): void;
27
+ requestResume(): void;
28
+ onPause(handler: PauseHandler): Unsubscribe;
29
+ onResume(handler: PauseHandler): Unsubscribe;
30
+ getCurrentTick(): number;
31
+ getTickRate(): number;
32
+ isActive(): boolean;
33
+ dispose(): void;
34
+ }
35
+ //# sourceMappingURL=TickFrameManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TickFrameManager.d.ts","sourceRoot":"","sources":["../src/TickFrameManager.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,WAAW,EAEX,YAAY,EACb,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,sBAAsB;IAErC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,gBAAiB,YAAW,kBAAkB;IACzD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,cAAc,CAAsB;IAC5C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,cAAc,CAAsB;IAE5C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,KAAK,CAAuB;gBAExB,MAAM,CAAC,EAAE,sBAAsB;IAYpC,MAAM,CAAC,QAAQ,EAAE,WAAW,GAAG,WAAW;IAe1C,OAAO,CAAC,QAAQ,EAAE,YAAY,GAAG,WAAW;IAa5C,KAAK,IAAI,IAAI;IAgBb,IAAI,IAAI,IAAI;IAYnB,OAAO,CAAC,IAAI,CA2CV;IAOK,YAAY,IAAI,IAAI;IAUpB,aAAa,IAAI,IAAI;IAUrB,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW;IAW3C,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW;IAW5C,cAAc,IAAI,MAAM;IAOxB,WAAW,IAAI,MAAM;IAOrB,QAAQ,IAAI,OAAO;IAOnB,OAAO,IAAI,IAAI;CAOvB"}
@@ -0,0 +1,130 @@
1
+ export class TickFrameManager {
2
+ tickRate;
3
+ tickDuration;
4
+ maxFrameTime;
5
+ tickCallbacks = [];
6
+ frameCallbacks = [];
7
+ pauseHandlers = [];
8
+ resumeHandlers = [];
9
+ currentTick = 0;
10
+ accumulator = 0;
11
+ lastTime = 0;
12
+ isRunning = false;
13
+ _isPaused = false;
14
+ rafId = null;
15
+ constructor(config) {
16
+ this.tickRate = config?.tickRate ?? 60;
17
+ this.tickDuration = 1.0 / this.tickRate;
18
+ this.maxFrameTime = config?.maxFrameTime ?? 0.25;
19
+ }
20
+ onTick(callback) {
21
+ this.tickCallbacks.push(callback);
22
+ return () => {
23
+ const index = this.tickCallbacks.indexOf(callback);
24
+ if (index !== -1) {
25
+ this.tickCallbacks.splice(index, 1);
26
+ }
27
+ };
28
+ }
29
+ onFrame(callback) {
30
+ this.frameCallbacks.push(callback);
31
+ return () => {
32
+ const index = this.frameCallbacks.indexOf(callback);
33
+ if (index !== -1) {
34
+ this.frameCallbacks.splice(index, 1);
35
+ }
36
+ };
37
+ }
38
+ start() {
39
+ if (this.isRunning) {
40
+ console.warn('[TickFrameManager] Already running');
41
+ return;
42
+ }
43
+ this.isRunning = true;
44
+ this.lastTime = performance.now() / 1000;
45
+ this.accumulator = 0;
46
+ this.loop();
47
+ }
48
+ stop() {
49
+ this.isRunning = false;
50
+ if (this.rafId !== null) {
51
+ cancelAnimationFrame(this.rafId);
52
+ this.rafId = null;
53
+ }
54
+ }
55
+ loop = () => {
56
+ if (!this.isRunning)
57
+ return;
58
+ const currentTime = performance.now() / 1000;
59
+ let frameTime = currentTime - this.lastTime;
60
+ this.lastTime = currentTime;
61
+ if (frameTime > this.maxFrameTime) {
62
+ frameTime = this.maxFrameTime;
63
+ }
64
+ this.accumulator += frameTime;
65
+ while (this.accumulator >= this.tickDuration) {
66
+ const emptyBatch = {
67
+ tick: this.currentTick,
68
+ commands: {},
69
+ };
70
+ for (const callback of this.tickCallbacks) {
71
+ callback(this.currentTick, emptyBatch);
72
+ }
73
+ this.currentTick++;
74
+ this.accumulator -= this.tickDuration;
75
+ }
76
+ const alpha = this.accumulator / this.tickDuration;
77
+ for (const callback of this.frameCallbacks) {
78
+ callback(alpha, frameTime);
79
+ }
80
+ this.rafId = requestAnimationFrame(this.loop);
81
+ };
82
+ requestPause() {
83
+ if (this._isPaused)
84
+ return;
85
+ this._isPaused = true;
86
+ this.stop();
87
+ for (const handler of this.pauseHandlers)
88
+ handler();
89
+ }
90
+ requestResume() {
91
+ if (!this._isPaused)
92
+ return;
93
+ this._isPaused = false;
94
+ this.start();
95
+ for (const handler of this.resumeHandlers)
96
+ handler();
97
+ }
98
+ onPause(handler) {
99
+ this.pauseHandlers.push(handler);
100
+ return () => {
101
+ const idx = this.pauseHandlers.indexOf(handler);
102
+ if (idx !== -1)
103
+ this.pauseHandlers.splice(idx, 1);
104
+ };
105
+ }
106
+ onResume(handler) {
107
+ this.resumeHandlers.push(handler);
108
+ return () => {
109
+ const idx = this.resumeHandlers.indexOf(handler);
110
+ if (idx !== -1)
111
+ this.resumeHandlers.splice(idx, 1);
112
+ };
113
+ }
114
+ getCurrentTick() {
115
+ return this.currentTick;
116
+ }
117
+ getTickRate() {
118
+ return this.tickRate;
119
+ }
120
+ isActive() {
121
+ return this.isRunning;
122
+ }
123
+ dispose() {
124
+ this.stop();
125
+ this.tickCallbacks = [];
126
+ this.frameCallbacks = [];
127
+ this.pauseHandlers = [];
128
+ this.resumeHandlers = [];
129
+ }
130
+ }
@@ -0,0 +1,26 @@
1
+ import type { EntityManager } from '../EntityManager';
2
+ import type { PoolManager } from '../pool/PoolManager';
3
+ import type { DebugSnapshot, DebugDataProviderConfig } from './types';
4
+ type SnapshotCallback = (snapshot: DebugSnapshot) => void;
5
+ export declare class DebugDataProvider {
6
+ private readonly entityManager;
7
+ private readonly pools;
8
+ private readonly updateInterval;
9
+ private subscribers;
10
+ private intervalId;
11
+ private _paused;
12
+ set paused(value: boolean);
13
+ constructor(entityManager: EntityManager, pools: PoolManager | null, config?: DebugDataProviderConfig);
14
+ subscribe(callback: SnapshotCallback): () => void;
15
+ getSnapshot(): DebugSnapshot;
16
+ start(): void;
17
+ stop(): void;
18
+ dispose(): void;
19
+ private push;
20
+ private collectSnapshot;
21
+ private collectEntities;
22
+ private collectSoAStores;
23
+ private collectPools;
24
+ }
25
+ export {};
26
+ //# sourceMappingURL=DebugDataProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DebugDataProvider.d.ts","sourceRoot":"","sources":["../../src/debug/DebugDataProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,KAAK,EACV,aAAa,EAKb,uBAAuB,EACxB,MAAM,SAAS,CAAC;AAKjB,KAAK,gBAAgB,GAAG,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;AAyB1D,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,OAAO,CAAkB;IAGjC,IAAW,MAAM,CAAC,KAAK,EAAE,OAAO,EAE/B;gBAGC,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,WAAW,GAAG,IAAI,EACzB,MAAM,CAAC,EAAE,uBAAuB;IAa3B,SAAS,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAYjD,WAAW,IAAI,aAAa;IAU5B,KAAK,IAAI,IAAI;IAYb,IAAI,IAAI,IAAI;IAUZ,OAAO,IAAI,IAAI;IAOtB,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,eAAe;IAgCvB,OAAO,CAAC,gBAAgB;IA6BxB,OAAO,CAAC,YAAY;CAYrB"}