@draug/engine 1.0.10 → 1.0.12

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 CHANGED
@@ -245,7 +245,7 @@ In Node, `performance.now()` is available on modern versions; otherwise pass `{
245
245
 
246
246
  ### Runtime (optional)
247
247
 
248
- `Runtime` is a tiny wrapper: `update(dt)` forwards to `world.update(dt)`. In the Amber workspace it is usually constructed together with `@draug/assets`’s `AssetsManager` for loading textures and similar; for a headless server or a toy sim you can ignore `Runtime` and call `world.update` directly from your own driver.
248
+ `Runtime` is a tiny wrapper: `update(dt)` forwards to `world.update(dt)`. In the Amber workspace it is usually constructed together with `AssetsManager` from this package for loading textures and similar; for a headless server or a toy sim you can ignore `Runtime` and call `world.update` directly from your own driver.
249
249
 
250
250
  ## Configuration
251
251
 
@@ -269,7 +269,7 @@ Issues and PRs are welcome in the [GitHub repository](https://github.com/yazmeya
269
269
 
270
270
  - **Author:** future_undefined — [GitHub @yazmeyaa](https://github.com/yazmeyaa) · [evgenijantonenkov456@gmail.com](mailto:evgenijantonenkov456@gmail.com)
271
271
 
272
- Related workspace packages: `@draug/core` (DAG sort, object pools, etc.) and `@draug/types` (shared `ClassType` helpers). Everything this library exposes to apps is listed in `src/index.ts` in the repository.
272
+ Related workspace packages: `@draug/types` (WebSocket `RawData` helper for networking code). Everything this library exposes to apps is listed in `src/index.ts` in the repository.
273
273
 
274
274
  ## License
275
275
 
package/dist/index.cjs CHANGED
@@ -20,15 +20,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ Asset: () => Asset,
24
+ AssetState: () => AssetState,
25
+ AssetStorage: () => AssetStorage,
26
+ AssetsManager: () => AssetsManager,
23
27
  Clock: () => Clock,
24
28
  Commands: () => Commands,
25
29
  Component: () => Component,
26
30
  ComponentAlreadyRegisteredError: () => ComponentAlreadyRegisteredError,
27
31
  ComponentStorage: () => ComponentStorage,
28
32
  ComponentsManager: () => ComponentsManager,
33
+ DAGNode: () => DAGNode,
29
34
  EntitiesManager: () => EntitiesManager,
30
35
  EntityMaskNotFoundError: () => EntityMaskNotFoundError,
31
36
  EntityRef: () => EntityRef,
37
+ ErrDAGCycleDetected: () => ErrDAGCycleDetected,
32
38
  ErrMissingPluginMetadata: () => ErrMissingPluginMetadata,
33
39
  ErrMissingSystemMetadata: () => ErrMissingSystemMetadata,
34
40
  ErrNotAPlugin: () => ErrNotAPlugin,
@@ -38,6 +44,7 @@ __export(index_exports, {
38
44
  EventBuffer: () => EventBuffer,
39
45
  EventBus: () => EventBus,
40
46
  GameLoop: () => GameLoop,
47
+ ObjectPool: () => ObjectPool,
41
48
  Plugin: () => Plugin,
42
49
  PluginBase: () => PluginBase,
43
50
  PluginError: () => PluginError,
@@ -50,18 +57,62 @@ __export(index_exports, {
50
57
  SystemError: () => SystemError,
51
58
  SystemsManager: () => SystemsManager,
52
59
  UnregisteredComponentStorageError: () => UnregisteredComponentStorageError,
60
+ VisitedState: () => VisitedState,
53
61
  World: () => World3,
54
62
  createEventKey: () => createEventKey,
55
63
  entry: () => entry,
56
64
  getPluginMetadata: () => getPluginMetadata,
57
65
  getSystemMetadata: () => getSystemMetadata,
58
66
  isPlugin: () => isPlugin,
59
- isSystem: () => isSystem
67
+ isSystem: () => isSystem,
68
+ topologicalSort: () => topologicalSort
60
69
  });
61
70
  module.exports = __toCommonJS(index_exports);
62
71
 
72
+ // src/core/graph/dag.ts
73
+ var VisitedState = /* @__PURE__ */ ((VisitedState2) => {
74
+ VisitedState2[VisitedState2["Unvisited"] = 0] = "Unvisited";
75
+ VisitedState2[VisitedState2["Visiting"] = 1] = "Visiting";
76
+ VisitedState2[VisitedState2["Visited"] = 2] = "Visited";
77
+ return VisitedState2;
78
+ })(VisitedState || {});
79
+ var DAGNode = class {
80
+ data;
81
+ vertices = [];
82
+ constructor(data, vertices) {
83
+ this.data = data;
84
+ if (vertices)
85
+ this.vertices = vertices;
86
+ }
87
+ };
88
+ var ErrDAGCycleDetected = class extends Error {
89
+ constructor() {
90
+ super(`Cycle detected!`);
91
+ }
92
+ };
93
+ function topologicalSort(nodes) {
94
+ const visited = /* @__PURE__ */ new Map();
95
+ const result = [];
96
+ const dfs = (node) => {
97
+ const state = visited.get(node) ?? 0 /* Unvisited */;
98
+ if (state === 2 /* Visited */) return;
99
+ if (state === 1 /* Visiting */) {
100
+ throw new ErrDAGCycleDetected();
101
+ }
102
+ visited.set(node, 1 /* Visiting */);
103
+ for (const child of node.vertices) {
104
+ dfs(child);
105
+ }
106
+ visited.set(node, 2 /* Visited */);
107
+ result.push(node);
108
+ };
109
+ for (const node of nodes) {
110
+ dfs(node);
111
+ }
112
+ return result.reverse();
113
+ }
114
+
63
115
  // src/ecs/system.ts
64
- var import_core = require("@draug/core");
65
116
  var SystemError = class extends Error {
66
117
  constructor(target) {
67
118
  super(`[System Error] (System "${target.name}".`);
@@ -88,7 +139,8 @@ function System(props) {
88
139
  const query = { ...props.query };
89
140
  const requiredComponents = new Set(props.requiredComponents);
90
141
  const computeAfter = new Set(props.computeAfter);
91
- const metadata = { query, requiredComponents, computeAfter };
142
+ const phase = props.phase ?? "main";
143
+ const metadata = { query, requiredComponents, computeAfter, phase };
92
144
  systemTarget[SystemMetadataSymbol] = metadata;
93
145
  };
94
146
  }
@@ -158,11 +210,26 @@ var SystemsManager = class {
158
210
  }
159
211
  }
160
212
  buildSystemsArray() {
213
+ const pre = [];
214
+ const main = [];
215
+ const post = [];
161
216
  const map = /* @__PURE__ */ new Map();
162
217
  for (const [ctor, system] of this.systems_.entries()) {
163
- map.set(ctor, new import_core.DAGNode(system));
218
+ const meta = getSystemMetadata(ctor);
219
+ switch (meta.phase) {
220
+ case "pre":
221
+ pre.push(system);
222
+ break;
223
+ case "post":
224
+ post.push(system);
225
+ break;
226
+ default:
227
+ main.push(system);
228
+ map.set(ctor, new DAGNode(system));
229
+ break;
230
+ }
164
231
  }
165
- for (const ctor of this.systems_.keys()) {
232
+ for (const ctor of map.keys()) {
166
233
  const currentNode = map.get(ctor);
167
234
  const { computeAfter } = getSystemMetadata(ctor);
168
235
  for (const depCtor of computeAfter ?? []) {
@@ -173,7 +240,11 @@ var SystemsManager = class {
173
240
  depNode.vertices.push(currentNode);
174
241
  }
175
242
  }
176
- this.executionOrder_ = (0, import_core.topologicalSort)(map.values()).map((x) => x.data);
243
+ this.executionOrder_ = [
244
+ ...pre,
245
+ ...topologicalSort(map.values()).map((x) => x.data),
246
+ ...post
247
+ ];
177
248
  }
178
249
  };
179
250
 
@@ -274,8 +345,36 @@ var EventBus = class {
274
345
  }
275
346
  };
276
347
 
348
+ // src/core/memory/pool.ts
349
+ var ObjectPool = class {
350
+ pool_;
351
+ factory_;
352
+ cursor_;
353
+ constructor(factory, initialSize = 1024) {
354
+ this.pool_ = new Array(initialSize);
355
+ for (let i = 0; i < initialSize; i++) {
356
+ this.pool_[i] = factory();
357
+ }
358
+ this.factory_ = factory;
359
+ this.cursor_ = initialSize - 1;
360
+ }
361
+ acquire() {
362
+ if (this.cursor_ < 0) this.grow();
363
+ return this.pool_[this.cursor_--];
364
+ }
365
+ release(obj) {
366
+ this.pool_[++this.cursor_] = obj;
367
+ }
368
+ grow() {
369
+ const oldSize = this.pool_.length;
370
+ const newSize = oldSize * 2;
371
+ for (let i = oldSize; i < newSize; i++) this.pool_[i] = this.factory_();
372
+ this.cursor_ = newSize - 1;
373
+ this.pool_.length = newSize;
374
+ }
375
+ };
376
+
277
377
  // src/ecs/components/component-storage.ts
278
- var import_core2 = require("@draug/core");
279
378
  var import_bitmap_index = require("bitmap-index");
280
379
  var import_ts_sparse_set = require("ts-sparse-set");
281
380
  var ComponentStorage = class {
@@ -287,7 +386,7 @@ var ComponentStorage = class {
287
386
  constructor(cap = ECS_DEFAULTS.MAX_ENTITY_COUNT, factory, cls) {
288
387
  this.set_ = new import_ts_sparse_set.SparseSet(cap);
289
388
  this.bits_ = new import_bitmap_index.Bitmap(cap);
290
- this.pool_ = new import_core2.ObjectPool(factory, cap);
389
+ this.pool_ = new ObjectPool(factory, cap);
291
390
  this.cls = cls;
292
391
  }
293
392
  bitmap() {
@@ -646,7 +745,6 @@ var QueryManager = class {
646
745
  };
647
746
 
648
747
  // src/plugin/plugin.ts
649
- var import_core3 = require("@draug/core");
650
748
  var PluginMetadataSymbol = /* @__PURE__ */ Symbol("plugin");
651
749
  function Plugin(metadata) {
652
750
  return (target) => {
@@ -729,7 +827,7 @@ var PluginsManager = class {
729
827
  build() {
730
828
  const nodes = /* @__PURE__ */ new Map();
731
829
  for (const id2 of this.plugins_.keys()) {
732
- nodes.set(id2, new import_core3.DAGNode(id2));
830
+ nodes.set(id2, new DAGNode(id2));
733
831
  }
734
832
  for (const [id2, entry2] of this.plugins_) {
735
833
  const node = nodes.get(id2);
@@ -744,9 +842,9 @@ var PluginsManager = class {
744
842
  }
745
843
  let sortedNodes;
746
844
  try {
747
- sortedNodes = (0, import_core3.topologicalSort)(nodes.values());
845
+ sortedNodes = topologicalSort(nodes.values());
748
846
  } catch (e) {
749
- if (e instanceof import_core3.ErrDAGCycleDetected) {
847
+ if (e instanceof ErrDAGCycleDetected) {
750
848
  throw new ErrDAGCycleDetectedPlugin();
751
849
  }
752
850
  throw e;
@@ -903,17 +1001,169 @@ var Runtime = class {
903
1001
  this.world.update(dt);
904
1002
  }
905
1003
  };
1004
+
1005
+ // src/assets/assets.ts
1006
+ var AssetState = /* @__PURE__ */ ((AssetState2) => {
1007
+ AssetState2[AssetState2["NOT_READY"] = 1] = "NOT_READY";
1008
+ AssetState2[AssetState2["LOADING"] = 2] = "LOADING";
1009
+ AssetState2[AssetState2["READY"] = 3] = "READY";
1010
+ return AssetState2;
1011
+ })(AssetState || {});
1012
+ var Asset = class {
1013
+ constructor(id2, url, loader, disposer) {
1014
+ this.id = id2;
1015
+ this.url = url;
1016
+ this.loader = loader;
1017
+ this.disposer = disposer;
1018
+ }
1019
+ id;
1020
+ url;
1021
+ loader;
1022
+ disposer;
1023
+ data_ = null;
1024
+ state_ = 1 /* NOT_READY */;
1025
+ loading_ = null;
1026
+ disposed_ = false;
1027
+ async load() {
1028
+ if (this.state_ === 3 /* READY */)
1029
+ return this.data_;
1030
+ if (this.state_ === 2 /* LOADING */)
1031
+ return this.loading_;
1032
+ this.state_ = 2 /* LOADING */;
1033
+ this.disposed_ = false;
1034
+ this.loading_ = this.loader(this.url).then((data) => {
1035
+ if (this.disposed_) return data;
1036
+ this.data_ = data;
1037
+ this.state_ = 3 /* READY */;
1038
+ this.loading_ = null;
1039
+ return data;
1040
+ }).catch((err) => {
1041
+ this.state_ = 1 /* NOT_READY */;
1042
+ this.loading_ = null;
1043
+ throw err;
1044
+ });
1045
+ return this.loading_;
1046
+ }
1047
+ reset() {
1048
+ this.data_ = null;
1049
+ this.loading_ = null;
1050
+ this.state_ = 1 /* NOT_READY */;
1051
+ }
1052
+ async dispose() {
1053
+ this.disposed_ = true;
1054
+ if (this.data_)
1055
+ await this.disposer(this.data_);
1056
+ this.reset();
1057
+ }
1058
+ getData() {
1059
+ if (this.state_ !== 3 /* READY */)
1060
+ throw new Error("Data is not loaded yet!");
1061
+ return this.data_;
1062
+ }
1063
+ };
1064
+ var AssetStorage = class {
1065
+ constructor(nextIdFn_, defaultLoader_, defaultDisposer_) {
1066
+ this.nextIdFn_ = nextIdFn_;
1067
+ this.defaultLoader_ = defaultLoader_;
1068
+ this.defaultDisposer_ = defaultDisposer_;
1069
+ }
1070
+ nextIdFn_;
1071
+ defaultLoader_;
1072
+ defaultDisposer_;
1073
+ items_ = /* @__PURE__ */ new Map();
1074
+ newAsset(id2, url, loader, disposer) {
1075
+ return new Asset(id2, url, loader, disposer);
1076
+ }
1077
+ add(url) {
1078
+ const id2 = this.nextIdFn_();
1079
+ const rs = this.newAsset(id2, url, this.defaultLoader_, this.defaultDisposer_);
1080
+ this.items_.set(id2, rs);
1081
+ return rs;
1082
+ }
1083
+ addCustom(url, customLoader, customDisposer) {
1084
+ const id2 = this.nextIdFn_();
1085
+ const rs = this.newAsset(id2, url, customLoader, customDisposer);
1086
+ this.items_.set(id2, rs);
1087
+ return rs;
1088
+ }
1089
+ get(id2) {
1090
+ const item = this.items_.get(id2);
1091
+ return item ?? null;
1092
+ }
1093
+ tryGet(id2) {
1094
+ const item = this.items_.get(id2);
1095
+ if (!item)
1096
+ throw new Error(`Asset with id ${id2} in storage ${this.constructor.name} not exist`);
1097
+ return item;
1098
+ }
1099
+ async remove(id2) {
1100
+ const item = this.items_.get(id2);
1101
+ if (!item) return;
1102
+ await item.dispose();
1103
+ this.items_.delete(id2);
1104
+ }
1105
+ async loadAll() {
1106
+ await Promise.all(
1107
+ Array.from(this.items_.values(), (r) => r.load())
1108
+ );
1109
+ }
1110
+ async clearAll() {
1111
+ await Promise.all(
1112
+ Array.from(this.items_.values(), (r) => r.dispose())
1113
+ );
1114
+ this.items_.clear();
1115
+ }
1116
+ };
1117
+ var NOOP_DISPOSER = async () => {
1118
+ };
1119
+ var AssetsManager = class {
1120
+ storages_ = /* @__PURE__ */ new Map();
1121
+ currId = 0;
1122
+ nextIdFn = () => {
1123
+ return ++this.currId;
1124
+ };
1125
+ register(res, defaultLoader, defaultDisposer = NOOP_DISPOSER) {
1126
+ const storage = new AssetStorage(
1127
+ this.nextIdFn,
1128
+ defaultLoader,
1129
+ defaultDisposer
1130
+ );
1131
+ this.storages_.set(res, storage);
1132
+ return storage;
1133
+ }
1134
+ getStorage(res) {
1135
+ return this.storages_.get(res) ?? null;
1136
+ }
1137
+ tryGetStorage(res) {
1138
+ const s = this.storages_.get(res);
1139
+ if (!s)
1140
+ throw new Error(`Storage ${res.name} is not registered!`);
1141
+ return s;
1142
+ }
1143
+ async loadAll() {
1144
+ await Promise.all(Array.from(this.storages_.values(), (s) => s.loadAll()));
1145
+ }
1146
+ disposeAll() {
1147
+ Array.from(this.storages_.values(), (s) => s.clearAll());
1148
+ }
1149
+ };
906
1150
  // Annotate the CommonJS export names for ESM import in node:
907
1151
  0 && (module.exports = {
1152
+ Asset,
1153
+ AssetState,
1154
+ AssetStorage,
1155
+ AssetsManager,
908
1156
  Clock,
909
1157
  Commands,
910
1158
  Component,
911
1159
  ComponentAlreadyRegisteredError,
912
1160
  ComponentStorage,
913
1161
  ComponentsManager,
1162
+ DAGNode,
914
1163
  EntitiesManager,
915
1164
  EntityMaskNotFoundError,
916
1165
  EntityRef,
1166
+ ErrDAGCycleDetected,
917
1167
  ErrMissingPluginMetadata,
918
1168
  ErrMissingSystemMetadata,
919
1169
  ErrNotAPlugin,
@@ -923,6 +1173,7 @@ var Runtime = class {
923
1173
  EventBuffer,
924
1174
  EventBus,
925
1175
  GameLoop,
1176
+ ObjectPool,
926
1177
  Plugin,
927
1178
  PluginBase,
928
1179
  PluginError,
@@ -935,12 +1186,14 @@ var Runtime = class {
935
1186
  SystemError,
936
1187
  SystemsManager,
937
1188
  UnregisteredComponentStorageError,
1189
+ VisitedState,
938
1190
  World,
939
1191
  createEventKey,
940
1192
  entry,
941
1193
  getPluginMetadata,
942
1194
  getSystemMetadata,
943
1195
  isPlugin,
944
- isSystem
1196
+ isSystem,
1197
+ topologicalSort
945
1198
  });
946
1199
  //# sourceMappingURL=index.cjs.map