koota 0.1.8 → 0.1.10
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 +70 -23
- package/dist/{chunk-XQK5JJDD.js → chunk-L2TCLFSQ.js} +77 -13
- package/dist/index.cjs +77 -13
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/react.cjs +77 -13
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{world-liWg9VB-.d.cts → world-CIrtrsKj.d.cts} +7 -5
- package/dist/{world-liWg9VB-.d.ts → world-CIrtrsKj.d.ts} +7 -5
- package/package.json +5 -4
- package/react/index.cjs +77 -13
- package/react/index.d.cts +1 -1
- package/react/index.d.ts +1 -1
- package/react/index.js +1 -1
- package/dist/chunk-5UTSDPRH.js +0 -1309
- package/dist/chunk-TOFVEQXZ.js +0 -1338
- package/dist/chunk-WCDM6ECE.js +0 -1343
- package/dist/world-B-pehncS.d.cts +0 -234
- package/dist/world-B-pehncS.d.ts +0 -234
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ const Velocity = trait({ x: 0, y: 0 });
|
|
|
20
20
|
|
|
21
21
|
// Trait with a callback for initial value
|
|
22
22
|
// ⚠️ Must be an object
|
|
23
|
-
const Mesh = trait(() => THREE.Mesh());
|
|
23
|
+
const Mesh = trait(() => new THREE.Mesh());
|
|
24
24
|
|
|
25
25
|
// Tag trait (no data)
|
|
26
26
|
const IsActive = trait();
|
|
@@ -309,6 +309,20 @@ entity.set(Position, { x: 10, y: 20 });
|
|
|
309
309
|
entity.remove(Position);
|
|
310
310
|
```
|
|
311
311
|
|
|
312
|
+
### Change detection with `udpateEach`
|
|
313
|
+
|
|
314
|
+
By default, `updateEach` will automatically turn on change detection for traits that are being tracked via `onChange` or the `Changed` modifier. If you want to silence change detection for a loop or force it to always run, you can do so with an options config.
|
|
315
|
+
|
|
316
|
+
```js
|
|
317
|
+
// Setting changeDetection to 'never' will silence it, triggering no change events
|
|
318
|
+
world.query(Position, Velocity).updateEach(([position, velocity]) => {
|
|
319
|
+
}, { changeDetection: 'never' });
|
|
320
|
+
|
|
321
|
+
// Setting changeDetection to 'always' will ignore selective tracking and always emit change events for all traits that are mutated
|
|
322
|
+
world.query(Position, Velocity).updateEach(([position, velocity]) => {
|
|
323
|
+
}, { changeDetection: 'never' });
|
|
324
|
+
```
|
|
325
|
+
|
|
312
326
|
### World traits
|
|
313
327
|
|
|
314
328
|
For global data like time, these can be traits added to the world. **World traits do not appear in queries.**
|
|
@@ -338,7 +352,7 @@ world.query(Position, Velocity, Mass)
|
|
|
338
352
|
|
|
339
353
|
### Modifying trait stores direclty
|
|
340
354
|
|
|
341
|
-
For performance-critical operations, you can modify trait stores directly using the useStore hook. This approach bypasses some of the safety checks and event triggers, so use it with caution. All stores are structure of arrays for performance purposes.
|
|
355
|
+
For performance-critical operations, you can modify trait stores directly using the `useStore` hook. This approach bypasses some of the safety checks and event triggers, so use it with caution. All stores are structure of arrays for performance purposes.
|
|
342
356
|
|
|
343
357
|
```js
|
|
344
358
|
// Returns the SoA stores
|
|
@@ -400,6 +414,11 @@ const time = world.get(Time)
|
|
|
400
414
|
|
|
401
415
|
// Sets the trait and triggers a change event
|
|
402
416
|
world.set(Time, { current: performance.now() })
|
|
417
|
+
// Can take a callback with the previous state passed in
|
|
418
|
+
world.set(Time, (prev) => ({
|
|
419
|
+
current: performance.now(),
|
|
420
|
+
delta: performance.now() - prev.current
|
|
421
|
+
}))
|
|
403
422
|
|
|
404
423
|
// Subscribe to add, remove or change events for a set of query parameters
|
|
405
424
|
// Anything you can put in a query is legal
|
|
@@ -445,6 +464,11 @@ const position = entity.get(Position)
|
|
|
445
464
|
|
|
446
465
|
// Sets the trait and triggers a change event
|
|
447
466
|
entity.set(Position, { x: 10, y: 10 })
|
|
467
|
+
// Can take a callback with the previous state passed in
|
|
468
|
+
entity.set(Position, (prev) => ({
|
|
469
|
+
x: prev + 1,
|
|
470
|
+
y: prev + 1
|
|
471
|
+
}))
|
|
448
472
|
|
|
449
473
|
// Get the targets for a relationship
|
|
450
474
|
// Return Entity[]
|
|
@@ -464,14 +488,37 @@ entity.destroy()
|
|
|
464
488
|
|
|
465
489
|
### Trait
|
|
466
490
|
|
|
467
|
-
A trait
|
|
491
|
+
A trait is a specific block of data. They are added to entities to build up its overall data signature. If you are familiar with ECS, it is our version of a component. It is called a trait instead to not get confused with React or web components.
|
|
492
|
+
|
|
493
|
+
A trait can be created with a schema that describes the kind of data it will hold.
|
|
468
494
|
|
|
469
495
|
```js
|
|
470
|
-
// A schema
|
|
471
496
|
const Position = trait({ x: 0, y: 0, z: 0 })
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
In cases where the data needs to be initialized for each instance of the trait created, a callback can be passed in to be used a as a lazy initializer.
|
|
500
|
+
|
|
501
|
+
```js
|
|
502
|
+
// ❌ The items array will be shared between every instance of this trait
|
|
503
|
+
const Inventory = trait({
|
|
504
|
+
items: [],
|
|
505
|
+
max: 10,
|
|
506
|
+
})
|
|
507
|
+
|
|
508
|
+
// ✅ With a lazy initializer, each instance will now get its own array
|
|
509
|
+
const Inventory = trait({
|
|
510
|
+
items: () => [],
|
|
511
|
+
max: 10,
|
|
512
|
+
})
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Sometimes a trait only has one field that points to an object instance. In cases like this, it is useful to skip the schema and use a callback directly in the trait.
|
|
516
|
+
|
|
517
|
+
```js
|
|
518
|
+
const Velocity = trait(() => new THREE.Vector3())
|
|
472
519
|
|
|
473
|
-
//
|
|
474
|
-
const
|
|
520
|
+
// The returned state is simply the instance
|
|
521
|
+
const velocity = entity.get(Velocity)
|
|
475
522
|
```
|
|
476
523
|
|
|
477
524
|
Both schema-based and callback-based traits are used similarly, but they have different performance implications due to how their data is stored internally:
|
|
@@ -512,7 +559,7 @@ const store = [
|
|
|
512
559
|
];
|
|
513
560
|
|
|
514
561
|
// Similarly, this will create a new instance of Mesh in each index
|
|
515
|
-
const Mesh = trait(() => THREE.Mesh())
|
|
562
|
+
const Mesh = trait(() => new THREE.Mesh())
|
|
516
563
|
```
|
|
517
564
|
|
|
518
565
|
#### Typing traits
|
|
@@ -521,17 +568,17 @@ Traits can have a schema type passed into its generic. This can be useful if the
|
|
|
521
568
|
|
|
522
569
|
```js
|
|
523
570
|
type AttackerSchema = {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
571
|
+
continueCombo: boolean | null,
|
|
572
|
+
currentStageIndex: number | null,
|
|
573
|
+
stages: Array<AttackStage> | null,
|
|
574
|
+
startedAt: number | null,
|
|
528
575
|
}
|
|
529
576
|
|
|
530
577
|
const Attacker = trait<AttackerSchema>({
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
578
|
+
continueCombo: null,
|
|
579
|
+
currentStageIndex: null,
|
|
580
|
+
stages: null,
|
|
581
|
+
startedAt: null,
|
|
535
582
|
})
|
|
536
583
|
```
|
|
537
584
|
|
|
@@ -540,18 +587,18 @@ Interfaces can be used with `Pick` to convert the key signatures into something
|
|
|
540
587
|
|
|
541
588
|
```js
|
|
542
589
|
interface AttackerSchema {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
590
|
+
continueCombo: boolean | null,
|
|
591
|
+
currentStageIndex: number | null,
|
|
592
|
+
stages: Array<AttackStage> | null,
|
|
593
|
+
startedAt: number | null,
|
|
547
594
|
}
|
|
548
595
|
|
|
549
596
|
// Pick is required to not get type errors
|
|
550
597
|
const Attacker = trait<Pick<AttackerSchema, keyof AttackerSchema>>({
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
598
|
+
continueCombo: null,
|
|
599
|
+
currentStageIndex: null,
|
|
600
|
+
stages: null,
|
|
601
|
+
startedAt: null,
|
|
555
602
|
})
|
|
556
603
|
```
|
|
557
604
|
|
|
@@ -553,11 +553,17 @@ Number.prototype.changed = function(trait2) {
|
|
|
553
553
|
return setChanged(world, this, trait2);
|
|
554
554
|
};
|
|
555
555
|
Number.prototype.get = function(trait2) {
|
|
556
|
-
const ctx = trait2[$internal];
|
|
557
|
-
const index = this & ENTITY_ID_MASK;
|
|
558
556
|
const worldId = this >>> WORLD_ID_SHIFT;
|
|
559
|
-
const
|
|
560
|
-
|
|
557
|
+
const world = universe.worlds[worldId];
|
|
558
|
+
const worldCtx = world[$internal];
|
|
559
|
+
const data = worldCtx.traitData.get(trait2);
|
|
560
|
+
if (!data) return void 0;
|
|
561
|
+
const mask = worldCtx.entityMasks[data.generationId][this];
|
|
562
|
+
if ((mask & data.bitflag) !== data.bitflag) return void 0;
|
|
563
|
+
const traitCtx = trait2[$internal];
|
|
564
|
+
const index = this & ENTITY_ID_MASK;
|
|
565
|
+
const store = traitCtx.stores[worldId];
|
|
566
|
+
return traitCtx.get(index, store);
|
|
561
567
|
};
|
|
562
568
|
Number.prototype.set = function(trait2, value, triggerChanged = true) {
|
|
563
569
|
const ctx = trait2[$internal];
|
|
@@ -794,6 +800,7 @@ var Query = class {
|
|
|
794
800
|
__publicField(this, "entities", new SparseSet());
|
|
795
801
|
__publicField(this, "isTracking", false);
|
|
796
802
|
__publicField(this, "hasChangedModifiers", false);
|
|
803
|
+
__publicField(this, "changedTraits", /* @__PURE__ */ new Set());
|
|
797
804
|
__publicField(this, "toRemove", new SparseSet());
|
|
798
805
|
__publicField(this, "addSubscriptions", /* @__PURE__ */ new Set());
|
|
799
806
|
__publicField(this, "removeSubscriptions", /* @__PURE__ */ new Set());
|
|
@@ -847,6 +854,7 @@ var Query = class {
|
|
|
847
854
|
}
|
|
848
855
|
if (parameter.type.includes("changed")) {
|
|
849
856
|
for (const trait2 of traits) {
|
|
857
|
+
this.changedTraits.add(trait2);
|
|
850
858
|
const data = ctx.traitData.get(trait2);
|
|
851
859
|
this.traitData.changed.push(data);
|
|
852
860
|
this.traits.push(trait2);
|
|
@@ -1084,27 +1092,60 @@ function createQueryResult(query, world, params) {
|
|
|
1084
1092
|
const traits = [];
|
|
1085
1093
|
getQueryStores(params, traits, stores, world);
|
|
1086
1094
|
const results = Object.assign(entities, {
|
|
1087
|
-
updateEach(callback, options) {
|
|
1088
|
-
const changeDetection = options?.changeDetection ?? false;
|
|
1095
|
+
updateEach(callback, options = { changeDetection: "auto" }) {
|
|
1089
1096
|
const state = new Array(traits.length);
|
|
1090
|
-
if (
|
|
1097
|
+
if (options.changeDetection === "auto") {
|
|
1098
|
+
const changedPairs = [];
|
|
1099
|
+
const atomicSnapshots = [];
|
|
1100
|
+
const trackedTraits = [];
|
|
1101
|
+
const untrackedTraits = [];
|
|
1102
|
+
for (const trait2 of traits) {
|
|
1103
|
+
if (world[$internal].trackedTraits.has(trait2)) {
|
|
1104
|
+
trackedTraits.push(trait2);
|
|
1105
|
+
} else if (query.hasChangedModifiers && query.changedTraits.has(trait2)) {
|
|
1106
|
+
trackedTraits.push(trait2);
|
|
1107
|
+
} else {
|
|
1108
|
+
untrackedTraits.push(trait2);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1091
1111
|
for (let i = 0; i < entities.length; i++) {
|
|
1092
1112
|
const entity = entities[i];
|
|
1093
1113
|
const eid = getEntityId(entity);
|
|
1094
1114
|
for (let j = 0; j < traits.length; j++) {
|
|
1095
1115
|
const trait2 = traits[j];
|
|
1096
1116
|
const ctx = trait2[$internal];
|
|
1097
|
-
|
|
1117
|
+
const value = ctx.get(eid, stores[j]);
|
|
1118
|
+
state[j] = value;
|
|
1119
|
+
atomicSnapshots[j] = ctx.type === "aos" ? { ...value } : null;
|
|
1098
1120
|
}
|
|
1099
1121
|
callback(state, entity, i);
|
|
1100
1122
|
if (!world.has(entity)) continue;
|
|
1101
|
-
for (let j = 0; j <
|
|
1102
|
-
const trait2 =
|
|
1123
|
+
for (let j = 0; j < trackedTraits.length; j++) {
|
|
1124
|
+
const trait2 = trackedTraits[j];
|
|
1125
|
+
const ctx = trait2[$internal];
|
|
1126
|
+
const newValue = state[j];
|
|
1127
|
+
let changed = false;
|
|
1128
|
+
if (ctx.type === "aos") {
|
|
1129
|
+
changed = ctx.fastSetWithChangeDetection(eid, stores[j], newValue);
|
|
1130
|
+
if (!changed) {
|
|
1131
|
+
changed = !shallowEqual(newValue, atomicSnapshots[j]);
|
|
1132
|
+
}
|
|
1133
|
+
} else {
|
|
1134
|
+
changed = ctx.fastSetWithChangeDetection(eid, stores[j], newValue);
|
|
1135
|
+
}
|
|
1136
|
+
if (changed) changedPairs.push([entity, trait2]);
|
|
1137
|
+
}
|
|
1138
|
+
for (let j = 0; j < untrackedTraits.length; j++) {
|
|
1139
|
+
const trait2 = untrackedTraits[j];
|
|
1103
1140
|
const ctx = trait2[$internal];
|
|
1104
1141
|
ctx.fastSet(eid, stores[j], state[j]);
|
|
1105
1142
|
}
|
|
1106
1143
|
}
|
|
1107
|
-
|
|
1144
|
+
for (let i = 0; i < changedPairs.length; i++) {
|
|
1145
|
+
const [entity, trait2] = changedPairs[i];
|
|
1146
|
+
entity.changed(trait2);
|
|
1147
|
+
}
|
|
1148
|
+
} else if (options.changeDetection === "always") {
|
|
1108
1149
|
const changedPairs = [];
|
|
1109
1150
|
const atomicSnapshots = [];
|
|
1110
1151
|
for (let i = 0; i < entities.length; i++) {
|
|
@@ -1139,6 +1180,23 @@ function createQueryResult(query, world, params) {
|
|
|
1139
1180
|
const [entity, trait2] = changedPairs[i];
|
|
1140
1181
|
entity.changed(trait2);
|
|
1141
1182
|
}
|
|
1183
|
+
} else if (options.changeDetection === "never") {
|
|
1184
|
+
for (let i = 0; i < entities.length; i++) {
|
|
1185
|
+
const entity = entities[i];
|
|
1186
|
+
const eid = getEntityId(entity);
|
|
1187
|
+
for (let j = 0; j < traits.length; j++) {
|
|
1188
|
+
const trait2 = traits[j];
|
|
1189
|
+
const ctx = trait2[$internal];
|
|
1190
|
+
state[j] = ctx.get(eid, stores[j]);
|
|
1191
|
+
}
|
|
1192
|
+
callback(state, entity, i);
|
|
1193
|
+
if (!world.has(entity)) continue;
|
|
1194
|
+
for (let j = 0; j < traits.length; j++) {
|
|
1195
|
+
const trait2 = traits[j];
|
|
1196
|
+
const ctx = trait2[$internal];
|
|
1197
|
+
ctx.fastSet(eid, stores[j], state[j]);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1142
1200
|
}
|
|
1143
1201
|
return results;
|
|
1144
1202
|
},
|
|
@@ -1194,7 +1252,8 @@ var World = class {
|
|
|
1194
1252
|
dirtyMasks: /* @__PURE__ */ new Map(),
|
|
1195
1253
|
trackingSnapshots: /* @__PURE__ */ new Map(),
|
|
1196
1254
|
changedMasks: /* @__PURE__ */ new Map(),
|
|
1197
|
-
worldEntity: null
|
|
1255
|
+
worldEntity: null,
|
|
1256
|
+
trackedTraits: /* @__PURE__ */ new Set()
|
|
1198
1257
|
});
|
|
1199
1258
|
__privateAdd(this, _isInitialized, false);
|
|
1200
1259
|
__publicField(this, "traits", /* @__PURE__ */ new Set());
|
|
@@ -1268,6 +1327,7 @@ var World = class {
|
|
|
1268
1327
|
ctx.trackingSnapshots.clear();
|
|
1269
1328
|
ctx.dirtyMasks.clear();
|
|
1270
1329
|
ctx.changedMasks.clear();
|
|
1330
|
+
ctx.trackedTraits.clear();
|
|
1271
1331
|
ctx.worldEntity = createEntity(this, IsExcluded);
|
|
1272
1332
|
}
|
|
1273
1333
|
query(...args) {
|
|
@@ -1317,7 +1377,11 @@ var World = class {
|
|
|
1317
1377
|
if (!ctx.traitData.has(trait2)) registerTrait(this, trait2);
|
|
1318
1378
|
const data = ctx.traitData.get(trait2);
|
|
1319
1379
|
data.changedSubscriptions.add(callback);
|
|
1320
|
-
|
|
1380
|
+
ctx.trackedTraits.add(trait2);
|
|
1381
|
+
return () => {
|
|
1382
|
+
data.changedSubscriptions.delete(callback);
|
|
1383
|
+
ctx.trackedTraits.delete(trait2);
|
|
1384
|
+
};
|
|
1321
1385
|
}
|
|
1322
1386
|
};
|
|
1323
1387
|
_id = new WeakMap();
|
package/dist/index.cjs
CHANGED
|
@@ -590,11 +590,17 @@ Number.prototype.changed = function(trait2) {
|
|
|
590
590
|
return setChanged(world, this, trait2);
|
|
591
591
|
};
|
|
592
592
|
Number.prototype.get = function(trait2) {
|
|
593
|
-
const ctx = trait2[$internal];
|
|
594
|
-
const index = this & ENTITY_ID_MASK;
|
|
595
593
|
const worldId = this >>> WORLD_ID_SHIFT;
|
|
596
|
-
const
|
|
597
|
-
|
|
594
|
+
const world = universe.worlds[worldId];
|
|
595
|
+
const worldCtx = world[$internal];
|
|
596
|
+
const data = worldCtx.traitData.get(trait2);
|
|
597
|
+
if (!data) return void 0;
|
|
598
|
+
const mask = worldCtx.entityMasks[data.generationId][this];
|
|
599
|
+
if ((mask & data.bitflag) !== data.bitflag) return void 0;
|
|
600
|
+
const traitCtx = trait2[$internal];
|
|
601
|
+
const index = this & ENTITY_ID_MASK;
|
|
602
|
+
const store = traitCtx.stores[worldId];
|
|
603
|
+
return traitCtx.get(index, store);
|
|
598
604
|
};
|
|
599
605
|
Number.prototype.set = function(trait2, value, triggerChanged = true) {
|
|
600
606
|
const ctx = trait2[$internal];
|
|
@@ -772,6 +778,7 @@ var Query = class {
|
|
|
772
778
|
__publicField(this, "entities", new SparseSet());
|
|
773
779
|
__publicField(this, "isTracking", false);
|
|
774
780
|
__publicField(this, "hasChangedModifiers", false);
|
|
781
|
+
__publicField(this, "changedTraits", /* @__PURE__ */ new Set());
|
|
775
782
|
__publicField(this, "toRemove", new SparseSet());
|
|
776
783
|
__publicField(this, "addSubscriptions", /* @__PURE__ */ new Set());
|
|
777
784
|
__publicField(this, "removeSubscriptions", /* @__PURE__ */ new Set());
|
|
@@ -825,6 +832,7 @@ var Query = class {
|
|
|
825
832
|
}
|
|
826
833
|
if (parameter.type.includes("changed")) {
|
|
827
834
|
for (const trait2 of traits) {
|
|
835
|
+
this.changedTraits.add(trait2);
|
|
828
836
|
const data = ctx.traitData.get(trait2);
|
|
829
837
|
this.traitData.changed.push(data);
|
|
830
838
|
this.traits.push(trait2);
|
|
@@ -1062,27 +1070,60 @@ function createQueryResult(query, world, params) {
|
|
|
1062
1070
|
const traits = [];
|
|
1063
1071
|
getQueryStores(params, traits, stores, world);
|
|
1064
1072
|
const results = Object.assign(entities, {
|
|
1065
|
-
updateEach(callback, options) {
|
|
1066
|
-
const changeDetection = options?.changeDetection ?? false;
|
|
1073
|
+
updateEach(callback, options = { changeDetection: "auto" }) {
|
|
1067
1074
|
const state = new Array(traits.length);
|
|
1068
|
-
if (
|
|
1075
|
+
if (options.changeDetection === "auto") {
|
|
1076
|
+
const changedPairs = [];
|
|
1077
|
+
const atomicSnapshots = [];
|
|
1078
|
+
const trackedTraits = [];
|
|
1079
|
+
const untrackedTraits = [];
|
|
1080
|
+
for (const trait2 of traits) {
|
|
1081
|
+
if (world[$internal].trackedTraits.has(trait2)) {
|
|
1082
|
+
trackedTraits.push(trait2);
|
|
1083
|
+
} else if (query.hasChangedModifiers && query.changedTraits.has(trait2)) {
|
|
1084
|
+
trackedTraits.push(trait2);
|
|
1085
|
+
} else {
|
|
1086
|
+
untrackedTraits.push(trait2);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1069
1089
|
for (let i = 0; i < entities.length; i++) {
|
|
1070
1090
|
const entity = entities[i];
|
|
1071
1091
|
const eid = getEntityId(entity);
|
|
1072
1092
|
for (let j = 0; j < traits.length; j++) {
|
|
1073
1093
|
const trait2 = traits[j];
|
|
1074
1094
|
const ctx = trait2[$internal];
|
|
1075
|
-
|
|
1095
|
+
const value = ctx.get(eid, stores[j]);
|
|
1096
|
+
state[j] = value;
|
|
1097
|
+
atomicSnapshots[j] = ctx.type === "aos" ? { ...value } : null;
|
|
1076
1098
|
}
|
|
1077
1099
|
callback(state, entity, i);
|
|
1078
1100
|
if (!world.has(entity)) continue;
|
|
1079
|
-
for (let j = 0; j <
|
|
1080
|
-
const trait2 =
|
|
1101
|
+
for (let j = 0; j < trackedTraits.length; j++) {
|
|
1102
|
+
const trait2 = trackedTraits[j];
|
|
1103
|
+
const ctx = trait2[$internal];
|
|
1104
|
+
const newValue = state[j];
|
|
1105
|
+
let changed = false;
|
|
1106
|
+
if (ctx.type === "aos") {
|
|
1107
|
+
changed = ctx.fastSetWithChangeDetection(eid, stores[j], newValue);
|
|
1108
|
+
if (!changed) {
|
|
1109
|
+
changed = !shallowEqual(newValue, atomicSnapshots[j]);
|
|
1110
|
+
}
|
|
1111
|
+
} else {
|
|
1112
|
+
changed = ctx.fastSetWithChangeDetection(eid, stores[j], newValue);
|
|
1113
|
+
}
|
|
1114
|
+
if (changed) changedPairs.push([entity, trait2]);
|
|
1115
|
+
}
|
|
1116
|
+
for (let j = 0; j < untrackedTraits.length; j++) {
|
|
1117
|
+
const trait2 = untrackedTraits[j];
|
|
1081
1118
|
const ctx = trait2[$internal];
|
|
1082
1119
|
ctx.fastSet(eid, stores[j], state[j]);
|
|
1083
1120
|
}
|
|
1084
1121
|
}
|
|
1085
|
-
|
|
1122
|
+
for (let i = 0; i < changedPairs.length; i++) {
|
|
1123
|
+
const [entity, trait2] = changedPairs[i];
|
|
1124
|
+
entity.changed(trait2);
|
|
1125
|
+
}
|
|
1126
|
+
} else if (options.changeDetection === "always") {
|
|
1086
1127
|
const changedPairs = [];
|
|
1087
1128
|
const atomicSnapshots = [];
|
|
1088
1129
|
for (let i = 0; i < entities.length; i++) {
|
|
@@ -1117,6 +1158,23 @@ function createQueryResult(query, world, params) {
|
|
|
1117
1158
|
const [entity, trait2] = changedPairs[i];
|
|
1118
1159
|
entity.changed(trait2);
|
|
1119
1160
|
}
|
|
1161
|
+
} else if (options.changeDetection === "never") {
|
|
1162
|
+
for (let i = 0; i < entities.length; i++) {
|
|
1163
|
+
const entity = entities[i];
|
|
1164
|
+
const eid = getEntityId(entity);
|
|
1165
|
+
for (let j = 0; j < traits.length; j++) {
|
|
1166
|
+
const trait2 = traits[j];
|
|
1167
|
+
const ctx = trait2[$internal];
|
|
1168
|
+
state[j] = ctx.get(eid, stores[j]);
|
|
1169
|
+
}
|
|
1170
|
+
callback(state, entity, i);
|
|
1171
|
+
if (!world.has(entity)) continue;
|
|
1172
|
+
for (let j = 0; j < traits.length; j++) {
|
|
1173
|
+
const trait2 = traits[j];
|
|
1174
|
+
const ctx = trait2[$internal];
|
|
1175
|
+
ctx.fastSet(eid, stores[j], state[j]);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1120
1178
|
}
|
|
1121
1179
|
return results;
|
|
1122
1180
|
},
|
|
@@ -1172,7 +1230,8 @@ var World = class {
|
|
|
1172
1230
|
dirtyMasks: /* @__PURE__ */ new Map(),
|
|
1173
1231
|
trackingSnapshots: /* @__PURE__ */ new Map(),
|
|
1174
1232
|
changedMasks: /* @__PURE__ */ new Map(),
|
|
1175
|
-
worldEntity: null
|
|
1233
|
+
worldEntity: null,
|
|
1234
|
+
trackedTraits: /* @__PURE__ */ new Set()
|
|
1176
1235
|
});
|
|
1177
1236
|
__privateAdd(this, _isInitialized, false);
|
|
1178
1237
|
__publicField(this, "traits", /* @__PURE__ */ new Set());
|
|
@@ -1246,6 +1305,7 @@ var World = class {
|
|
|
1246
1305
|
ctx.trackingSnapshots.clear();
|
|
1247
1306
|
ctx.dirtyMasks.clear();
|
|
1248
1307
|
ctx.changedMasks.clear();
|
|
1308
|
+
ctx.trackedTraits.clear();
|
|
1249
1309
|
ctx.worldEntity = createEntity(this, IsExcluded);
|
|
1250
1310
|
}
|
|
1251
1311
|
query(...args) {
|
|
@@ -1295,7 +1355,11 @@ var World = class {
|
|
|
1295
1355
|
if (!ctx.traitData.has(trait2)) registerTrait(this, trait2);
|
|
1296
1356
|
const data = ctx.traitData.get(trait2);
|
|
1297
1357
|
data.changedSubscriptions.add(callback);
|
|
1298
|
-
|
|
1358
|
+
ctx.trackedTraits.add(trait2);
|
|
1359
|
+
return () => {
|
|
1360
|
+
data.changedSubscriptions.delete(callback);
|
|
1361
|
+
ctx.trackedTraits.delete(trait2);
|
|
1362
|
+
};
|
|
1299
1363
|
}
|
|
1300
1364
|
};
|
|
1301
1365
|
_id = new WeakMap();
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Schema, T as Trait, N as Norm, M as ModifierData, W as World, Q as QueryParameter, R as Relation, a as RelationTarget, b as WildcardRelation } from './world-
|
|
2
|
-
export { $ as $internal, A as AoSFactory, C as ConfigurableTrait, l as Entity, j as ExtractIsTag, E as ExtractSchema, i as ExtractStore, k as ExtractStores, r as InstancesFromParameters, s as IsNotModifier, I as IsTag, m as QueryModifier, p as QueryResult, o as QueryResultOptions, n as QuerySubscriber, h as Store, q as StoresFromParameters, g as TraitInstance, f as TraitTuple, d as TraitType, e as TraitValue, c as createWorld } from './world-
|
|
1
|
+
import { S as Schema, T as Trait, N as Norm, M as ModifierData, W as World, Q as QueryParameter, R as Relation, a as RelationTarget, b as WildcardRelation } from './world-CIrtrsKj.cjs';
|
|
2
|
+
export { $ as $internal, A as AoSFactory, C as ConfigurableTrait, l as Entity, j as ExtractIsTag, E as ExtractSchema, i as ExtractStore, k as ExtractStores, r as InstancesFromParameters, s as IsNotModifier, I as IsTag, m as QueryModifier, p as QueryResult, o as QueryResultOptions, n as QuerySubscriber, h as Store, q as StoresFromParameters, g as TraitInstance, f as TraitTuple, d as TraitType, e as TraitValue, c as createWorld } from './world-CIrtrsKj.cjs';
|
|
3
3
|
|
|
4
4
|
declare function defineTrait<S extends Schema = {}>(schema?: S): Trait<Norm<S>>;
|
|
5
5
|
declare const trait: typeof defineTrait;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Schema, T as Trait, N as Norm, M as ModifierData, W as World, Q as QueryParameter, R as Relation, a as RelationTarget, b as WildcardRelation } from './world-
|
|
2
|
-
export { $ as $internal, A as AoSFactory, C as ConfigurableTrait, l as Entity, j as ExtractIsTag, E as ExtractSchema, i as ExtractStore, k as ExtractStores, r as InstancesFromParameters, s as IsNotModifier, I as IsTag, m as QueryModifier, p as QueryResult, o as QueryResultOptions, n as QuerySubscriber, h as Store, q as StoresFromParameters, g as TraitInstance, f as TraitTuple, d as TraitType, e as TraitValue, c as createWorld } from './world-
|
|
1
|
+
import { S as Schema, T as Trait, N as Norm, M as ModifierData, W as World, Q as QueryParameter, R as Relation, a as RelationTarget, b as WildcardRelation } from './world-CIrtrsKj.js';
|
|
2
|
+
export { $ as $internal, A as AoSFactory, C as ConfigurableTrait, l as Entity, j as ExtractIsTag, E as ExtractSchema, i as ExtractStore, k as ExtractStores, r as InstancesFromParameters, s as IsNotModifier, I as IsTag, m as QueryModifier, p as QueryResult, o as QueryResultOptions, n as QuerySubscriber, h as Store, q as StoresFromParameters, g as TraitInstance, f as TraitTuple, d as TraitType, e as TraitValue, c as createWorld } from './world-CIrtrsKj.js';
|
|
3
3
|
|
|
4
4
|
declare function defineTrait<S extends Schema = {}>(schema?: S): Trait<Norm<S>>;
|
|
5
5
|
declare const trait: typeof defineTrait;
|
package/dist/index.js
CHANGED
package/dist/react.cjs
CHANGED
|
@@ -582,11 +582,17 @@ Number.prototype.changed = function(trait2) {
|
|
|
582
582
|
return setChanged(world, this, trait2);
|
|
583
583
|
};
|
|
584
584
|
Number.prototype.get = function(trait2) {
|
|
585
|
-
const ctx = trait2[$internal];
|
|
586
|
-
const index = this & ENTITY_ID_MASK;
|
|
587
585
|
const worldId = this >>> WORLD_ID_SHIFT;
|
|
588
|
-
const
|
|
589
|
-
|
|
586
|
+
const world = universe.worlds[worldId];
|
|
587
|
+
const worldCtx = world[$internal];
|
|
588
|
+
const data = worldCtx.traitData.get(trait2);
|
|
589
|
+
if (!data) return void 0;
|
|
590
|
+
const mask = worldCtx.entityMasks[data.generationId][this];
|
|
591
|
+
if ((mask & data.bitflag) !== data.bitflag) return void 0;
|
|
592
|
+
const traitCtx = trait2[$internal];
|
|
593
|
+
const index = this & ENTITY_ID_MASK;
|
|
594
|
+
const store = traitCtx.stores[worldId];
|
|
595
|
+
return traitCtx.get(index, store);
|
|
590
596
|
};
|
|
591
597
|
Number.prototype.set = function(trait2, value, triggerChanged = true) {
|
|
592
598
|
const ctx = trait2[$internal];
|
|
@@ -764,6 +770,7 @@ var Query = class {
|
|
|
764
770
|
__publicField(this, "entities", new SparseSet());
|
|
765
771
|
__publicField(this, "isTracking", false);
|
|
766
772
|
__publicField(this, "hasChangedModifiers", false);
|
|
773
|
+
__publicField(this, "changedTraits", /* @__PURE__ */ new Set());
|
|
767
774
|
__publicField(this, "toRemove", new SparseSet());
|
|
768
775
|
__publicField(this, "addSubscriptions", /* @__PURE__ */ new Set());
|
|
769
776
|
__publicField(this, "removeSubscriptions", /* @__PURE__ */ new Set());
|
|
@@ -817,6 +824,7 @@ var Query = class {
|
|
|
817
824
|
}
|
|
818
825
|
if (parameter.type.includes("changed")) {
|
|
819
826
|
for (const trait2 of traits) {
|
|
827
|
+
this.changedTraits.add(trait2);
|
|
820
828
|
const data = ctx.traitData.get(trait2);
|
|
821
829
|
this.traitData.changed.push(data);
|
|
822
830
|
this.traits.push(trait2);
|
|
@@ -1054,27 +1062,60 @@ function createQueryResult(query, world, params) {
|
|
|
1054
1062
|
const traits = [];
|
|
1055
1063
|
getQueryStores(params, traits, stores, world);
|
|
1056
1064
|
const results = Object.assign(entities, {
|
|
1057
|
-
updateEach(callback, options) {
|
|
1058
|
-
const changeDetection = options?.changeDetection ?? false;
|
|
1065
|
+
updateEach(callback, options = { changeDetection: "auto" }) {
|
|
1059
1066
|
const state = new Array(traits.length);
|
|
1060
|
-
if (
|
|
1067
|
+
if (options.changeDetection === "auto") {
|
|
1068
|
+
const changedPairs = [];
|
|
1069
|
+
const atomicSnapshots = [];
|
|
1070
|
+
const trackedTraits = [];
|
|
1071
|
+
const untrackedTraits = [];
|
|
1072
|
+
for (const trait2 of traits) {
|
|
1073
|
+
if (world[$internal].trackedTraits.has(trait2)) {
|
|
1074
|
+
trackedTraits.push(trait2);
|
|
1075
|
+
} else if (query.hasChangedModifiers && query.changedTraits.has(trait2)) {
|
|
1076
|
+
trackedTraits.push(trait2);
|
|
1077
|
+
} else {
|
|
1078
|
+
untrackedTraits.push(trait2);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1061
1081
|
for (let i = 0; i < entities.length; i++) {
|
|
1062
1082
|
const entity = entities[i];
|
|
1063
1083
|
const eid = getEntityId(entity);
|
|
1064
1084
|
for (let j = 0; j < traits.length; j++) {
|
|
1065
1085
|
const trait2 = traits[j];
|
|
1066
1086
|
const ctx = trait2[$internal];
|
|
1067
|
-
|
|
1087
|
+
const value = ctx.get(eid, stores[j]);
|
|
1088
|
+
state[j] = value;
|
|
1089
|
+
atomicSnapshots[j] = ctx.type === "aos" ? { ...value } : null;
|
|
1068
1090
|
}
|
|
1069
1091
|
callback(state, entity, i);
|
|
1070
1092
|
if (!world.has(entity)) continue;
|
|
1071
|
-
for (let j = 0; j <
|
|
1072
|
-
const trait2 =
|
|
1093
|
+
for (let j = 0; j < trackedTraits.length; j++) {
|
|
1094
|
+
const trait2 = trackedTraits[j];
|
|
1095
|
+
const ctx = trait2[$internal];
|
|
1096
|
+
const newValue = state[j];
|
|
1097
|
+
let changed = false;
|
|
1098
|
+
if (ctx.type === "aos") {
|
|
1099
|
+
changed = ctx.fastSetWithChangeDetection(eid, stores[j], newValue);
|
|
1100
|
+
if (!changed) {
|
|
1101
|
+
changed = !shallowEqual(newValue, atomicSnapshots[j]);
|
|
1102
|
+
}
|
|
1103
|
+
} else {
|
|
1104
|
+
changed = ctx.fastSetWithChangeDetection(eid, stores[j], newValue);
|
|
1105
|
+
}
|
|
1106
|
+
if (changed) changedPairs.push([entity, trait2]);
|
|
1107
|
+
}
|
|
1108
|
+
for (let j = 0; j < untrackedTraits.length; j++) {
|
|
1109
|
+
const trait2 = untrackedTraits[j];
|
|
1073
1110
|
const ctx = trait2[$internal];
|
|
1074
1111
|
ctx.fastSet(eid, stores[j], state[j]);
|
|
1075
1112
|
}
|
|
1076
1113
|
}
|
|
1077
|
-
|
|
1114
|
+
for (let i = 0; i < changedPairs.length; i++) {
|
|
1115
|
+
const [entity, trait2] = changedPairs[i];
|
|
1116
|
+
entity.changed(trait2);
|
|
1117
|
+
}
|
|
1118
|
+
} else if (options.changeDetection === "always") {
|
|
1078
1119
|
const changedPairs = [];
|
|
1079
1120
|
const atomicSnapshots = [];
|
|
1080
1121
|
for (let i = 0; i < entities.length; i++) {
|
|
@@ -1109,6 +1150,23 @@ function createQueryResult(query, world, params) {
|
|
|
1109
1150
|
const [entity, trait2] = changedPairs[i];
|
|
1110
1151
|
entity.changed(trait2);
|
|
1111
1152
|
}
|
|
1153
|
+
} else if (options.changeDetection === "never") {
|
|
1154
|
+
for (let i = 0; i < entities.length; i++) {
|
|
1155
|
+
const entity = entities[i];
|
|
1156
|
+
const eid = getEntityId(entity);
|
|
1157
|
+
for (let j = 0; j < traits.length; j++) {
|
|
1158
|
+
const trait2 = traits[j];
|
|
1159
|
+
const ctx = trait2[$internal];
|
|
1160
|
+
state[j] = ctx.get(eid, stores[j]);
|
|
1161
|
+
}
|
|
1162
|
+
callback(state, entity, i);
|
|
1163
|
+
if (!world.has(entity)) continue;
|
|
1164
|
+
for (let j = 0; j < traits.length; j++) {
|
|
1165
|
+
const trait2 = traits[j];
|
|
1166
|
+
const ctx = trait2[$internal];
|
|
1167
|
+
ctx.fastSet(eid, stores[j], state[j]);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1112
1170
|
}
|
|
1113
1171
|
return results;
|
|
1114
1172
|
},
|
|
@@ -1164,7 +1222,8 @@ var World = class {
|
|
|
1164
1222
|
dirtyMasks: /* @__PURE__ */ new Map(),
|
|
1165
1223
|
trackingSnapshots: /* @__PURE__ */ new Map(),
|
|
1166
1224
|
changedMasks: /* @__PURE__ */ new Map(),
|
|
1167
|
-
worldEntity: null
|
|
1225
|
+
worldEntity: null,
|
|
1226
|
+
trackedTraits: /* @__PURE__ */ new Set()
|
|
1168
1227
|
});
|
|
1169
1228
|
__privateAdd(this, _isInitialized, false);
|
|
1170
1229
|
__publicField(this, "traits", /* @__PURE__ */ new Set());
|
|
@@ -1238,6 +1297,7 @@ var World = class {
|
|
|
1238
1297
|
ctx.trackingSnapshots.clear();
|
|
1239
1298
|
ctx.dirtyMasks.clear();
|
|
1240
1299
|
ctx.changedMasks.clear();
|
|
1300
|
+
ctx.trackedTraits.clear();
|
|
1241
1301
|
ctx.worldEntity = createEntity(this, IsExcluded);
|
|
1242
1302
|
}
|
|
1243
1303
|
query(...args) {
|
|
@@ -1287,7 +1347,11 @@ var World = class {
|
|
|
1287
1347
|
if (!ctx.traitData.has(trait2)) registerTrait(this, trait2);
|
|
1288
1348
|
const data = ctx.traitData.get(trait2);
|
|
1289
1349
|
data.changedSubscriptions.add(callback);
|
|
1290
|
-
|
|
1350
|
+
ctx.trackedTraits.add(trait2);
|
|
1351
|
+
return () => {
|
|
1352
|
+
data.changedSubscriptions.delete(callback);
|
|
1353
|
+
ctx.trackedTraits.delete(trait2);
|
|
1354
|
+
};
|
|
1291
1355
|
}
|
|
1292
1356
|
};
|
|
1293
1357
|
_id = new WeakMap();
|
package/dist/react.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Q as QueryParameter, p as QueryResult, W as World, T as Trait, l as Entity, g as TraitInstance } from './world-
|
|
1
|
+
import { Q as QueryParameter, p as QueryResult, W as World, T as Trait, l as Entity, g as TraitInstance } from './world-CIrtrsKj.cjs';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
declare function useQuery<T extends QueryParameter[]>(...parameters: T): QueryResult<T>;
|