@draug/engine 1.0.17 → 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.ts CHANGED
@@ -3,6 +3,20 @@ import { Bitmap } from 'bitmap-index';
3
3
  type ClassType<T> = new (...args: any[]) => T;
4
4
  type ComponentType<T extends object = object> = ClassType<T>;
5
5
 
6
+ declare enum LogLevel {
7
+ Debug = 0,
8
+ Info = 1,
9
+ Warn = 2,
10
+ Error = 3
11
+ }
12
+ type LogMessage = () => string;
13
+ interface Logger {
14
+ debug(message: LogMessage): void;
15
+ info(message: LogMessage): void;
16
+ warn(message: LogMessage): void;
17
+ error(message: LogMessage): void;
18
+ }
19
+
6
20
  interface IStorage<T extends object> {
7
21
  add(id: number, initFn?: (obj: T) => T): T;
8
22
  remove(id: number): void;
@@ -47,9 +61,12 @@ declare function Component(): ClassDecorator;
47
61
 
48
62
  declare class ComponentStorage<T extends object> implements IStorage<T> {
49
63
  private bits_;
50
- private set_;
64
+ private data_;
65
+ private entityIds_;
66
+ private indexMap_;
51
67
  private pool_;
52
68
  private id_;
69
+ private size_;
53
70
  private cls;
54
71
  constructor(cap: number | undefined, factory: () => T, cls: ClassType<T>);
55
72
  bitmap(): Bitmap;
@@ -61,7 +78,6 @@ declare class ComponentStorage<T extends object> implements IStorage<T> {
61
78
  tryGet(id: number): T;
62
79
  writeComponentsToBuf(ids: ReadonlyArray<number>, out: T[]): number;
63
80
  has(id: number): boolean;
64
- entityIds(): number[];
65
81
  size(): number;
66
82
  forEach(cb: (id: number) => void): void;
67
83
  }
@@ -90,7 +106,9 @@ declare class EntityMaskNotFoundError extends Error {
90
106
  constructor(id: EntityID);
91
107
  }
92
108
  declare class EntitiesManager {
109
+ private readonly logger;
93
110
  private id_;
111
+ constructor(logger: Logger);
94
112
  private nextId;
95
113
  create(): EntityID;
96
114
  }
@@ -141,7 +159,9 @@ declare class EventBus {
141
159
  }
142
160
 
143
161
  declare class ResourcesManager {
162
+ private readonly logger;
144
163
  private readonly items_;
164
+ constructor(logger: Logger);
145
165
  insert<T extends object>(type: ClassType<T>, value: T): T;
146
166
  get<T extends object>(type: ClassType<T>): T;
147
167
  getOrInsert<T extends object>(type: ClassType<T>, factory: () => T): T;
@@ -154,8 +174,9 @@ type CreateEntityComponentEntry<T extends ComponentType = ComponentType> = T ext
154
174
  declare function entry<T extends ComponentType>(component: T, init?: ComponentInitFn<T>): CreateEntityComponentEntry;
155
175
  declare class Commands {
156
176
  private readonly world;
177
+ private readonly logger;
157
178
  private readonly commandsQueue_;
158
- constructor(world: World);
179
+ constructor(world: World, logger: Logger);
159
180
  add(cmd: WorldCommand): void;
160
181
  flush(world: World): void;
161
182
  createEntity(...entries: CreateEntityComponentEntry[]): number;
@@ -223,28 +244,57 @@ declare class ErrPluginNotInit extends PluginError {
223
244
  constructor(pluginId: string);
224
245
  }
225
246
  declare class PluginsManager {
247
+ private readonly logger;
226
248
  private plugins_;
227
249
  private isInitiated_;
250
+ constructor(logger: Logger);
228
251
  install<T extends ClassType<PluginBase>>(plugin: T, ...constructorProps: ConstructorParameters<T>): void;
229
252
  build(): void;
253
+ /**
254
+ * @internal
255
+ */
230
256
  __internal__onAfterWorldInit(world: World): void;
231
257
  getPluginMetadata(pluginOrId: ClassType<PluginBase> | PluginID): PluginMetadata;
232
258
  getPluginInstance<T extends PluginBase>(pluginOrId: ClassType<T> | PluginID): T;
233
259
  private resolveId;
234
260
  }
235
261
 
236
- declare enum LogLevel {
237
- Debug = 0,
238
- Info = 1,
239
- Warn = 2,
240
- Error = 3
262
+ interface TimeSource {
263
+ now(): number;
241
264
  }
242
- type LogMessage = () => string;
243
- interface Logger {
244
- debug(message: LogMessage): void;
245
- info(message: LogMessage): void;
246
- warn(message: LogMessage): void;
247
- error(message: LogMessage): void;
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;
248
298
  }
249
299
 
250
300
  type WorldConstructor = {
@@ -260,6 +310,7 @@ declare class World {
260
310
  readonly commands: Commands;
261
311
  readonly queries: QueryManager;
262
312
  readonly plugins: PluginsManager;
313
+ private readonly logger;
263
314
  private entityRefs_;
264
315
  constructor(params: WorldConstructor);
265
316
  getEntityRef(id: number): EntityRef;
@@ -268,7 +319,7 @@ declare class World {
268
319
  removeComponent<T extends object>(entity: EntityID, component: ComponentType<T>): void;
269
320
  addComponent<T extends object>(id: EntityID, component: ClassType<T>, initFn?: (obj: T) => void): T;
270
321
  addComponent<T extends object>(id: EntityRef, component: ClassType<T>, initFn?: (obj: T) => void): T;
271
- update(dt: number): void;
322
+ update(clock: Clock): void;
272
323
  build(): void;
273
324
  }
274
325
 
@@ -333,7 +384,7 @@ type SystemComputeContext = {
333
384
  /** ECS world instance. */
334
385
  readonly world: World;
335
386
  /** Delta time (seconds or your engine's convention) since the previous update. */
336
- readonly dt: number;
387
+ readonly time: Time;
337
388
  /** Logger instance for debugging and diagnostics. */
338
389
  readonly logger: Logger;
339
390
  };
@@ -368,42 +419,10 @@ declare class SystemsManager {
368
419
  build(): void;
369
420
  private rebuild;
370
421
  get<T extends SystemBase>(ctor: SystemCtor<T>): T;
371
- update(dt: number): void;
422
+ update(time: Time): void;
372
423
  private buildSystemsArray;
373
424
  }
374
425
 
375
- interface TimeSource {
376
- now(): number;
377
- }
378
- declare class Clock {
379
- private readonly timeSource_;
380
- private lastTimeMs_;
381
- private elapsedTime_;
382
- private dt_;
383
- constructor(timeSource_: TimeSource);
384
- get dt(): number;
385
- get ellapsedTime(): number;
386
- tick(): void;
387
- }
388
-
389
- type StepFunction = (dt: number, world: World) => void;
390
- type PlatformLoop = (callback: () => void) => void;
391
- declare class Loop {
392
- private readonly clock;
393
- private readonly stepFn;
394
- private readonly platformLoop;
395
- private running;
396
- constructor(clock: Clock, stepFn: StepFunction, platformLoop: PlatformLoop);
397
- start(world: World): void;
398
- stop(): void;
399
- }
400
-
401
- declare class Runtime {
402
- private readonly loop;
403
- constructor(loop: Loop);
404
- run(world: World): void;
405
- }
406
-
407
426
  declare enum VisitedState {
408
427
  Unvisited = 0,
409
428
  Visiting = 1,
@@ -482,6 +501,7 @@ declare class AssetsManager {
482
501
  type EngineConstructor = {
483
502
  loop: Loop;
484
503
  logger?: Logger;
504
+ maxEntityCount?: number;
485
505
  };
486
506
  declare class Engine {
487
507
  readonly runtime: Runtime;
package/dist/index.js CHANGED
@@ -121,13 +121,14 @@ var SystemsManager = class {
121
121
  this.requiredComponents_.add(c);
122
122
  for (const c of requiredComponents)
123
123
  this.requiredComponents_.add(c);
124
+ this.logger.debug(() => `[Systems]: "${ctor.name}" was registered`);
124
125
  }
125
126
  build() {
126
- this.logger.info(() => "Building systems...");
127
127
  this.buildSystemsArray();
128
- for (const sys of this.systems_.values())
128
+ for (const sys of this.systems_.values()) {
129
129
  sys.onInit?.({ world: this.world, logger: this.logger });
130
- this.logger.info(() => "Systems sucessfully was built!");
130
+ }
131
+ this.logger.debug(() => `Built ${this.systems_.size} systems`);
131
132
  this.dirty_ = false;
132
133
  }
133
134
  rebuild() {
@@ -139,7 +140,7 @@ var SystemsManager = class {
139
140
  throw new Error("System not registered");
140
141
  return s;
141
142
  }
142
- update(dt) {
143
+ update(time) {
143
144
  if (this.dirty_)
144
145
  this.rebuild();
145
146
  this.world.events.swapAll();
@@ -149,7 +150,7 @@ var SystemsManager = class {
149
150
  s.compute({
150
151
  world: this.world,
151
152
  entities,
152
- dt,
153
+ time,
153
154
  logger: this.logger
154
155
  });
155
156
  }
@@ -205,12 +206,18 @@ var EntityMaskNotFoundError = class extends Error {
205
206
  }
206
207
  };
207
208
  var EntitiesManager = class {
209
+ constructor(logger) {
210
+ this.logger = logger;
211
+ }
212
+ logger;
208
213
  id_ = 0;
209
214
  nextId() {
210
215
  return ++this.id_;
211
216
  }
212
217
  create() {
213
- return this.nextId();
218
+ const id2 = this.nextId();
219
+ this.logger.debug(() => `[Entities]: Created new entity with ID ${id2}`);
220
+ return id2;
214
221
  }
215
222
  };
216
223
  var EntityRef = class {
@@ -230,7 +237,7 @@ var EntityRef = class {
230
237
 
231
238
  // src/ecs/constant.ts
232
239
  var ECS_DEFAULTS = {
233
- MAX_ENTITY_COUNT: Math.pow(2, 16)
240
+ MAX_ENTITY_COUNT: Math.pow(2, 12)
234
241
  };
235
242
 
236
243
  // src/ecs/events-buffer.ts
@@ -295,17 +302,16 @@ var ObjectPool = class {
295
302
  pool_;
296
303
  factory_;
297
304
  cursor_;
298
- constructor(factory, initialSize = 1024) {
305
+ constructor(factory, initialSize = 0) {
299
306
  this.pool_ = new Array(initialSize);
300
- for (let i = 0; i < initialSize; i++) {
301
- this.pool_[i] = factory();
302
- }
303
307
  this.factory_ = factory;
304
308
  this.cursor_ = initialSize - 1;
305
309
  }
306
310
  acquire() {
307
- if (this.cursor_ < 0) this.grow();
308
- return this.pool_[this.cursor_--];
311
+ if (this.cursor_ >= 0) {
312
+ return this.pool_[this.cursor_--];
313
+ }
314
+ return this.factory_();
309
315
  }
310
316
  release(obj) {
311
317
  this.pool_[++this.cursor_] = obj;
@@ -321,17 +327,18 @@ var ObjectPool = class {
321
327
 
322
328
  // src/ecs/components/component-storage.ts
323
329
  import { Bitmap } from "bitmap-index";
324
- import { SparseSet } from "ts-sparse-set";
325
330
  var ComponentStorage = class {
326
331
  bits_;
327
- set_;
332
+ data_ = [];
333
+ entityIds_ = [];
334
+ indexMap_ = /* @__PURE__ */ new Map();
328
335
  pool_;
329
336
  id_ = 0;
337
+ size_ = 0;
330
338
  cls;
331
339
  constructor(cap = ECS_DEFAULTS.MAX_ENTITY_COUNT, factory, cls) {
332
- this.set_ = new SparseSet(cap);
333
340
  this.bits_ = new Bitmap(cap);
334
- this.pool_ = new ObjectPool(factory, cap);
341
+ this.pool_ = new ObjectPool(factory, 0);
335
342
  this.cls = cls;
336
343
  }
337
344
  bitmap() {
@@ -344,47 +351,65 @@ var ComponentStorage = class {
344
351
  return this.id_ = id2;
345
352
  }
346
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
+ }
347
357
  const obj = this.pool_.acquire();
348
358
  initFn?.(obj);
349
- 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);
350
363
  this.bits_.set(id2);
351
- return value;
364
+ this.size_++;
365
+ return obj;
352
366
  }
353
367
  remove(id2) {
354
- const obj = this.set_.get(id2);
355
- if (!obj) return;
368
+ const index = this.indexMap_.get(id2);
369
+ if (index === void 0) return;
356
370
  this.bits_.remove(id2);
357
- this.pool_.release(obj);
358
- 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_--;
359
384
  }
360
385
  get(id2) {
361
- return this.set_.get(id2);
386
+ const index = this.indexMap_.get(id2);
387
+ return index !== void 0 ? this.data_[index] : null;
362
388
  }
363
389
  tryGet(id2) {
364
- const x = this.set_.get(id2);
365
- if (!x)
390
+ const index = this.indexMap_.get(id2);
391
+ if (index === void 0)
366
392
  throw new Error(`[ComponentStorage "${this.cls.name}"]: Requesting non-existing item with ID ${id2}.`);
367
- return x;
393
+ return this.data_[index];
368
394
  }
369
395
  writeComponentsToBuf(ids, out) {
370
396
  let len = 0;
371
397
  for (const id2 of ids) {
372
- const obj = this.set_.get(id2);
373
- if (obj !== null) out[len++] = obj;
398
+ const index = this.indexMap_.get(id2);
399
+ if (index !== void 0) out[len++] = this.data_[index];
374
400
  }
375
401
  return len;
376
402
  }
377
403
  has(id2) {
378
404
  return this.bits_.contains(id2);
379
405
  }
380
- entityIds() {
381
- return Array.from(this.bits_);
382
- }
383
406
  size() {
384
- return this.bits_.count();
407
+ return this.size_;
385
408
  }
386
409
  forEach(cb) {
387
- this.bits_.range((x) => cb(x));
410
+ for (const id2 of this.entityIds_) {
411
+ cb(id2);
412
+ }
388
413
  }
389
414
  };
390
415
 
@@ -514,9 +539,14 @@ function getComponentId(ctor) {
514
539
 
515
540
  // src/ecs/resources/resources.ts
516
541
  var ResourcesManager = class {
542
+ constructor(logger) {
543
+ this.logger = logger;
544
+ }
545
+ logger;
517
546
  items_ = /* @__PURE__ */ new Map();
518
547
  insert(type, value) {
519
548
  this.items_.set(type, value);
549
+ this.logger.debug(() => `[Resources]: Inserted new Resource "${type.name}"`);
520
550
  return value;
521
551
  }
522
552
  get(type) {
@@ -529,11 +559,12 @@ var ResourcesManager = class {
529
559
  let value = this.items_.get(type) ?? null;
530
560
  if (value === null) {
531
561
  value = factory();
532
- this.items_.set(type, value);
562
+ this.insert(type, value);
533
563
  }
534
564
  return value;
535
565
  }
536
566
  remove(type) {
567
+ this.logger.debug(() => `[Resources]: Removed resource "${type.name}"`);
537
568
  this.items_.delete(type);
538
569
  }
539
570
  };
@@ -544,10 +575,12 @@ function entry(component, init = () => {
544
575
  return [component, init];
545
576
  }
546
577
  var Commands = class {
547
- constructor(world) {
578
+ constructor(world, logger) {
548
579
  this.world = world;
580
+ this.logger = logger;
549
581
  }
550
582
  world;
583
+ logger;
551
584
  commandsQueue_ = [];
552
585
  add(cmd) {
553
586
  this.commandsQueue_.push(cmd);
@@ -565,6 +598,7 @@ var Commands = class {
565
598
  }
566
599
  };
567
600
  this.add(cmd);
601
+ this.logger.debug(() => `[Commands.createEntity]: Created new entity with ID ${id2}. Linked components: [${entries.map((x) => x[0].name).join(", ")}]`);
568
602
  return id2;
569
603
  }
570
604
  };
@@ -754,6 +788,10 @@ var ErrDAGCycleDetectedPlugin = class extends Error {
754
788
  }
755
789
  };
756
790
  var PluginsManager = class {
791
+ constructor(logger) {
792
+ this.logger = logger;
793
+ }
794
+ logger;
757
795
  plugins_ = /* @__PURE__ */ new Map();
758
796
  isInitiated_ = false;
759
797
  install(plugin, ...constructorProps) {
@@ -768,8 +806,12 @@ var PluginsManager = class {
768
806
  metadata
769
807
  };
770
808
  this.plugins_.set(metadata.id, entry2);
809
+ this.logger.debug(() => `[Plugins]: Installed plugin ${metadata.name} (${metadata.version})`);
771
810
  }
772
811
  build() {
812
+ if (this.plugins_.size === 0) {
813
+ return;
814
+ }
773
815
  const nodes = /* @__PURE__ */ new Map();
774
816
  for (const id2 of this.plugins_.keys()) {
775
817
  nodes.set(id2, new DAGNode(id2));
@@ -802,7 +844,11 @@ var PluginsManager = class {
802
844
  instance.onPluginLoad?.();
803
845
  }
804
846
  this.isInitiated_ = true;
847
+ this.logger.debug(() => `[Plugins]: Plugins built successfully!`);
805
848
  }
849
+ /**
850
+ * @internal
851
+ */
806
852
  __internal__onAfterWorldInit(world) {
807
853
  for (const p of this.plugins_.values()) {
808
854
  p.instance?.onAfterWorldInit?.(world);
@@ -845,16 +891,18 @@ var World3 = class {
845
891
  commands;
846
892
  queries;
847
893
  plugins;
894
+ logger;
848
895
  entityRefs_ = /* @__PURE__ */ new Map();
849
896
  constructor(params) {
897
+ this.entities = new EntitiesManager(params.logger);
850
898
  this.components = new ComponentsManager(params.maxEntityCount ?? ECS_DEFAULTS.MAX_ENTITY_COUNT);
851
899
  this.systems = new SystemsManager(this, params.logger);
852
- this.entities = new EntitiesManager();
853
900
  this.events = new EventBus();
854
- this.resources = new ResourcesManager();
855
- this.commands = new Commands(this);
901
+ this.resources = new ResourcesManager(params.logger);
902
+ this.commands = new Commands(this, params.logger);
856
903
  this.queries = new QueryManager(this);
857
- this.plugins = new PluginsManager();
904
+ this.plugins = new PluginsManager(params.logger);
905
+ this.logger = params.logger;
858
906
  }
859
907
  getEntityRef(id2) {
860
908
  let ref = this.entityRefs_.get(id2);
@@ -890,12 +938,13 @@ var World3 = class {
890
938
  this.queries.invalidate(component);
891
939
  return c;
892
940
  }
893
- update(dt) {
894
- this.systems.update(dt);
941
+ update(clock) {
942
+ this.systems.update(clock.getTime());
895
943
  this.commands.flush(this);
896
944
  }
897
945
  build() {
898
946
  this.plugins.build();
947
+ this.logger.debug(() => "World was built successfully");
899
948
  }
900
949
  };
901
950
 
@@ -918,21 +967,28 @@ var Clock = class {
918
967
  }
919
968
  timeSource_;
920
969
  lastTimeMs_;
921
- elapsedTime_ = 0;
922
- dt_ = 0;
923
- get dt() {
924
- 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_;
925
978
  }
926
979
  get ellapsedTime() {
927
- return this.elapsedTime_;
980
+ return this.ellapsedTime_;
928
981
  }
929
982
  tick() {
930
983
  const now = this.timeSource_.now();
931
- const dt = now - this.lastTimeMs_;
932
- this.dt_ = dt;
933
- 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;
934
987
  this.lastTimeMs_ = now;
935
988
  }
989
+ getTime() {
990
+ return this.time_;
991
+ }
936
992
  };
937
993
 
938
994
  // src/runtime/loop.ts
@@ -951,7 +1007,7 @@ var Loop = class {
951
1007
  const loop = () => {
952
1008
  if (!this.running) return;
953
1009
  this.clock.tick();
954
- this.stepFn(this.clock.dt, world);
1010
+ this.stepFn(this.clock.deltaMs, world);
955
1011
  this.platformLoop(loop);
956
1012
  };
957
1013
  this.platformLoop(loop);
@@ -1136,7 +1192,8 @@ var Engine = class {
1136
1192
  this.runtime = new Runtime(params.loop);
1137
1193
  this.logger = params.logger ?? new NoopLogger();
1138
1194
  this.world = new World3({
1139
- logger: this.logger
1195
+ logger: this.logger,
1196
+ maxEntityCount: params.maxEntityCount ?? ECS_DEFAULTS.MAX_ENTITY_COUNT
1140
1197
  });
1141
1198
  }
1142
1199
  init() {