@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 +2 -2
- package/dist/index.cjs +266 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -9
- package/dist/index.d.ts +87 -9
- package/dist/index.js +254 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -3
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
|
|
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/
|
|
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
|
|
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
|
-
|
|
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
|
|
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_ =
|
|
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
|
|
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
|
|
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 =
|
|
845
|
+
sortedNodes = topologicalSort(nodes.values());
|
|
748
846
|
} catch (e) {
|
|
749
|
-
if (e instanceof
|
|
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
|