@draug/engine 1.0.18 → 1.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -61,9 +61,12 @@ declare function Component(): ClassDecorator;
61
61
 
62
62
  declare class ComponentStorage<T extends object> implements IStorage<T> {
63
63
  private bits_;
64
- private set_;
64
+ private data_;
65
+ private entityIds_;
66
+ private indexMap_;
65
67
  private pool_;
66
68
  private id_;
69
+ private size_;
67
70
  private cls;
68
71
  constructor(cap: number | undefined, factory: () => T, cls: ClassType<T>);
69
72
  bitmap(): Bitmap;
@@ -75,7 +78,6 @@ declare class ComponentStorage<T extends object> implements IStorage<T> {
75
78
  tryGet(id: number): T;
76
79
  writeComponentsToBuf(ids: ReadonlyArray<number>, out: T[]): number;
77
80
  has(id: number): boolean;
78
- entityIds(): number[];
79
81
  size(): number;
80
82
  forEach(cb: (id: number) => void): void;
81
83
  }
@@ -257,6 +259,44 @@ declare class PluginsManager {
257
259
  private resolveId;
258
260
  }
259
261
 
262
+ interface TimeSource {
263
+ now(): number;
264
+ }
265
+ declare class Clock {
266
+ private readonly timeSource_;
267
+ private lastTimeMs_;
268
+ private ellapsedTime_;
269
+ private delta_;
270
+ private readonly time_;
271
+ constructor(timeSource_: TimeSource);
272
+ get deltaMs(): number;
273
+ get ellapsedTime(): number;
274
+ tick(): void;
275
+ getTime(): Readonly<Time>;
276
+ }
277
+ type Time = {
278
+ delta: number;
279
+ elapsed: number;
280
+ };
281
+
282
+ type StepFunction = (dt: number, world: World) => void;
283
+ type PlatformLoop = (callback: () => void) => void;
284
+ declare class Loop {
285
+ private readonly clock;
286
+ private readonly stepFn;
287
+ private readonly platformLoop;
288
+ private running;
289
+ constructor(clock: Clock, stepFn: StepFunction, platformLoop: PlatformLoop);
290
+ start(world: World): void;
291
+ stop(): void;
292
+ }
293
+
294
+ declare class Runtime {
295
+ private readonly loop;
296
+ constructor(loop: Loop);
297
+ run(world: World): void;
298
+ }
299
+
260
300
  type WorldConstructor = {
261
301
  maxEntityCount?: number;
262
302
  logger: Logger;
@@ -279,7 +319,7 @@ declare class World {
279
319
  removeComponent<T extends object>(entity: EntityID, component: ComponentType<T>): void;
280
320
  addComponent<T extends object>(id: EntityID, component: ClassType<T>, initFn?: (obj: T) => void): T;
281
321
  addComponent<T extends object>(id: EntityRef, component: ClassType<T>, initFn?: (obj: T) => void): T;
282
- update(dt: number): void;
322
+ update(clock: Clock): void;
283
323
  build(): void;
284
324
  }
285
325
 
@@ -344,7 +384,7 @@ type SystemComputeContext = {
344
384
  /** ECS world instance. */
345
385
  readonly world: World;
346
386
  /** Delta time (seconds or your engine's convention) since the previous update. */
347
- readonly dt: number;
387
+ readonly time: Time;
348
388
  /** Logger instance for debugging and diagnostics. */
349
389
  readonly logger: Logger;
350
390
  };
@@ -379,42 +419,10 @@ declare class SystemsManager {
379
419
  build(): void;
380
420
  private rebuild;
381
421
  get<T extends SystemBase>(ctor: SystemCtor<T>): T;
382
- update(dt: number): void;
422
+ update(time: Time): void;
383
423
  private buildSystemsArray;
384
424
  }
385
425
 
386
- interface TimeSource {
387
- now(): number;
388
- }
389
- declare class Clock {
390
- private readonly timeSource_;
391
- private lastTimeMs_;
392
- private elapsedTime_;
393
- private dt_;
394
- constructor(timeSource_: TimeSource);
395
- get dt(): number;
396
- get ellapsedTime(): number;
397
- tick(): void;
398
- }
399
-
400
- type StepFunction = (dt: number, world: World) => void;
401
- type PlatformLoop = (callback: () => void) => void;
402
- declare class Loop {
403
- private readonly clock;
404
- private readonly stepFn;
405
- private readonly platformLoop;
406
- private running;
407
- constructor(clock: Clock, stepFn: StepFunction, platformLoop: PlatformLoop);
408
- start(world: World): void;
409
- stop(): void;
410
- }
411
-
412
- declare class Runtime {
413
- private readonly loop;
414
- constructor(loop: Loop);
415
- run(world: World): void;
416
- }
417
-
418
426
  declare enum VisitedState {
419
427
  Unvisited = 0,
420
428
  Visiting = 1,
@@ -493,6 +501,7 @@ declare class AssetsManager {
493
501
  type EngineConstructor = {
494
502
  loop: Loop;
495
503
  logger?: Logger;
504
+ maxEntityCount?: number;
496
505
  };
497
506
  declare class Engine {
498
507
  readonly runtime: Runtime;
package/dist/index.d.ts CHANGED
@@ -61,9 +61,12 @@ declare function Component(): ClassDecorator;
61
61
 
62
62
  declare class ComponentStorage<T extends object> implements IStorage<T> {
63
63
  private bits_;
64
- private set_;
64
+ private data_;
65
+ private entityIds_;
66
+ private indexMap_;
65
67
  private pool_;
66
68
  private id_;
69
+ private size_;
67
70
  private cls;
68
71
  constructor(cap: number | undefined, factory: () => T, cls: ClassType<T>);
69
72
  bitmap(): Bitmap;
@@ -75,7 +78,6 @@ declare class ComponentStorage<T extends object> implements IStorage<T> {
75
78
  tryGet(id: number): T;
76
79
  writeComponentsToBuf(ids: ReadonlyArray<number>, out: T[]): number;
77
80
  has(id: number): boolean;
78
- entityIds(): number[];
79
81
  size(): number;
80
82
  forEach(cb: (id: number) => void): void;
81
83
  }
@@ -257,6 +259,44 @@ declare class PluginsManager {
257
259
  private resolveId;
258
260
  }
259
261
 
262
+ interface TimeSource {
263
+ now(): number;
264
+ }
265
+ declare class Clock {
266
+ private readonly timeSource_;
267
+ private lastTimeMs_;
268
+ private ellapsedTime_;
269
+ private delta_;
270
+ private readonly time_;
271
+ constructor(timeSource_: TimeSource);
272
+ get deltaMs(): number;
273
+ get ellapsedTime(): number;
274
+ tick(): void;
275
+ getTime(): Readonly<Time>;
276
+ }
277
+ type Time = {
278
+ delta: number;
279
+ elapsed: number;
280
+ };
281
+
282
+ type StepFunction = (dt: number, world: World) => void;
283
+ type PlatformLoop = (callback: () => void) => void;
284
+ declare class Loop {
285
+ private readonly clock;
286
+ private readonly stepFn;
287
+ private readonly platformLoop;
288
+ private running;
289
+ constructor(clock: Clock, stepFn: StepFunction, platformLoop: PlatformLoop);
290
+ start(world: World): void;
291
+ stop(): void;
292
+ }
293
+
294
+ declare class Runtime {
295
+ private readonly loop;
296
+ constructor(loop: Loop);
297
+ run(world: World): void;
298
+ }
299
+
260
300
  type WorldConstructor = {
261
301
  maxEntityCount?: number;
262
302
  logger: Logger;
@@ -279,7 +319,7 @@ declare class World {
279
319
  removeComponent<T extends object>(entity: EntityID, component: ComponentType<T>): void;
280
320
  addComponent<T extends object>(id: EntityID, component: ClassType<T>, initFn?: (obj: T) => void): T;
281
321
  addComponent<T extends object>(id: EntityRef, component: ClassType<T>, initFn?: (obj: T) => void): T;
282
- update(dt: number): void;
322
+ update(clock: Clock): void;
283
323
  build(): void;
284
324
  }
285
325
 
@@ -344,7 +384,7 @@ type SystemComputeContext = {
344
384
  /** ECS world instance. */
345
385
  readonly world: World;
346
386
  /** Delta time (seconds or your engine's convention) since the previous update. */
347
- readonly dt: number;
387
+ readonly time: Time;
348
388
  /** Logger instance for debugging and diagnostics. */
349
389
  readonly logger: Logger;
350
390
  };
@@ -379,42 +419,10 @@ declare class SystemsManager {
379
419
  build(): void;
380
420
  private rebuild;
381
421
  get<T extends SystemBase>(ctor: SystemCtor<T>): T;
382
- update(dt: number): void;
422
+ update(time: Time): void;
383
423
  private buildSystemsArray;
384
424
  }
385
425
 
386
- interface TimeSource {
387
- now(): number;
388
- }
389
- declare class Clock {
390
- private readonly timeSource_;
391
- private lastTimeMs_;
392
- private elapsedTime_;
393
- private dt_;
394
- constructor(timeSource_: TimeSource);
395
- get dt(): number;
396
- get ellapsedTime(): number;
397
- tick(): void;
398
- }
399
-
400
- type StepFunction = (dt: number, world: World) => void;
401
- type PlatformLoop = (callback: () => void) => void;
402
- declare class Loop {
403
- private readonly clock;
404
- private readonly stepFn;
405
- private readonly platformLoop;
406
- private running;
407
- constructor(clock: Clock, stepFn: StepFunction, platformLoop: PlatformLoop);
408
- start(world: World): void;
409
- stop(): void;
410
- }
411
-
412
- declare class Runtime {
413
- private readonly loop;
414
- constructor(loop: Loop);
415
- run(world: World): void;
416
- }
417
-
418
426
  declare enum VisitedState {
419
427
  Unvisited = 0,
420
428
  Visiting = 1,
@@ -493,6 +501,7 @@ declare class AssetsManager {
493
501
  type EngineConstructor = {
494
502
  loop: Loop;
495
503
  logger?: Logger;
504
+ maxEntityCount?: number;
496
505
  };
497
506
  declare class Engine {
498
507
  readonly runtime: Runtime;
package/dist/index.js CHANGED
@@ -140,7 +140,7 @@ var SystemsManager = class {
140
140
  throw new Error("System not registered");
141
141
  return s;
142
142
  }
143
- update(dt) {
143
+ update(time) {
144
144
  if (this.dirty_)
145
145
  this.rebuild();
146
146
  this.world.events.swapAll();
@@ -150,7 +150,7 @@ var SystemsManager = class {
150
150
  s.compute({
151
151
  world: this.world,
152
152
  entities,
153
- dt,
153
+ time,
154
154
  logger: this.logger
155
155
  });
156
156
  }
@@ -237,7 +237,7 @@ var EntityRef = class {
237
237
 
238
238
  // src/ecs/constant.ts
239
239
  var ECS_DEFAULTS = {
240
- MAX_ENTITY_COUNT: Math.pow(2, 16)
240
+ MAX_ENTITY_COUNT: Math.pow(2, 12)
241
241
  };
242
242
 
243
243
  // src/ecs/events-buffer.ts
@@ -302,17 +302,16 @@ var ObjectPool = class {
302
302
  pool_;
303
303
  factory_;
304
304
  cursor_;
305
- constructor(factory, initialSize = 1024) {
305
+ constructor(factory, initialSize = 0) {
306
306
  this.pool_ = new Array(initialSize);
307
- for (let i = 0; i < initialSize; i++) {
308
- this.pool_[i] = factory();
309
- }
310
307
  this.factory_ = factory;
311
308
  this.cursor_ = initialSize - 1;
312
309
  }
313
310
  acquire() {
314
- if (this.cursor_ < 0) this.grow();
315
- return this.pool_[this.cursor_--];
311
+ if (this.cursor_ >= 0) {
312
+ return this.pool_[this.cursor_--];
313
+ }
314
+ return this.factory_();
316
315
  }
317
316
  release(obj) {
318
317
  this.pool_[++this.cursor_] = obj;
@@ -328,17 +327,18 @@ var ObjectPool = class {
328
327
 
329
328
  // src/ecs/components/component-storage.ts
330
329
  import { Bitmap } from "bitmap-index";
331
- import { SparseSet } from "ts-sparse-set";
332
330
  var ComponentStorage = class {
333
331
  bits_;
334
- set_;
332
+ data_ = [];
333
+ entityIds_ = [];
334
+ indexMap_ = /* @__PURE__ */ new Map();
335
335
  pool_;
336
336
  id_ = 0;
337
+ size_ = 0;
337
338
  cls;
338
339
  constructor(cap = ECS_DEFAULTS.MAX_ENTITY_COUNT, factory, cls) {
339
- this.set_ = new SparseSet(cap);
340
340
  this.bits_ = new Bitmap(cap);
341
- this.pool_ = new ObjectPool(factory, cap);
341
+ this.pool_ = new ObjectPool(factory, 0);
342
342
  this.cls = cls;
343
343
  }
344
344
  bitmap() {
@@ -351,47 +351,65 @@ var ComponentStorage = class {
351
351
  return this.id_ = id2;
352
352
  }
353
353
  add(id2, initFn) {
354
+ if (this.indexMap_.has(id2)) {
355
+ throw new Error(`[ComponentStorage "${this.cls.name}"]: Entity ${id2} already has this component`);
356
+ }
354
357
  const obj = this.pool_.acquire();
355
358
  initFn?.(obj);
356
- const value = this.set_.add(id2, obj);
359
+ const index = this.data_.length;
360
+ this.data_.push(obj);
361
+ this.entityIds_.push(id2);
362
+ this.indexMap_.set(id2, index);
357
363
  this.bits_.set(id2);
358
- return value;
364
+ this.size_++;
365
+ return obj;
359
366
  }
360
367
  remove(id2) {
361
- const obj = this.set_.get(id2);
362
- if (!obj) return;
368
+ const index = this.indexMap_.get(id2);
369
+ if (index === void 0) return;
363
370
  this.bits_.remove(id2);
364
- this.pool_.release(obj);
365
- this.set_.remove(id2);
371
+ const lastIndex = this.data_.length - 1;
372
+ const lastEntityId = this.entityIds_[lastIndex];
373
+ const removedObj = this.data_[index];
374
+ if (index !== lastIndex) {
375
+ this.data_[index] = this.data_[lastIndex];
376
+ this.entityIds_[index] = lastEntityId;
377
+ this.indexMap_.set(lastEntityId, index);
378
+ }
379
+ this.data_.pop();
380
+ this.entityIds_.pop();
381
+ this.indexMap_.delete(id2);
382
+ this.pool_.release(removedObj);
383
+ this.size_--;
366
384
  }
367
385
  get(id2) {
368
- return this.set_.get(id2);
386
+ const index = this.indexMap_.get(id2);
387
+ return index !== void 0 ? this.data_[index] : null;
369
388
  }
370
389
  tryGet(id2) {
371
- const x = this.set_.get(id2);
372
- if (!x)
390
+ const index = this.indexMap_.get(id2);
391
+ if (index === void 0)
373
392
  throw new Error(`[ComponentStorage "${this.cls.name}"]: Requesting non-existing item with ID ${id2}.`);
374
- return x;
393
+ return this.data_[index];
375
394
  }
376
395
  writeComponentsToBuf(ids, out) {
377
396
  let len = 0;
378
397
  for (const id2 of ids) {
379
- const obj = this.set_.get(id2);
380
- if (obj !== null) out[len++] = obj;
398
+ const index = this.indexMap_.get(id2);
399
+ if (index !== void 0) out[len++] = this.data_[index];
381
400
  }
382
401
  return len;
383
402
  }
384
403
  has(id2) {
385
404
  return this.bits_.contains(id2);
386
405
  }
387
- entityIds() {
388
- return Array.from(this.bits_);
389
- }
390
406
  size() {
391
- return this.bits_.count();
407
+ return this.size_;
392
408
  }
393
409
  forEach(cb) {
394
- this.bits_.range((x) => cb(x));
410
+ for (const id2 of this.entityIds_) {
411
+ cb(id2);
412
+ }
395
413
  }
396
414
  };
397
415
 
@@ -791,6 +809,9 @@ var PluginsManager = class {
791
809
  this.logger.debug(() => `[Plugins]: Installed plugin ${metadata.name} (${metadata.version})`);
792
810
  }
793
811
  build() {
812
+ if (this.plugins_.size === 0) {
813
+ return;
814
+ }
794
815
  const nodes = /* @__PURE__ */ new Map();
795
816
  for (const id2 of this.plugins_.keys()) {
796
817
  nodes.set(id2, new DAGNode(id2));
@@ -917,8 +938,8 @@ var World3 = class {
917
938
  this.queries.invalidate(component);
918
939
  return c;
919
940
  }
920
- update(dt) {
921
- this.systems.update(dt);
941
+ update(clock) {
942
+ this.systems.update(clock.getTime());
922
943
  this.commands.flush(this);
923
944
  }
924
945
  build() {
@@ -946,21 +967,28 @@ var Clock = class {
946
967
  }
947
968
  timeSource_;
948
969
  lastTimeMs_;
949
- elapsedTime_ = 0;
950
- dt_ = 0;
951
- get dt() {
952
- return this.dt_;
970
+ ellapsedTime_ = 0;
971
+ delta_ = 0;
972
+ time_ = {
973
+ delta: 0,
974
+ elapsed: 0
975
+ };
976
+ get deltaMs() {
977
+ return this.delta_;
953
978
  }
954
979
  get ellapsedTime() {
955
- return this.elapsedTime_;
980
+ return this.ellapsedTime_;
956
981
  }
957
982
  tick() {
958
983
  const now = this.timeSource_.now();
959
- const dt = now - this.lastTimeMs_;
960
- this.dt_ = dt;
961
- this.elapsedTime_ += dt;
984
+ const dt = Math.min(now - this.lastTimeMs_, 100);
985
+ this.delta_ = this.time_.delta = dt / 1e3;
986
+ this.ellapsedTime_ = this.time_.elapsed += dt / 1e3;
962
987
  this.lastTimeMs_ = now;
963
988
  }
989
+ getTime() {
990
+ return this.time_;
991
+ }
964
992
  };
965
993
 
966
994
  // src/runtime/loop.ts
@@ -979,7 +1007,7 @@ var Loop = class {
979
1007
  const loop = () => {
980
1008
  if (!this.running) return;
981
1009
  this.clock.tick();
982
- this.stepFn(this.clock.dt, world);
1010
+ this.stepFn(this.clock.deltaMs, world);
983
1011
  this.platformLoop(loop);
984
1012
  };
985
1013
  this.platformLoop(loop);
@@ -1164,7 +1192,8 @@ var Engine = class {
1164
1192
  this.runtime = new Runtime(params.loop);
1165
1193
  this.logger = params.logger ?? new NoopLogger();
1166
1194
  this.world = new World3({
1167
- logger: this.logger
1195
+ logger: this.logger,
1196
+ maxEntityCount: params.maxEntityCount ?? ECS_DEFAULTS.MAX_ENTITY_COUNT
1168
1197
  });
1169
1198
  }
1170
1199
  init() {