@draug/engine 1.0.18 → 1.0.20

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.js CHANGED
@@ -75,7 +75,8 @@ function System(props) {
75
75
  const requiredComponents = new Set(props.requiredComponents);
76
76
  const computeAfter = new Set(props.computeAfter);
77
77
  const phase = props.phase ?? 1 /* MAIN */;
78
- const metadata = { query, requiredComponents, computeAfter, phase };
78
+ const name = props.name ?? target.name;
79
+ const metadata = { query, requiredComponents, computeAfter, phase, name };
79
80
  systemTarget[SystemMetadataSymbol] = metadata;
80
81
  };
81
82
  }
@@ -121,7 +122,8 @@ var SystemsManager = class {
121
122
  this.requiredComponents_.add(c);
122
123
  for (const c of requiredComponents)
123
124
  this.requiredComponents_.add(c);
124
- this.logger.debug(() => `[Systems]: "${ctor.name}" was registered`);
125
+ const meta = getSystemMetadata(ctor);
126
+ this.logger.debug(() => `[Systems]: system "${meta.name}" was registered`);
125
127
  }
126
128
  build() {
127
129
  this.buildSystemsArray();
@@ -140,7 +142,7 @@ var SystemsManager = class {
140
142
  throw new Error("System not registered");
141
143
  return s;
142
144
  }
143
- update(dt) {
145
+ update(time) {
144
146
  if (this.dirty_)
145
147
  this.rebuild();
146
148
  this.world.events.swapAll();
@@ -150,7 +152,7 @@ var SystemsManager = class {
150
152
  s.compute({
151
153
  world: this.world,
152
154
  entities,
153
- dt,
155
+ time,
154
156
  logger: this.logger
155
157
  });
156
158
  }
@@ -237,7 +239,7 @@ var EntityRef = class {
237
239
 
238
240
  // src/ecs/constant.ts
239
241
  var ECS_DEFAULTS = {
240
- MAX_ENTITY_COUNT: Math.pow(2, 16)
242
+ MAX_ENTITY_COUNT: Math.pow(2, 12)
241
243
  };
242
244
 
243
245
  // src/ecs/events-buffer.ts
@@ -302,17 +304,16 @@ var ObjectPool = class {
302
304
  pool_;
303
305
  factory_;
304
306
  cursor_;
305
- constructor(factory, initialSize = 1024) {
307
+ constructor(factory, initialSize = 0) {
306
308
  this.pool_ = new Array(initialSize);
307
- for (let i = 0; i < initialSize; i++) {
308
- this.pool_[i] = factory();
309
- }
310
309
  this.factory_ = factory;
311
310
  this.cursor_ = initialSize - 1;
312
311
  }
313
312
  acquire() {
314
- if (this.cursor_ < 0) this.grow();
315
- return this.pool_[this.cursor_--];
313
+ if (this.cursor_ >= 0) {
314
+ return this.pool_[this.cursor_--];
315
+ }
316
+ return this.factory_();
316
317
  }
317
318
  release(obj) {
318
319
  this.pool_[++this.cursor_] = obj;
@@ -328,17 +329,18 @@ var ObjectPool = class {
328
329
 
329
330
  // src/ecs/components/component-storage.ts
330
331
  import { Bitmap } from "bitmap-index";
331
- import { SparseSet } from "ts-sparse-set";
332
332
  var ComponentStorage = class {
333
333
  bits_;
334
- set_;
334
+ data_ = [];
335
+ entityIds_ = [];
336
+ indexMap_ = /* @__PURE__ */ new Map();
335
337
  pool_;
336
338
  id_ = 0;
339
+ size_ = 0;
337
340
  cls;
338
341
  constructor(cap = ECS_DEFAULTS.MAX_ENTITY_COUNT, factory, cls) {
339
- this.set_ = new SparseSet(cap);
340
342
  this.bits_ = new Bitmap(cap);
341
- this.pool_ = new ObjectPool(factory, cap);
343
+ this.pool_ = new ObjectPool(factory, 0);
342
344
  this.cls = cls;
343
345
  }
344
346
  bitmap() {
@@ -351,99 +353,103 @@ var ComponentStorage = class {
351
353
  return this.id_ = id2;
352
354
  }
353
355
  add(id2, initFn) {
356
+ if (this.indexMap_.has(id2)) {
357
+ throw new Error(`[ComponentStorage "${this.cls.name}"]: Entity ${id2} already has this component`);
358
+ }
354
359
  const obj = this.pool_.acquire();
355
360
  initFn?.(obj);
356
- const value = this.set_.add(id2, obj);
361
+ const index = this.data_.length;
362
+ this.data_.push(obj);
363
+ this.entityIds_.push(id2);
364
+ this.indexMap_.set(id2, index);
357
365
  this.bits_.set(id2);
358
- return value;
366
+ this.size_++;
367
+ return obj;
359
368
  }
360
369
  remove(id2) {
361
- const obj = this.set_.get(id2);
362
- if (!obj) return;
370
+ const index = this.indexMap_.get(id2);
371
+ if (index === void 0) return;
363
372
  this.bits_.remove(id2);
364
- this.pool_.release(obj);
365
- this.set_.remove(id2);
373
+ const lastIndex = this.data_.length - 1;
374
+ const lastEntityId = this.entityIds_[lastIndex];
375
+ const removedObj = this.data_[index];
376
+ if (index !== lastIndex) {
377
+ this.data_[index] = this.data_[lastIndex];
378
+ this.entityIds_[index] = lastEntityId;
379
+ this.indexMap_.set(lastEntityId, index);
380
+ }
381
+ this.data_.pop();
382
+ this.entityIds_.pop();
383
+ this.indexMap_.delete(id2);
384
+ this.pool_.release(removedObj);
385
+ this.size_--;
366
386
  }
367
387
  get(id2) {
368
- return this.set_.get(id2);
388
+ const index = this.indexMap_.get(id2);
389
+ return index !== void 0 ? this.data_[index] : null;
369
390
  }
370
391
  tryGet(id2) {
371
- const x = this.set_.get(id2);
372
- if (!x)
392
+ const index = this.indexMap_.get(id2);
393
+ if (index === void 0)
373
394
  throw new Error(`[ComponentStorage "${this.cls.name}"]: Requesting non-existing item with ID ${id2}.`);
374
- return x;
395
+ return this.data_[index];
375
396
  }
376
397
  writeComponentsToBuf(ids, out) {
377
398
  let len = 0;
378
399
  for (const id2 of ids) {
379
- const obj = this.set_.get(id2);
380
- if (obj !== null) out[len++] = obj;
400
+ const index = this.indexMap_.get(id2);
401
+ if (index !== void 0) out[len++] = this.data_[index];
381
402
  }
382
403
  return len;
383
404
  }
384
405
  has(id2) {
385
406
  return this.bits_.contains(id2);
386
407
  }
387
- entityIds() {
388
- return Array.from(this.bits_);
389
- }
390
408
  size() {
391
- return this.bits_.count();
409
+ return this.size_;
392
410
  }
393
411
  forEach(cb) {
394
- this.bits_.range((x) => cb(x));
412
+ for (const id2 of this.entityIds_) {
413
+ cb(id2);
414
+ }
395
415
  }
396
416
  };
397
417
 
398
- // src/ecs/components/singleton-storage.ts
399
- var SingletonStorage = class {
400
- constructor(factory) {
401
- this.factory = factory;
402
- }
403
- factory;
404
- value = null;
405
- entityId = null;
406
- bitmap() {
407
- throw new Error("Singletone component cannot has a bitmap!");
408
- }
409
- add(id2, initFn) {
410
- if (this.value !== null)
411
- throw new Error("Singleton already initiated");
412
- this.entityId = id2;
413
- this.value = this.factory();
414
- initFn?.(this.value);
415
- return this.value;
416
- }
417
- remove(id2) {
418
- if (!this.validateId(id2))
419
- return;
420
- this.value = null;
421
- this.entityId = null;
422
- }
423
- get(id2) {
424
- if (!this.validateId(id2))
425
- return null;
426
- return this.value;
427
- }
428
- tryGet(id2) {
429
- if (!this.validateId(id2))
430
- throw new Error("[SingletoneStorage]: ID missmatch.");
431
- return this.value;
432
- }
433
- has(id2) {
434
- return this.validateId(id2);
435
- }
436
- size() {
437
- return this.value !== null ? 1 : 0;
438
- }
439
- forEach(cb) {
440
- if (this.entityId !== null)
441
- cb(this.entityId);
418
+ // src/ecs/components/utils.ts
419
+ var registry = /* @__PURE__ */ new Map();
420
+ var id = 0;
421
+ var ComponentMetadataSymbol = /* @__PURE__ */ Symbol("component");
422
+ function Component(options) {
423
+ return (target) => {
424
+ const metadata = {
425
+ name: options.name,
426
+ id: ++id
427
+ };
428
+ registry.set(target, metadata.id);
429
+ target[ComponentMetadataSymbol] = metadata;
430
+ };
431
+ }
432
+ function getComponentId(ctor) {
433
+ const id2 = registry.get(ctor);
434
+ if (id2 === void 0) {
435
+ throw new Error(`Component not registered: ${ctor.name}`);
442
436
  }
443
- validateId(id2) {
444
- return this.entityId !== null && id2 === this.entityId;
437
+ return id2;
438
+ }
439
+ var ErrNotComponent = class extends Error {
440
+ constructor(ctor) {
441
+ super(`Class ${ctor.name} is not a Component. Use @Component decorator to define components.`);
445
442
  }
446
443
  };
444
+ function getComponentMetadata(component) {
445
+ if (isComponent(component)) {
446
+ return component[ComponentMetadataSymbol];
447
+ }
448
+ throw new ErrNotComponent(component);
449
+ }
450
+ function isComponent(ctor) {
451
+ return ComponentMetadataSymbol in ctor;
452
+ }
447
453
 
448
454
  // src/ecs/components/manager.ts
449
455
  var ComponentAlreadyRegisteredError = class extends Error {
@@ -452,9 +458,11 @@ var ComponentAlreadyRegisteredError = class extends Error {
452
458
  }
453
459
  };
454
460
  var ComponentsManager = class {
455
- constructor(maxEntityCount = ECS_DEFAULTS.MAX_ENTITY_COUNT) {
461
+ constructor(logger, maxEntityCount = ECS_DEFAULTS.MAX_ENTITY_COUNT) {
462
+ this.logger = logger;
456
463
  this.maxEntityCount = maxEntityCount;
457
464
  }
465
+ logger;
458
466
  maxEntityCount;
459
467
  storages_ = /* @__PURE__ */ new Map();
460
468
  currId_ = 0;
@@ -464,19 +472,10 @@ var ComponentsManager = class {
464
472
  register(component, opts) {
465
473
  if (this.storages_.has(component))
466
474
  return this.storages_.get(component);
467
- let store;
468
- switch (opts?.storageType) {
469
- case 2 /* SINGLETON_STORAGE */:
470
- store = this.createSingletonStore(opts);
471
- break;
472
- case 1 /* COMPONENT_STORAGE */:
473
- store = this.createComponentStore(component, opts);
474
- break;
475
- default:
476
- store = this.createComponentStore(component, opts);
477
- break;
478
- }
475
+ const store = this.createComponentStore(component, opts);
479
476
  this.storages_.set(component, store);
477
+ const meta = getComponentMetadata(component);
478
+ this.logger.debug(() => `[Components]: Registered component "${meta.name}"`);
480
479
  return store;
481
480
  }
482
481
  createComponentStore(component, opts) {
@@ -485,12 +484,6 @@ var ComponentsManager = class {
485
484
  store._internalSetId(this.nextId());
486
485
  return store;
487
486
  }
488
- createSingletonStore(opts) {
489
- if (!opts?.factory) {
490
- throw new Error("For singletone storage provide factory is required!");
491
- }
492
- return new SingletonStorage(opts.factory);
493
- }
494
487
  getStorage(component) {
495
488
  const store = this.storages_.get(component);
496
489
  if (store === void 0)
@@ -502,24 +495,30 @@ var ComponentsManager = class {
502
495
  }
503
496
  };
504
497
 
505
- // src/ecs/components/utils.ts
506
- var registry = /* @__PURE__ */ new Map();
507
- var id = 0;
508
- function Component() {
498
+ // src/ecs/resources/resources.ts
499
+ var ResourceMetadataSymbol = /* @__PURE__ */ Symbol("resource");
500
+ function Resource(params) {
509
501
  return (target) => {
510
- if (registry.has(target)) return;
511
- registry.set(target, ++id);
502
+ const metadata = {
503
+ name: params.name
504
+ };
505
+ target[ResourceMetadataSymbol] = metadata;
512
506
  };
513
507
  }
514
- function getComponentId(ctor) {
515
- const id2 = registry.get(ctor);
516
- if (id2 === void 0) {
517
- throw new Error(`Component not registered: ${ctor.name}`);
508
+ var ErrNotResource = class extends Error {
509
+ constructor(ctor) {
510
+ super(`Class ${ctor.name} is not a Resource. Use @Resource decorator to define resources.`);
518
511
  }
519
- return id2;
512
+ };
513
+ function getResourceMetadata(resource) {
514
+ if (isResource(resource)) {
515
+ return resource[ResourceMetadataSymbol];
516
+ }
517
+ throw new ErrNotResource(resource);
518
+ }
519
+ function isResource(ctor) {
520
+ return ResourceMetadataSymbol in ctor;
520
521
  }
521
-
522
- // src/ecs/resources/resources.ts
523
522
  var ResourcesManager = class {
524
523
  constructor(logger) {
525
524
  this.logger = logger;
@@ -528,13 +527,15 @@ var ResourcesManager = class {
528
527
  items_ = /* @__PURE__ */ new Map();
529
528
  insert(type, value) {
530
529
  this.items_.set(type, value);
531
- this.logger.debug(() => `[Resources]: Inserted new Resource "${type.name}"`);
530
+ const metadata = getResourceMetadata(type);
531
+ this.logger.debug(() => `[Resources]: Inserted new Resource "${metadata.name}"`);
532
532
  return value;
533
533
  }
534
534
  get(type) {
535
535
  const value = this.items_.get(type);
536
+ const meta = getResourceMetadata(type);
536
537
  if (!value)
537
- throw new Error(`Resource of class ${type.name} does not exist!`);
538
+ throw new Error(`Resource of class ${meta.name} does not exist!`);
538
539
  return value;
539
540
  }
540
541
  getOrInsert(type, factory) {
@@ -546,7 +547,8 @@ var ResourcesManager = class {
546
547
  return value;
547
548
  }
548
549
  remove(type) {
549
- this.logger.debug(() => `[Resources]: Removed resource "${type.name}"`);
550
+ const meta = getResourceMetadata(type);
551
+ this.logger.debug(() => `[Resources]: Removed resource "${meta.name}"`);
550
552
  this.items_.delete(type);
551
553
  }
552
554
  };
@@ -580,7 +582,10 @@ var Commands = class {
580
582
  }
581
583
  };
582
584
  this.add(cmd);
583
- this.logger.debug(() => `[Commands.createEntity]: Created new entity with ID ${id2}. Linked components: [${entries.map((x) => x[0].name).join(", ")}]`);
585
+ this.logger.debug(() => {
586
+ const components = entries.map((x) => getComponentMetadata(x[0]).name).join(", ");
587
+ return `[Commands.createEntity]: Created new entity with ID ${id2}. Linked components: [${components}]`;
588
+ });
584
589
  return id2;
585
590
  }
586
591
  };
@@ -780,30 +785,33 @@ var PluginsManager = class {
780
785
  if (!isPlugin(plugin))
781
786
  throw new ErrMissingPluginMetadata(plugin);
782
787
  const metadata = getPluginMetadata(plugin);
783
- if (this.plugins_.has(metadata.id))
788
+ if (this.plugins_.has(plugin))
784
789
  return;
785
790
  const entry2 = {
786
791
  ctor: plugin,
787
792
  ctorParams: constructorProps,
788
793
  metadata
789
794
  };
790
- this.plugins_.set(metadata.id, entry2);
795
+ this.plugins_.set(plugin, entry2);
791
796
  this.logger.debug(() => `[Plugins]: Installed plugin ${metadata.name} (${metadata.version})`);
792
797
  }
793
798
  build() {
799
+ if (this.plugins_.size === 0) {
800
+ return;
801
+ }
794
802
  const nodes = /* @__PURE__ */ new Map();
795
803
  for (const id2 of this.plugins_.keys()) {
796
804
  nodes.set(id2, new DAGNode(id2));
797
805
  }
798
- for (const [id2, entry2] of this.plugins_) {
799
- const node = nodes.get(id2);
806
+ for (const [plugin, entry2] of this.plugins_) {
807
+ const node = nodes.get(plugin);
800
808
  const depPlugins = entry2.metadata.dependencies?.plugins ?? [];
801
809
  for (const dep of depPlugins) {
802
- const depNode = nodes.get(dep.id);
810
+ const depNode = nodes.get(dep.plugin);
803
811
  if (!depNode) {
804
- throw new ErrMissingPluginDependency(id2, dep.id);
812
+ throw new ErrMissingPluginDependency(plugin, dep.plugin);
805
813
  }
806
- node.vertices.push(depNode);
814
+ depNode.vertices.push(node);
807
815
  }
808
816
  }
809
817
  let sortedNodes;
@@ -833,20 +841,18 @@ var PluginsManager = class {
833
841
  p.instance?.onAfterWorldInit?.(world);
834
842
  }
835
843
  }
836
- getPluginMetadata(pluginOrId) {
837
- const id2 = this.resolveId(pluginOrId);
838
- const entry2 = this.plugins_.get(id2);
839
- if (!entry2) throw new ErrUnknownPlugin(id2);
844
+ getPluginMetadata(plugin) {
845
+ const entry2 = this.plugins_.get(plugin);
846
+ if (!entry2) throw new ErrUnknownPlugin(plugin);
840
847
  return entry2.metadata;
841
848
  }
842
- getPluginInstance(pluginOrId) {
849
+ getPluginInstance(plugin) {
850
+ const entry2 = this.plugins_.get(plugin);
851
+ if (!entry2) throw new ErrUnknownPlugin(plugin);
852
+ if (!entry2.instance) throw new ErrPluginNotInit(plugin);
843
853
  if (!this.isInitiated_) {
844
- throw new Error("Plugin instance is not initiated yet. Use PluginManager.build() before use plugins.");
854
+ throw new ErrPluginNotInit(plugin);
845
855
  }
846
- const id2 = this.resolveId(pluginOrId);
847
- const entry2 = this.plugins_.get(id2);
848
- if (!entry2) throw new ErrUnknownPlugin(id2);
849
- if (!entry2.instance) throw new ErrPluginNotInit(id2);
850
856
  return entry2.instance;
851
857
  }
852
858
  resolveId(pluginOrId) {
@@ -872,9 +878,13 @@ var World3 = class {
872
878
  plugins;
873
879
  logger;
874
880
  entityRefs_ = /* @__PURE__ */ new Map();
881
+ updatesCount_ = 0;
882
+ get updatesCount() {
883
+ return this.updatesCount_;
884
+ }
875
885
  constructor(params) {
876
886
  this.entities = new EntitiesManager(params.logger);
877
- this.components = new ComponentsManager(params.maxEntityCount ?? ECS_DEFAULTS.MAX_ENTITY_COUNT);
887
+ this.components = new ComponentsManager(params.logger, params.maxEntityCount ?? ECS_DEFAULTS.MAX_ENTITY_COUNT);
878
888
  this.systems = new SystemsManager(this, params.logger);
879
889
  this.events = new EventBus();
880
890
  this.resources = new ResourcesManager(params.logger);
@@ -917,9 +927,10 @@ var World3 = class {
917
927
  this.queries.invalidate(component);
918
928
  return c;
919
929
  }
920
- update(dt) {
921
- this.systems.update(dt);
930
+ update(clock) {
931
+ this.systems.update(clock.getTime());
922
932
  this.commands.flush(this);
933
+ this.updatesCount_++;
923
934
  }
924
935
  build() {
925
936
  this.plugins.build();
@@ -946,21 +957,28 @@ var Clock = class {
946
957
  }
947
958
  timeSource_;
948
959
  lastTimeMs_;
949
- elapsedTime_ = 0;
950
- dt_ = 0;
951
- get dt() {
952
- return this.dt_;
960
+ ellapsedTime_ = 0;
961
+ delta_ = 0;
962
+ time_ = {
963
+ delta: 0,
964
+ elapsed: 0
965
+ };
966
+ get deltaMs() {
967
+ return this.delta_;
953
968
  }
954
969
  get ellapsedTime() {
955
- return this.elapsedTime_;
970
+ return this.ellapsedTime_;
956
971
  }
957
972
  tick() {
958
973
  const now = this.timeSource_.now();
959
- const dt = now - this.lastTimeMs_;
960
- this.dt_ = dt;
961
- this.elapsedTime_ += dt;
974
+ const dt = Math.min(now - this.lastTimeMs_, 100);
975
+ this.delta_ = this.time_.delta = dt / 1e3;
976
+ this.ellapsedTime_ = this.time_.elapsed += dt / 1e3;
962
977
  this.lastTimeMs_ = now;
963
978
  }
979
+ getTime() {
980
+ return this.time_;
981
+ }
964
982
  };
965
983
 
966
984
  // src/runtime/loop.ts
@@ -979,7 +997,7 @@ var Loop = class {
979
997
  const loop = () => {
980
998
  if (!this.running) return;
981
999
  this.clock.tick();
982
- this.stepFn(this.clock.dt, world);
1000
+ this.stepFn(this.clock.deltaMs, world);
983
1001
  this.platformLoop(loop);
984
1002
  };
985
1003
  this.platformLoop(loop);
@@ -1164,7 +1182,8 @@ var Engine = class {
1164
1182
  this.runtime = new Runtime(params.loop);
1165
1183
  this.logger = params.logger ?? new NoopLogger();
1166
1184
  this.world = new World3({
1167
- logger: this.logger
1185
+ logger: this.logger,
1186
+ maxEntityCount: params.maxEntityCount ?? ECS_DEFAULTS.MAX_ENTITY_COUNT
1168
1187
  });
1169
1188
  }
1170
1189
  init() {
@@ -1206,9 +1225,9 @@ export {
1206
1225
  PluginBase,
1207
1226
  PluginError,
1208
1227
  PluginsManager,
1228
+ Resource,
1209
1229
  ResourcesManager,
1210
1230
  Runtime,
1211
- SingletonStorage,
1212
1231
  System,
1213
1232
  SystemBase,
1214
1233
  SystemError,
@@ -1220,6 +1239,7 @@ export {
1220
1239
  createEventKey,
1221
1240
  entry,
1222
1241
  getPluginMetadata,
1242
+ getResourceMetadata,
1223
1243
  getSystemMetadata,
1224
1244
  isPlugin,
1225
1245
  isSystem,