@forgeax/engine-ecs 0.1.23 → 0.1.25
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 +42 -1
- package/dist/buffer-pool.d.ts +4 -5
- package/dist/buffer-pool.d.ts.map +1 -1
- package/dist/component.d.ts +9 -6
- package/dist/component.d.ts.map +1 -1
- package/dist/errors/query-and-component-errors.d.ts +1 -1
- package/dist/errors/query-and-component-errors.d.ts.map +1 -1
- package/dist/externalization/index.mjs.map +1 -1
- package/dist/index.mjs +597 -152
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.ts +3 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +6 -1
- package/dist/internal.mjs.map +1 -1
- package/dist/projection/index.mjs.map +1 -1
- package/dist/query/derived-range-writer.d.ts +14 -0
- package/dist/query/derived-range-writer.d.ts.map +1 -1
- package/dist/query/query.d.ts.map +1 -1
- package/dist/shared.mjs.map +1 -1
- package/dist/world-component-access.d.ts +63 -18
- package/dist/world-component-access.d.ts.map +1 -1
- package/dist/world-component-storage.d.ts +26 -10
- package/dist/world-component-storage.d.ts.map +1 -1
- package/dist/world-entity-lifecycle.d.ts.map +1 -1
- package/dist/world-internal.d.ts +1 -1
- package/dist/world-internal.d.ts.map +1 -1
- package/dist/world.d.ts +5 -0
- package/dist/world.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/archetype.unit.test.ts +38 -10
- package/src/__tests__/relationship-index.test.ts +147 -1
- package/src/buffer-pool.ts +47 -74
- package/src/component.ts +9 -6
- package/src/errors/query-and-component-errors.ts +4 -1
- package/src/internal.ts +3 -0
- package/src/query/derived-range-writer.ts +111 -0
- package/src/query/query.ts +26 -1
- package/src/world-component-access.ts +427 -127
- package/src/world-component-storage.ts +104 -18
- package/src/world-entity-lifecycle.ts +49 -9
- package/src/world-internal.ts +5 -0
- package/src/world.ts +52 -3
package/dist/index.mjs
CHANGED
|
@@ -2046,6 +2046,7 @@ function createDerivedRangeWriter(world, component, source) {
|
|
|
2046
2046
|
let boundEpoch = -1;
|
|
2047
2047
|
let bindingTables = [];
|
|
2048
2048
|
let bindings = [];
|
|
2049
|
+
let bindingByTable = /* @__PURE__ */ new Map();
|
|
2049
2050
|
let runStartBuffers = [];
|
|
2050
2051
|
let runCountBuffers = [];
|
|
2051
2052
|
const rebind = () => {
|
|
@@ -2055,6 +2056,8 @@ function createDerivedRangeWriter(world, component, source) {
|
|
|
2055
2056
|
const nextRunCountBuffers = [];
|
|
2056
2057
|
for (const table of tables) {
|
|
2057
2058
|
nextBindings.push({
|
|
2059
|
+
tableId: table.id,
|
|
2060
|
+
entities: table.storage.get(componentId(Entity))?.fields.get("self")?.view ?? new Uint32Array(0),
|
|
2058
2061
|
read: buildWholeColumnShape(table, source.readComponents),
|
|
2059
2062
|
write: buildWholeColumnShape(table, source.writeComponents),
|
|
2060
2063
|
rowCapacity: table.size
|
|
@@ -2065,6 +2068,11 @@ function createDerivedRangeWriter(world, component, source) {
|
|
|
2065
2068
|
}
|
|
2066
2069
|
bindingTables = tables;
|
|
2067
2070
|
bindings = nextBindings;
|
|
2071
|
+
bindingByTable = /* @__PURE__ */ new Map();
|
|
2072
|
+
for (let index = 0; index < nextBindings.length; index += 1) {
|
|
2073
|
+
const binding = nextBindings[index];
|
|
2074
|
+
if (binding !== void 0) bindingByTable.set(binding.tableId, index);
|
|
2075
|
+
}
|
|
2068
2076
|
runStartBuffers = nextRunStartBuffers;
|
|
2069
2077
|
runCountBuffers = nextRunCountBuffers;
|
|
2070
2078
|
boundEpoch = source.structureEpoch();
|
|
@@ -2075,6 +2083,82 @@ function createDerivedRangeWriter(world, component, source) {
|
|
|
2075
2083
|
if (boundEpoch !== source.structureEpoch()) rebind();
|
|
2076
2084
|
return bindings;
|
|
2077
2085
|
},
|
|
2086
|
+
locateEntity(entity, cursor) {
|
|
2087
|
+
if (world.execution.health === "poisoned") return false;
|
|
2088
|
+
if (boundEpoch !== source.structureEpoch()) rebind();
|
|
2089
|
+
const archetype = world[worldInternal].getEntityArchetype(entity);
|
|
2090
|
+
if (archetype === void 0) return false;
|
|
2091
|
+
const bindingIndex = bindingByTable.get(archetype.tableId);
|
|
2092
|
+
if (bindingIndex === void 0) return false;
|
|
2093
|
+
const record = world[worldInternal].getRecords()[entity & 16777215];
|
|
2094
|
+
if (record === void 0 || record.archetypeId !== archetype.id) return false;
|
|
2095
|
+
const row = archetype.rows[record.archetypeRow];
|
|
2096
|
+
const binding = bindings[bindingIndex];
|
|
2097
|
+
if (binding === void 0 || row === void 0 || row < 0 || row >= binding.rowCapacity) {
|
|
2098
|
+
return false;
|
|
2099
|
+
}
|
|
2100
|
+
cursor.bindingIndex = bindingIndex;
|
|
2101
|
+
cursor.row = row;
|
|
2102
|
+
return true;
|
|
2103
|
+
},
|
|
2104
|
+
publishChangedRows(bindingIndex, changed) {
|
|
2105
|
+
if (world.execution.health === "poisoned") {
|
|
2106
|
+
return err(new WorldPoisonedError(world.identity, world.execution.fault));
|
|
2107
|
+
}
|
|
2108
|
+
if (boundEpoch !== source.structureEpoch()) rebind();
|
|
2109
|
+
const binding = bindings[bindingIndex];
|
|
2110
|
+
const table = bindingTables[bindingIndex];
|
|
2111
|
+
const runStarts = runStartBuffers[bindingIndex];
|
|
2112
|
+
const runCounts = runCountBuffers[bindingIndex];
|
|
2113
|
+
if (binding === void 0 || table === void 0 || runStarts === void 0 || runCounts === void 0 || !Number.isSafeInteger(bindingIndex) || bindingIndex < 0 || changed.length < binding.rowCapacity || table.storage.get(componentId(component)) === void 0) {
|
|
2114
|
+
return err(new DerivedRangeOutOfBoundsError(0, changed.length, binding?.rowCapacity ?? 0));
|
|
2115
|
+
}
|
|
2116
|
+
let runCount = 0;
|
|
2117
|
+
let runStart = -1;
|
|
2118
|
+
for (let row = 0; row < binding.rowCapacity; row += 1) {
|
|
2119
|
+
if ((changed[row] ?? 0) !== 0) {
|
|
2120
|
+
if (runStart < 0) runStart = row;
|
|
2121
|
+
} else if (runStart >= 0) {
|
|
2122
|
+
runStarts[runCount] = runStart;
|
|
2123
|
+
runCounts[runCount] = row - runStart;
|
|
2124
|
+
runCount += 1;
|
|
2125
|
+
runStart = -1;
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
if (runStart >= 0) {
|
|
2129
|
+
runStarts[runCount] = runStart;
|
|
2130
|
+
runCounts[runCount] = binding.rowCapacity - runStart;
|
|
2131
|
+
runCount += 1;
|
|
2132
|
+
}
|
|
2133
|
+
if (runCount === 0) return ok(void 0);
|
|
2134
|
+
const previousEpoch = world[worldInternal].getMutationEpoch();
|
|
2135
|
+
let epoch;
|
|
2136
|
+
try {
|
|
2137
|
+
epoch = world[worldInternal].nextMutationEpoch();
|
|
2138
|
+
const componentIdentifier = componentId(component);
|
|
2139
|
+
for (let index = 0; index < runCount; index += 1) {
|
|
2140
|
+
world[worldInternal].publishDerivedRange(
|
|
2141
|
+
table,
|
|
2142
|
+
componentIdentifier,
|
|
2143
|
+
runStarts[index] ?? 0,
|
|
2144
|
+
runCounts[index] ?? 0,
|
|
2145
|
+
epoch
|
|
2146
|
+
);
|
|
2147
|
+
}
|
|
2148
|
+
changed.fill(0, 0, binding.rowCapacity);
|
|
2149
|
+
return ok(void 0);
|
|
2150
|
+
} catch (cause) {
|
|
2151
|
+
world[worldInternal].restoreMutationEpoch(previousEpoch);
|
|
2152
|
+
world[worldInternal].poisonExecution({
|
|
2153
|
+
code: "shared-kernel-failed",
|
|
2154
|
+
kernelName: `derived-range:${component.name}:publish`,
|
|
2155
|
+
cause,
|
|
2156
|
+
partialWrite: true,
|
|
2157
|
+
retryable: false
|
|
2158
|
+
});
|
|
2159
|
+
return err(new SharedKernelFailureError(component.name, world.identity, cause, true));
|
|
2160
|
+
}
|
|
2161
|
+
},
|
|
2078
2162
|
writeRange(bindingIndex, base, start, count, kernel, context) {
|
|
2079
2163
|
if (world.execution.health === "poisoned") {
|
|
2080
2164
|
return err(new WorldPoisonedError(world.identity, world.execution.fault));
|
|
@@ -2288,7 +2372,13 @@ var QueryRowFacade = class _QueryRowFacade {
|
|
|
2288
2372
|
mut(component) {
|
|
2289
2373
|
const current = this.get(component);
|
|
2290
2374
|
if (current === void 0) throw new Error(`Query row lacks ${component.name}.`);
|
|
2291
|
-
|
|
2375
|
+
if (isRelationshipTarget(component)) {
|
|
2376
|
+
throw new RelationshipTargetReadonlyError(component.name, "query row");
|
|
2377
|
+
}
|
|
2378
|
+
const relationshipSource2 = relationshipRole(component)?.kind === "source";
|
|
2379
|
+
if (!relationshipSource2) {
|
|
2380
|
+
this.world[worldInternal].markComponentChanged(this.entity, componentId(component));
|
|
2381
|
+
}
|
|
2292
2382
|
return new Proxy(current, {
|
|
2293
2383
|
set: (target, property, value) => {
|
|
2294
2384
|
if (typeof property !== "string") return false;
|
|
@@ -2325,6 +2415,9 @@ var QuerySpanFacade = class {
|
|
|
2325
2415
|
);
|
|
2326
2416
|
}
|
|
2327
2417
|
mut(component) {
|
|
2418
|
+
if (relationshipRole(component) !== void 0) {
|
|
2419
|
+
throw new RelationshipTargetReadonlyError(component.name, "query span");
|
|
2420
|
+
}
|
|
2328
2421
|
this.world[worldInternal].markComponentRangeChanged(
|
|
2329
2422
|
this.table,
|
|
2330
2423
|
componentId(component),
|
|
@@ -2650,6 +2743,9 @@ var ExecutableQuery = class {
|
|
|
2650
2743
|
spanUnavailableReason() {
|
|
2651
2744
|
if (this.compiled.sparseRoute) return "sparse-component";
|
|
2652
2745
|
if ((this.compiled.descriptor.optional?.length ?? 0) > 0) return "optional-data";
|
|
2746
|
+
if ((this.compiled.descriptor.write ?? []).some((component) => relationshipRole(component))) {
|
|
2747
|
+
return "relationship-component";
|
|
2748
|
+
}
|
|
2653
2749
|
return void 0;
|
|
2654
2750
|
}
|
|
2655
2751
|
};
|
|
@@ -3637,6 +3733,52 @@ var ComponentStorage = class {
|
|
|
3637
3733
|
const elementCount = countCol?.view[row] ?? 0;
|
|
3638
3734
|
return reinterpretSlotBytes(liveBytes, arrayMeta.elementType, elementCount);
|
|
3639
3735
|
}
|
|
3736
|
+
/**
|
|
3737
|
+
* Read the live length of an array field without materialising a typed view.
|
|
3738
|
+
* Relationship consumers use this narrow storage seam for hot reverse-list
|
|
3739
|
+
* walks; the target array remains owned by the ECS column and no snapshot is
|
|
3740
|
+
* created per entity.
|
|
3741
|
+
*/
|
|
3742
|
+
readArrayLength(arch, component, row, fieldName) {
|
|
3743
|
+
const fieldCols = this.table(arch).storage.get(componentId(component))?.fields;
|
|
3744
|
+
if (fieldCols === void 0) return void 0;
|
|
3745
|
+
const arrayMeta = componentDefinition(component).fields[fieldName]?.arrayMeta;
|
|
3746
|
+
if (arrayMeta === void 0) return void 0;
|
|
3747
|
+
if (arrayMeta.length !== void 0) return arrayMeta.length;
|
|
3748
|
+
const count = fieldCols.get(arrayCountColumnName(fieldName))?.view[row];
|
|
3749
|
+
return typeof count === "number" ? count : 0;
|
|
3750
|
+
}
|
|
3751
|
+
/**
|
|
3752
|
+
* Read one array element directly from its backing column/BufferPool slot.
|
|
3753
|
+
* The relationship target vocabulary is `array<entity>`, so its hot path
|
|
3754
|
+
* decodes the packed u32 in-place and does not allocate a TypedArray view.
|
|
3755
|
+
*/
|
|
3756
|
+
readArrayElement(arch, component, row, fieldName, index) {
|
|
3757
|
+
if (!Number.isSafeInteger(index) || index < 0) return void 0;
|
|
3758
|
+
const fieldCols = this.table(arch).storage.get(componentId(component))?.fields;
|
|
3759
|
+
if (fieldCols === void 0) return void 0;
|
|
3760
|
+
const arrayMeta = componentDefinition(component).fields[fieldName]?.arrayMeta;
|
|
3761
|
+
if (arrayMeta === void 0) return void 0;
|
|
3762
|
+
const length = this.readArrayLength(arch, component, row, fieldName);
|
|
3763
|
+
if (length === void 0 || index >= length) return void 0;
|
|
3764
|
+
const col = fieldCols.get(fieldName);
|
|
3765
|
+
if (col === void 0) return void 0;
|
|
3766
|
+
if (arrayMeta.length !== void 0) {
|
|
3767
|
+
return col.view[row * col.arity + index];
|
|
3768
|
+
}
|
|
3769
|
+
const slotId = col.view[row];
|
|
3770
|
+
const bytes = this.bufferPool.view(slotId);
|
|
3771
|
+
if (arrayMeta.elementType === "entity") {
|
|
3772
|
+
const byteOffset = index * 4;
|
|
3773
|
+
if (byteOffset + 4 > bytes.byteLength) return void 0;
|
|
3774
|
+
return ((bytes[byteOffset] ?? 0) | (bytes[byteOffset + 1] ?? 0) << 8 | (bytes[byteOffset + 2] ?? 0) << 16 | (bytes[byteOffset + 3] ?? 0) << 24) >>> 0;
|
|
3775
|
+
}
|
|
3776
|
+
return this.readArrayElementAt(bytes, index, arrayMeta.elementType);
|
|
3777
|
+
}
|
|
3778
|
+
/** Read one scalar field without constructing a component shape. */
|
|
3779
|
+
readFieldValue(arch, component, row, fieldName) {
|
|
3780
|
+
return this.table(arch).storage.get(componentId(component))?.fields.get(fieldName)?.view[row];
|
|
3781
|
+
}
|
|
3640
3782
|
// ──────────────────────────────────────────────────────────────────────────
|
|
3641
3783
|
// Internal — archetype data read/write
|
|
3642
3784
|
// ──────────────────────────────────────────────────────────────────────────
|
|
@@ -3701,7 +3843,7 @@ var ComponentStorage = class {
|
|
|
3701
3843
|
if (!col) return;
|
|
3702
3844
|
col.view[row] = handle;
|
|
3703
3845
|
}
|
|
3704
|
-
writeRow(arch, component, row, value) {
|
|
3846
|
+
writeRow(arch, component, row, value, options) {
|
|
3705
3847
|
const localId = componentId(component);
|
|
3706
3848
|
const fieldCols = this.table(arch).storage.get(localId)?.fields;
|
|
3707
3849
|
if (!fieldCols) {
|
|
@@ -3750,6 +3892,12 @@ var ComponentStorage = class {
|
|
|
3750
3892
|
} else {
|
|
3751
3893
|
const arrayMeta = componentDefinition(component).fields[fieldName]?.arrayMeta;
|
|
3752
3894
|
if (arrayMeta !== void 0) {
|
|
3895
|
+
if (options?.skipVariableArrayInitialization === true && arrayMeta.length === void 0) {
|
|
3896
|
+
col.view[row] = 0;
|
|
3897
|
+
const countCol = fieldCols.get(arrayCountColumnName(fieldName));
|
|
3898
|
+
if (countCol !== void 0) countCol.view[row] = 0;
|
|
3899
|
+
continue;
|
|
3900
|
+
}
|
|
3753
3901
|
this.writeArrayField(arch, component, row, fieldName, fieldType, arrayMeta, raw);
|
|
3754
3902
|
} else {
|
|
3755
3903
|
col.view[row] = raw;
|
|
@@ -4125,20 +4273,19 @@ var ComponentStorage = class {
|
|
|
4125
4273
|
return readArrayElementAt(bytes, idx, elementType);
|
|
4126
4274
|
}
|
|
4127
4275
|
/**
|
|
4128
|
-
* Materialise a fresh `TypedArray`
|
|
4129
|
-
*
|
|
4130
|
-
*
|
|
4131
|
-
*
|
|
4276
|
+
* Materialise a fresh `TypedArray` view for an `array<T,N>` / `array<T>`
|
|
4277
|
+
* field at `row`. This is the internal row-storage path: the returned view
|
|
4278
|
+
* aliases the live column or BufferPool slot. `WorldComponentAccess.get`
|
|
4279
|
+
* detaches relationship target arrays at the public boundary; internal ECS
|
|
4280
|
+
* relationship maintenance and Scene traversal keep this zero-copy path.
|
|
4132
4281
|
*
|
|
4133
4282
|
* **Transient view contract (feat-20260602):** for fixed `array<T,N>`
|
|
4134
4283
|
* columns the returned `TypedArray` aliases the inline column buffer
|
|
4135
4284
|
* directly (`col.view.subarray(row * arity, ...)`); for variable
|
|
4136
4285
|
* `array<T>` columns it aliases the live `BufferPool` slot bytes
|
|
4137
|
-
* (zero-copy; `pool.view(slotId)` is the SSOT byte region). In both
|
|
4138
|
-
*
|
|
4139
|
-
*
|
|
4140
|
-
* and the implementation may switch to a copy in the future without
|
|
4141
|
-
* breaking AI users who consume only `length` / index reads.
|
|
4286
|
+
* (zero-copy; `pool.view(slotId)` is the SSOT byte region). In both cases
|
|
4287
|
+
* the view is valid only until the next structural change. Internal callers
|
|
4288
|
+
* must not write through it except via the owner mutation helpers.
|
|
4142
4289
|
*
|
|
4143
4290
|
* For variable arrays the typed-array length matches the live count from
|
|
4144
4291
|
* the sidecar `<fieldName>:count` column; for fixed arrays it matches the
|
|
@@ -4397,6 +4544,9 @@ function relationshipPayloadWrites(data) {
|
|
|
4397
4544
|
return true;
|
|
4398
4545
|
});
|
|
4399
4546
|
}
|
|
4547
|
+
function detachRelationshipTargetArray(value) {
|
|
4548
|
+
return value instanceof Uint32Array ? value.slice() : value;
|
|
4549
|
+
}
|
|
4400
4550
|
var WorldComponentAccess = class {
|
|
4401
4551
|
constructor(state) {
|
|
4402
4552
|
this.state = state;
|
|
@@ -4437,16 +4587,19 @@ var WorldComponentAccess = class {
|
|
|
4437
4587
|
}
|
|
4438
4588
|
return index;
|
|
4439
4589
|
}
|
|
4440
|
-
/**
|
|
4590
|
+
/**
|
|
4591
|
+
* Read the World-owned materialized target array without a public snapshot.
|
|
4592
|
+
* This is an internal relationship-owner path: it borrows the live
|
|
4593
|
+
* `Uint32Array` so attach/detach stays zero-copy. Public `World.get` detaches
|
|
4594
|
+
* target arrays before returning them to callers.
|
|
4595
|
+
*/
|
|
4441
4596
|
relationshipTargetEntries(source, target) {
|
|
4442
4597
|
const role = relationshipRole(source);
|
|
4443
4598
|
if (role?.kind !== "source") return [];
|
|
4444
4599
|
const mirror = relationshipMirror(source);
|
|
4445
4600
|
if (mirror === void 0) return [];
|
|
4446
|
-
const
|
|
4447
|
-
|
|
4448
|
-
const entries = result.value[role.targetField];
|
|
4449
|
-
return entries !== void 0 && typeof entries === "object" ? entries : [];
|
|
4601
|
+
const entries = this._getArrayView(target, mirror, role.targetField);
|
|
4602
|
+
return entries === void 0 ? [] : entries;
|
|
4450
4603
|
}
|
|
4451
4604
|
markComponentAdded(entity, component) {
|
|
4452
4605
|
this.state.markComponentAdded(entity, componentId(component));
|
|
@@ -4526,8 +4679,7 @@ var WorldComponentAccess = class {
|
|
|
4526
4679
|
const targetRecord = this.records[entityIndex(target)];
|
|
4527
4680
|
const actualGeneration = targetRecord?.generation ?? -1;
|
|
4528
4681
|
const targetLive = this.recordIsLive(targetRecord, entityGeneration(target));
|
|
4529
|
-
|
|
4530
|
-
if (!targetIsPending && !targetLive && !holderIsPending) {
|
|
4682
|
+
if (!targetIsPending && !targetLive) {
|
|
4531
4683
|
return err(
|
|
4532
4684
|
new StaleEntityError(target, entityIndex(target), entityGeneration(target), {
|
|
4533
4685
|
operation: "relationship-insert",
|
|
@@ -4584,7 +4736,14 @@ var WorldComponentAccess = class {
|
|
|
4584
4736
|
current = next;
|
|
4585
4737
|
}
|
|
4586
4738
|
}
|
|
4587
|
-
/**
|
|
4739
|
+
/**
|
|
4740
|
+
* Reserve target-side relationship capacity without touching World columns.
|
|
4741
|
+
*
|
|
4742
|
+
* A missing mirror reserves its first slot before the mirror archetype is
|
|
4743
|
+
* created. An existing mirror either reserves an empty slot or grows its
|
|
4744
|
+
* existing BufferPool slot; both failure paths return before any component,
|
|
4745
|
+
* relationship index, or ECS epoch changes.
|
|
4746
|
+
*/
|
|
4588
4747
|
prepareRelationshipInsert(component, value) {
|
|
4589
4748
|
const role = relationshipRole(component);
|
|
4590
4749
|
if (role?.kind !== "source") return ok(void 0);
|
|
@@ -4605,34 +4764,113 @@ var WorldComponentAccess = class {
|
|
|
4605
4764
|
);
|
|
4606
4765
|
}
|
|
4607
4766
|
const targetArch = this.graph.archetypes[targetRec.archetypeId];
|
|
4608
|
-
const
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4767
|
+
const mirrorLocalId = componentId(mirror);
|
|
4768
|
+
const mirrorPresent = targetArch?.components.some((candidate) => componentId(candidate) === mirrorLocalId) ?? false;
|
|
4769
|
+
const fieldName = role.targetField;
|
|
4770
|
+
const arrayMeta = componentDefinition(mirror).fields[fieldName]?.arrayMeta;
|
|
4771
|
+
if (arrayMeta === void 0) {
|
|
4772
|
+
return err(new ComponentNotPresentError(target, mirror.name));
|
|
4773
|
+
}
|
|
4774
|
+
const meta = TYPE_METADATA[arrayMeta.elementType];
|
|
4775
|
+
if (meta?.byteSize === void 0) {
|
|
4776
|
+
return err(new ComponentNotPresentError(target, mirror.name));
|
|
4777
|
+
}
|
|
4778
|
+
let currentLength = 0;
|
|
4779
|
+
let slotId = 0;
|
|
4780
|
+
if (mirrorPresent) {
|
|
4781
|
+
currentLength = this._getArrayLength(target, mirror, fieldName) ?? 0;
|
|
4782
|
+
const fieldCols = this.table(targetArch).storage.get(mirrorLocalId)?.fields;
|
|
4783
|
+
const column = fieldCols?.get(fieldName);
|
|
4784
|
+
if (column === void 0) {
|
|
4785
|
+
return err(new ComponentNotPresentError(target, mirror.name));
|
|
4786
|
+
}
|
|
4787
|
+
const row = this.tableRow(targetRec);
|
|
4788
|
+
slotId = column.view[row];
|
|
4789
|
+
}
|
|
4790
|
+
const requiredBytes = (currentLength + 1) * meta.byteSize;
|
|
4791
|
+
if (!Number.isSafeInteger(requiredBytes)) {
|
|
4792
|
+
return err(new ManagedBufferOutOfBoundsError(requiredBytes, Number.MAX_SAFE_INTEGER));
|
|
4793
|
+
}
|
|
4794
|
+
const preparation = {
|
|
4795
|
+
target,
|
|
4796
|
+
mirror,
|
|
4797
|
+
fieldName,
|
|
4798
|
+
mirrorPresent,
|
|
4799
|
+
reservedSlotId: void 0
|
|
4800
|
+
};
|
|
4801
|
+
if (!mirrorPresent || slotId === 0) {
|
|
4802
|
+
const allocated = this.bufferPool.alloc(requiredBytes);
|
|
4803
|
+
if (!allocated.ok) return allocated;
|
|
4804
|
+
preparation.reservedSlotId = allocated.value.id;
|
|
4805
|
+
} else if (this.bufferPool.view(slotId).byteLength < requiredBytes) {
|
|
4806
|
+
const grown = this.bufferPool.grow(slotId, requiredBytes);
|
|
4807
|
+
if (!grown.ok) return grown;
|
|
4808
|
+
}
|
|
4809
|
+
return ok(preparation);
|
|
4810
|
+
}
|
|
4811
|
+
/** Release a target-capacity reservation that did not reach commit. */
|
|
4812
|
+
releaseRelationshipPreparation(preparation) {
|
|
4813
|
+
if (preparation === void 0 || preparation.reservedSlotId === void 0) return;
|
|
4814
|
+
const slotId = preparation.reservedSlotId;
|
|
4815
|
+
this.bufferPool.release(slotId);
|
|
4816
|
+
preparation.reservedSlotId = void 0;
|
|
4817
|
+
}
|
|
4818
|
+
/** Install a reserved slot after the target mirror archetype exists. */
|
|
4819
|
+
installRelationshipPreparation(preparation) {
|
|
4820
|
+
const record = this.lookupAlive(
|
|
4821
|
+
preparation.target,
|
|
4822
|
+
"relationship-capacity",
|
|
4823
|
+
preparation.mirror.name
|
|
4824
|
+
);
|
|
4825
|
+
if (!record.ok) return record;
|
|
4826
|
+
const arch = this.graph.archetypes[record.value.archetypeId];
|
|
4827
|
+
if (arch === void 0) {
|
|
4828
|
+
return err(
|
|
4829
|
+
new ComponentNotPresentError(preparation.target, preparation.mirror.name)
|
|
4830
|
+
);
|
|
4831
|
+
}
|
|
4832
|
+
const fieldCols = this.table(arch).storage.get(componentId(preparation.mirror))?.fields;
|
|
4833
|
+
const column = fieldCols?.get(preparation.fieldName);
|
|
4834
|
+
if (column === void 0) {
|
|
4835
|
+
return err(
|
|
4836
|
+
new ComponentNotPresentError(preparation.target, preparation.mirror.name)
|
|
4614
4837
|
);
|
|
4615
|
-
if (!added.ok) return added;
|
|
4616
4838
|
}
|
|
4617
|
-
const
|
|
4618
|
-
|
|
4839
|
+
const slotId = preparation.reservedSlotId;
|
|
4840
|
+
if (slotId !== void 0) {
|
|
4841
|
+
column.view[this.tableRow(record.value)] = slotId;
|
|
4842
|
+
preparation.reservedSlotId = void 0;
|
|
4843
|
+
}
|
|
4844
|
+
return ok(void 0);
|
|
4619
4845
|
}
|
|
4620
4846
|
/** Append `holder` to the materialized target list. */
|
|
4621
|
-
relationshipOnInsert(holder, component, value) {
|
|
4847
|
+
relationshipOnInsert(holder, component, value, preparation) {
|
|
4622
4848
|
const role = relationshipRole(component);
|
|
4623
4849
|
if (role?.kind !== "source") return ok(void 0);
|
|
4624
4850
|
const target = this.relationshipTargetEntity(component, value);
|
|
4625
4851
|
if (target === null) return ok(void 0);
|
|
4626
4852
|
const mirror = relationshipMirror(component);
|
|
4627
4853
|
if (mirror === void 0) return ok(void 0);
|
|
4628
|
-
|
|
4629
|
-
if (
|
|
4630
|
-
|
|
4631
|
-
return
|
|
4854
|
+
let prepared = preparation;
|
|
4855
|
+
if (prepared === void 0) {
|
|
4856
|
+
const preparedResult = this.prepareRelationshipInsert(component, value);
|
|
4857
|
+
if (!preparedResult.ok) return preparedResult;
|
|
4858
|
+
prepared = preparedResult.value;
|
|
4632
4859
|
}
|
|
4860
|
+
if (prepared === void 0) return ok(void 0);
|
|
4633
4861
|
const targetSlot = entityIndex(target);
|
|
4634
4862
|
const targetRec = this.records[targetSlot];
|
|
4635
|
-
|
|
4863
|
+
const actualGeneration = targetRec?.generation ?? -1;
|
|
4864
|
+
if (!this.recordIsLive(targetRec, entityGeneration(target))) {
|
|
4865
|
+
return err(
|
|
4866
|
+
new StaleEntityError(target, targetSlot, entityGeneration(target), {
|
|
4867
|
+
operation: "relationship-insert",
|
|
4868
|
+
component: component.name,
|
|
4869
|
+
expectedGeneration: entityGeneration(target),
|
|
4870
|
+
actualGeneration
|
|
4871
|
+
})
|
|
4872
|
+
);
|
|
4873
|
+
}
|
|
4636
4874
|
const targetArch = this.graph.archetypes[targetRec.archetypeId];
|
|
4637
4875
|
const mirrorLocalId = componentId(mirror);
|
|
4638
4876
|
const hasMirror = targetArch?.components.some((component2) => componentId(component2) === mirrorLocalId) ?? false;
|
|
@@ -4643,9 +4881,19 @@ var WorldComponentAccess = class {
|
|
|
4643
4881
|
component: mirror,
|
|
4644
4882
|
data: {}
|
|
4645
4883
|
},
|
|
4884
|
+
true,
|
|
4885
|
+
false,
|
|
4646
4886
|
true
|
|
4647
4887
|
);
|
|
4648
|
-
if (!added.ok)
|
|
4888
|
+
if (!added.ok) {
|
|
4889
|
+
this.releaseRelationshipPreparation(prepared);
|
|
4890
|
+
return added;
|
|
4891
|
+
}
|
|
4892
|
+
}
|
|
4893
|
+
const installed = this.installRelationshipPreparation(prepared);
|
|
4894
|
+
if (!installed.ok) {
|
|
4895
|
+
this.releaseRelationshipPreparation(prepared);
|
|
4896
|
+
return installed;
|
|
4649
4897
|
}
|
|
4650
4898
|
const targetEntries = this.relationshipTargetEntries(component, target);
|
|
4651
4899
|
const slot = targetEntries.length;
|
|
@@ -4655,7 +4903,10 @@ var WorldComponentAccess = class {
|
|
|
4655
4903
|
role.targetField,
|
|
4656
4904
|
holder
|
|
4657
4905
|
);
|
|
4658
|
-
if (!mirrored.ok)
|
|
4906
|
+
if (!mirrored.ok) {
|
|
4907
|
+
this.releaseRelationshipPreparation(prepared);
|
|
4908
|
+
return mirrored;
|
|
4909
|
+
}
|
|
4659
4910
|
this.relationshipIndex(component)?.attach(holder, target, slot);
|
|
4660
4911
|
return ok(void 0);
|
|
4661
4912
|
}
|
|
@@ -4688,15 +4939,16 @@ var WorldComponentAccess = class {
|
|
|
4688
4939
|
/**
|
|
4689
4940
|
* Read component data from an entity.
|
|
4690
4941
|
*
|
|
4691
|
-
* **
|
|
4692
|
-
* `
|
|
4693
|
-
*
|
|
4694
|
-
*
|
|
4695
|
-
* `
|
|
4696
|
-
*
|
|
4697
|
-
*
|
|
4698
|
-
*
|
|
4699
|
-
* `
|
|
4942
|
+
* **Public array contract:** relationship target `array<entity>` fields are
|
|
4943
|
+
* detached `Uint32Array` copies. Mutating that returned array cannot alter
|
|
4944
|
+
* the materialized target, relationship index, or source. Other array
|
|
4945
|
+
* fields retain the existing transient view contract: fixed-capacity
|
|
4946
|
+
* `array<T,N>` and `buffer<N>` fields alias the archetype column buffer
|
|
4947
|
+
* directly, while variable managed arrays alias their BufferPool slot. Those
|
|
4948
|
+
* views are valid only until the next structural change (`spawn` /
|
|
4949
|
+
* `despawn` / `addComponent` / `removeComponent`); callers must re-fetch
|
|
4950
|
+
* `world.get(e, C)` on every access. Internal owners use `readRow` and
|
|
4951
|
+
* `_getArrayView` directly and retain zero-copy access.
|
|
4700
4952
|
*
|
|
4701
4953
|
* @returns `Result<ShapeOf<S>, EcsError>` —
|
|
4702
4954
|
* `ok(ShapeOf<S>)` on success;
|
|
@@ -4736,7 +4988,13 @@ var WorldComponentAccess = class {
|
|
|
4736
4988
|
if (!arch.components.some((candidate) => componentId(candidate) === localId)) {
|
|
4737
4989
|
return err(new ComponentNotPresentError(entity, component.name));
|
|
4738
4990
|
}
|
|
4739
|
-
|
|
4991
|
+
const value = this.storage.readRow(arch, component, this.tableRow(rec));
|
|
4992
|
+
const role = relationshipRole(component);
|
|
4993
|
+
if (role?.kind === "target") {
|
|
4994
|
+
const targetValue = value;
|
|
4995
|
+
targetValue[role.targetField] = detachRelationshipTargetArray(targetValue[role.targetField]);
|
|
4996
|
+
}
|
|
4997
|
+
return ok(value);
|
|
4740
4998
|
}
|
|
4741
4999
|
/**
|
|
4742
5000
|
* Column-level zero-copy view of an `array<T, N>` / `array<T>` field.
|
|
@@ -4770,9 +5028,10 @@ var WorldComponentAccess = class {
|
|
|
4770
5028
|
* Returns `undefined` when the entity is dead, the component is absent, the
|
|
4771
5029
|
* field does not exist, or the field is not an `array<...>` column.
|
|
4772
5030
|
*
|
|
4773
|
-
* @internal Engine-internal fast path; AI users read
|
|
4774
|
-
* `world.get
|
|
4775
|
-
*
|
|
5031
|
+
* @internal Engine-internal fast path; AI users read public component values
|
|
5032
|
+
* through `world.get`. This accessor is the zero-materialization route the
|
|
5033
|
+
* propagate kernel, relationship owner, and render walk use; it bypasses
|
|
5034
|
+
* the detached public relationship-target snapshot.
|
|
4776
5035
|
*/
|
|
4777
5036
|
_getArrayView(entity, component, fieldName) {
|
|
4778
5037
|
const record = this.lookupAlive(entity, "_getArrayView", component.name);
|
|
@@ -4782,6 +5041,109 @@ var WorldComponentAccess = class {
|
|
|
4782
5041
|
if (!arch) return void 0;
|
|
4783
5042
|
return this.storage.readArrayView(arch, component, this.tableRow(rec), fieldName);
|
|
4784
5043
|
}
|
|
5044
|
+
/** Internal zero-materialisation read for ECS-owned relationship lists. */
|
|
5045
|
+
_getArrayLength(entity, component, fieldName) {
|
|
5046
|
+
const record = this.lookupAlive(entity, "relationship-read", component.name);
|
|
5047
|
+
if (!record.ok) return void 0;
|
|
5048
|
+
const arch = this.graph.archetypes[record.value.archetypeId];
|
|
5049
|
+
if (arch === void 0) return void 0;
|
|
5050
|
+
return this.storage.readArrayLength(arch, component, this.tableRow(record.value), fieldName);
|
|
5051
|
+
}
|
|
5052
|
+
/** Internal zero-materialisation read for one ECS-owned array element. */
|
|
5053
|
+
_getArrayElement(entity, component, fieldName, index) {
|
|
5054
|
+
const record = this.lookupAlive(entity, "relationship-read", component.name);
|
|
5055
|
+
if (!record.ok) return void 0;
|
|
5056
|
+
const arch = this.graph.archetypes[record.value.archetypeId];
|
|
5057
|
+
if (arch === void 0) return void 0;
|
|
5058
|
+
return this.storage.readArrayElement(
|
|
5059
|
+
arch,
|
|
5060
|
+
component,
|
|
5061
|
+
this.tableRow(record.value),
|
|
5062
|
+
fieldName,
|
|
5063
|
+
index
|
|
5064
|
+
);
|
|
5065
|
+
}
|
|
5066
|
+
/** Internal scalar-column read used by parent-first hierarchy traversal. */
|
|
5067
|
+
_getFieldValue(entity, component, fieldName) {
|
|
5068
|
+
const record = this.lookupAlive(entity, "relationship-read", component.name);
|
|
5069
|
+
if (!record.ok) return void 0;
|
|
5070
|
+
const arch = this.graph.archetypes[record.value.archetypeId];
|
|
5071
|
+
if (arch === void 0) return void 0;
|
|
5072
|
+
return this.storage.readFieldValue(arch, component, this.tableRow(record.value), fieldName);
|
|
5073
|
+
}
|
|
5074
|
+
/**
|
|
5075
|
+
* Converge one writable relationship source mutation through the source
|
|
5076
|
+
* owner. The source scalar and its materialized target list are committed as
|
|
5077
|
+
* one operation; no caller receives a raw source column view that could
|
|
5078
|
+
* bypass the mirror/index maintenance.
|
|
5079
|
+
*/
|
|
5080
|
+
setRelationshipSource(entity, component, value, record, arch, markChanged) {
|
|
5081
|
+
const role = relationshipRole(component);
|
|
5082
|
+
if (role?.kind !== "source") return ok(void 0);
|
|
5083
|
+
const row = this.tableRow(record);
|
|
5084
|
+
const current = this.storage.readRow(arch, component, row);
|
|
5085
|
+
const valuePreflight = this.preflightComponentFieldValues(entity, {
|
|
5086
|
+
component,
|
|
5087
|
+
data: value
|
|
5088
|
+
});
|
|
5089
|
+
if (!valuePreflight.ok) return valuePreflight;
|
|
5090
|
+
const merged = { ...current, ...value };
|
|
5091
|
+
const enumError = validateEnumFieldValues(component, merged, entity);
|
|
5092
|
+
if (enumError !== null) return err(enumError);
|
|
5093
|
+
const oldTarget = this.relationshipTargetEntity(component, current);
|
|
5094
|
+
const target = this.relationshipTargetEntity(component, merged);
|
|
5095
|
+
if (target !== null) {
|
|
5096
|
+
const targetRecord = this.records[entityIndex(target)];
|
|
5097
|
+
const actualGeneration = targetRecord?.generation ?? -1;
|
|
5098
|
+
if (!this.recordIsLive(targetRecord, entityGeneration(target))) {
|
|
5099
|
+
return err(
|
|
5100
|
+
new StaleEntityError(target, entityIndex(target), entityGeneration(target), {
|
|
5101
|
+
operation: "relationship-insert",
|
|
5102
|
+
component: component.name,
|
|
5103
|
+
expectedGeneration: entityGeneration(target),
|
|
5104
|
+
actualGeneration
|
|
5105
|
+
})
|
|
5106
|
+
);
|
|
5107
|
+
}
|
|
5108
|
+
const roleAllowsSelf = role.allowSelf;
|
|
5109
|
+
if (entity === target && !roleAllowsSelf) {
|
|
5110
|
+
return err(
|
|
5111
|
+
new RelationshipSelfCycleError(component.name, entity, target)
|
|
5112
|
+
);
|
|
5113
|
+
}
|
|
5114
|
+
const cycleHit = entity === target && roleAllowsSelf ? null : this.relationshipCycleHit(component, target, entity);
|
|
5115
|
+
if (cycleHit !== null) {
|
|
5116
|
+
return err(
|
|
5117
|
+
new RelationshipSelfCycleError(component.name, entity, cycleHit)
|
|
5118
|
+
);
|
|
5119
|
+
}
|
|
5120
|
+
}
|
|
5121
|
+
if (oldTarget !== target) {
|
|
5122
|
+
const prepared = this.prepareRelationshipInsert(component, merged);
|
|
5123
|
+
if (!prepared.ok) return prepared;
|
|
5124
|
+
const preparation = prepared.value;
|
|
5125
|
+
if (oldTarget !== null) {
|
|
5126
|
+
const detached = this.relationshipOnRemove(entity, component, current);
|
|
5127
|
+
if (!detached.ok) {
|
|
5128
|
+
this.releaseRelationshipPreparation(preparation);
|
|
5129
|
+
return detached;
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
5132
|
+
const attached = this.relationshipOnInsert(entity, component, merged, preparation);
|
|
5133
|
+
if (!attached.ok) {
|
|
5134
|
+
this.releaseRelationshipPreparation(preparation);
|
|
5135
|
+
return attached;
|
|
5136
|
+
}
|
|
5137
|
+
}
|
|
5138
|
+
const fieldCols = this.table(arch).storage.get(componentId(component))?.fields;
|
|
5139
|
+
const sourceColumn = fieldCols?.get(role.sourceField);
|
|
5140
|
+
if (sourceColumn === void 0) {
|
|
5141
|
+
return err(new ComponentNotPresentError(entity, component.name));
|
|
5142
|
+
}
|
|
5143
|
+
sourceColumn.view[row] = target === null ? ENTITY_NULL_RAW : target;
|
|
5144
|
+
if (markChanged) this.markComponentChanged(entity, component);
|
|
5145
|
+
return ok(void 0);
|
|
5146
|
+
}
|
|
4785
5147
|
/**
|
|
4786
5148
|
* Write (partial) component data to an entity.
|
|
4787
5149
|
*
|
|
@@ -4820,6 +5182,20 @@ var WorldComponentAccess = class {
|
|
|
4820
5182
|
if (!arch.components.some((candidate) => componentId(candidate) === localId)) {
|
|
4821
5183
|
return err(new ComponentNotPresentError(entity, component.name));
|
|
4822
5184
|
}
|
|
5185
|
+
const role = relationshipRole(component);
|
|
5186
|
+
if (role?.kind === "target") {
|
|
5187
|
+
return err(new RelationshipTargetReadonlyError(component.name, "set"));
|
|
5188
|
+
}
|
|
5189
|
+
if (role?.kind === "source") {
|
|
5190
|
+
return this.setRelationshipSource(
|
|
5191
|
+
entity,
|
|
5192
|
+
component,
|
|
5193
|
+
value,
|
|
5194
|
+
rec,
|
|
5195
|
+
arch,
|
|
5196
|
+
markChanged
|
|
5197
|
+
);
|
|
5198
|
+
}
|
|
4823
5199
|
const valuePreflight = this.preflightComponentFieldValues(entity, {
|
|
4824
5200
|
component,
|
|
4825
5201
|
data: value
|
|
@@ -4986,51 +5362,6 @@ var WorldComponentAccess = class {
|
|
|
4986
5362
|
this.markComponentChanged(entity, component);
|
|
4987
5363
|
return ok(void 0);
|
|
4988
5364
|
}
|
|
4989
|
-
ensureArrayCapacity(entity, component, fieldName, minimum) {
|
|
4990
|
-
const record = this.lookupAlive(entity, "relationship-capacity", component.name);
|
|
4991
|
-
if (!record.ok) return record;
|
|
4992
|
-
const rec = record.value;
|
|
4993
|
-
const arch = this.graph.archetypes[rec.archetypeId];
|
|
4994
|
-
if (!arch) {
|
|
4995
|
-
return err(
|
|
4996
|
-
new StaleEntityError(entity, entityIndex(entity), entityGeneration(entity), {
|
|
4997
|
-
operation: "relationship-capacity",
|
|
4998
|
-
component: component.name,
|
|
4999
|
-
expectedGeneration: entityGeneration(entity),
|
|
5000
|
-
actualGeneration: rec.generation
|
|
5001
|
-
})
|
|
5002
|
-
);
|
|
5003
|
-
}
|
|
5004
|
-
const fieldCols = this.table(arch).storage.get(componentId(component))?.fields;
|
|
5005
|
-
if (!fieldCols) return err(new ComponentNotPresentError(entity, component.name));
|
|
5006
|
-
const fieldNameStr = fieldName;
|
|
5007
|
-
const col = fieldCols.get(fieldNameStr);
|
|
5008
|
-
if (!col) return err(new ComponentNotPresentError(entity, component.name));
|
|
5009
|
-
const arrayMeta = componentDefinition(component).fields[fieldNameStr]?.arrayMeta;
|
|
5010
|
-
if (arrayMeta === void 0) {
|
|
5011
|
-
return err(new ComponentNotPresentError(entity, component.name));
|
|
5012
|
-
}
|
|
5013
|
-
const meta = TYPE_METADATA[arrayMeta.elementType];
|
|
5014
|
-
if (!meta?.byteSize) {
|
|
5015
|
-
return err(new ComponentNotPresentError(entity, component.name));
|
|
5016
|
-
}
|
|
5017
|
-
const maximum = Math.floor(262144 / meta.byteSize);
|
|
5018
|
-
if (!Number.isSafeInteger(minimum) || minimum < 0 || minimum > maximum) {
|
|
5019
|
-
return err(new ManagedBufferOutOfBoundsError(minimum, maximum));
|
|
5020
|
-
}
|
|
5021
|
-
const byteLength = minimum * meta.byteSize;
|
|
5022
|
-
const slotId = col.view[this.tableRow(rec)];
|
|
5023
|
-
if (slotId === 0) {
|
|
5024
|
-
if (minimum === 0) return ok(void 0);
|
|
5025
|
-
const allocated = this.bufferPool.alloc(byteLength);
|
|
5026
|
-
if (!allocated.ok) return allocated;
|
|
5027
|
-
col.view[this.tableRow(rec)] = allocated.value.id;
|
|
5028
|
-
return ok(void 0);
|
|
5029
|
-
}
|
|
5030
|
-
if (this.bufferPool.view(slotId).byteLength >= byteLength) return ok(void 0);
|
|
5031
|
-
const grown = this.bufferPool.grow(slotId, byteLength);
|
|
5032
|
-
return grown.ok ? ok(void 0) : grown;
|
|
5033
|
-
}
|
|
5034
5365
|
/**
|
|
5035
5366
|
* Remove one variable-array element at a known slot. Relationship holders
|
|
5036
5367
|
* supply the slot from their backpointer, so this is O(1) and never scans
|
|
@@ -5098,7 +5429,7 @@ var WorldComponentAccess = class {
|
|
|
5098
5429
|
* (lazy mirror create or exclusive reparent).
|
|
5099
5430
|
* @internal
|
|
5100
5431
|
*/
|
|
5101
|
-
_addComponentCore(entity, componentData, internal, resolveRequirements = true) {
|
|
5432
|
+
_addComponentCore(entity, componentData, internal, resolveRequirements = true, skipVariableArrayInitialization = false) {
|
|
5102
5433
|
const record = this.lookupAlive(entity, "addComponent", componentData.component.name);
|
|
5103
5434
|
if (!record.ok) return record;
|
|
5104
5435
|
const rec = record.value;
|
|
@@ -5144,6 +5475,19 @@ var WorldComponentAccess = class {
|
|
|
5144
5475
|
if (enumErr !== null) {
|
|
5145
5476
|
return err(enumErr);
|
|
5146
5477
|
}
|
|
5478
|
+
const localId = componentId(componentData.component);
|
|
5479
|
+
let relationshipPreparation;
|
|
5480
|
+
const componentAlreadyPresent = srcArch.components.some(
|
|
5481
|
+
(candidate) => componentId(candidate) === localId
|
|
5482
|
+
);
|
|
5483
|
+
if (!internal && !componentAlreadyPresent && relationshipRole(componentData.component)?.kind === "source") {
|
|
5484
|
+
const prepared = this.prepareRelationshipInsert(
|
|
5485
|
+
componentData.component,
|
|
5486
|
+
filled
|
|
5487
|
+
);
|
|
5488
|
+
if (!prepared.ok) return prepared;
|
|
5489
|
+
relationshipPreparation = prepared.value;
|
|
5490
|
+
}
|
|
5147
5491
|
if (resolveRequirements) {
|
|
5148
5492
|
const required = expandComponentRequirements([componentData]).slice(1);
|
|
5149
5493
|
for (const requirement of required) {
|
|
@@ -5153,9 +5497,13 @@ var WorldComponentAccess = class {
|
|
|
5153
5497
|
continue;
|
|
5154
5498
|
}
|
|
5155
5499
|
const added = this._addComponentCore(entity, requirement, internal, false);
|
|
5156
|
-
if (!added.ok)
|
|
5500
|
+
if (!added.ok) {
|
|
5501
|
+
this.releaseRelationshipPreparation(relationshipPreparation);
|
|
5502
|
+
return added;
|
|
5503
|
+
}
|
|
5157
5504
|
srcArch = this.graph.archetypes[rec.archetypeId];
|
|
5158
5505
|
if (!srcArch) {
|
|
5506
|
+
this.releaseRelationshipPreparation(relationshipPreparation);
|
|
5159
5507
|
return err(
|
|
5160
5508
|
new StaleEntityError(entity, entityIndex(entity), entityGeneration(entity), {
|
|
5161
5509
|
operation: "addComponent",
|
|
@@ -5167,32 +5515,21 @@ var WorldComponentAccess = class {
|
|
|
5167
5515
|
}
|
|
5168
5516
|
}
|
|
5169
5517
|
}
|
|
5170
|
-
const localId = componentId(componentData.component);
|
|
5171
5518
|
if (srcArch.components.some((candidate) => componentId(candidate) === localId)) {
|
|
5519
|
+
this.releaseRelationshipPreparation(relationshipPreparation);
|
|
5172
5520
|
const role = relationshipRole(componentData.component);
|
|
5173
5521
|
if (role?.kind === "source" && role.exclusive && !internal) {
|
|
5174
|
-
|
|
5175
|
-
componentData.component,
|
|
5176
|
-
filled
|
|
5177
|
-
);
|
|
5178
|
-
if (!prepared.ok) return prepared;
|
|
5179
|
-
const removeR = this._removeComponentCore(
|
|
5522
|
+
return this.setRelationshipSource(
|
|
5180
5523
|
entity,
|
|
5181
5524
|
componentData.component,
|
|
5182
|
-
|
|
5525
|
+
filled,
|
|
5526
|
+
rec,
|
|
5527
|
+
srcArch,
|
|
5528
|
+
true
|
|
5183
5529
|
);
|
|
5184
|
-
if (!removeR.ok) return removeR;
|
|
5185
|
-
return this._addComponentCore(entity, componentData, false);
|
|
5186
5530
|
}
|
|
5187
5531
|
return err(new ComponentAlreadyPresentError(entity, componentData.component.name));
|
|
5188
5532
|
}
|
|
5189
|
-
if (!internal && relationshipRole(componentData.component)?.kind === "source") {
|
|
5190
|
-
const prepared = this.prepareRelationshipInsert(
|
|
5191
|
-
componentData.component,
|
|
5192
|
-
filled
|
|
5193
|
-
);
|
|
5194
|
-
if (!prepared.ok) return prepared;
|
|
5195
|
-
}
|
|
5196
5533
|
const targetArch = getAddEdge(
|
|
5197
5534
|
this.graph,
|
|
5198
5535
|
srcArch,
|
|
@@ -5209,7 +5546,8 @@ var WorldComponentAccess = class {
|
|
|
5209
5546
|
targetArch,
|
|
5210
5547
|
componentData.component,
|
|
5211
5548
|
this.tableRow(rec),
|
|
5212
|
-
filled
|
|
5549
|
+
filled,
|
|
5550
|
+
skipVariableArrayInitialization ? { skipVariableArrayInitialization: true } : void 0
|
|
5213
5551
|
);
|
|
5214
5552
|
}
|
|
5215
5553
|
this.markComponentAdded(entity, componentData.component);
|
|
@@ -5217,9 +5555,13 @@ var WorldComponentAccess = class {
|
|
|
5217
5555
|
const relationshipResult = this.relationshipOnInsert(
|
|
5218
5556
|
entity,
|
|
5219
5557
|
componentData.component,
|
|
5220
|
-
filled
|
|
5558
|
+
filled,
|
|
5559
|
+
relationshipPreparation
|
|
5221
5560
|
);
|
|
5222
|
-
if (!relationshipResult.ok)
|
|
5561
|
+
if (!relationshipResult.ok) {
|
|
5562
|
+
this.releaseRelationshipPreparation(relationshipPreparation);
|
|
5563
|
+
return relationshipResult;
|
|
5564
|
+
}
|
|
5223
5565
|
}
|
|
5224
5566
|
this.markStructureChanged();
|
|
5225
5567
|
this.state.recordStructuralEvidence({
|
|
@@ -5347,6 +5689,35 @@ var WorldComponentAccess = class {
|
|
|
5347
5689
|
const slot = entityIndex(entity);
|
|
5348
5690
|
const record = this.records[slot];
|
|
5349
5691
|
if (!record || record.archetypeId !== -1) return ok(void 0);
|
|
5692
|
+
const relationshipPreparations = [];
|
|
5693
|
+
for (let index = 0; index < componentDatas.length; index += 1) {
|
|
5694
|
+
const componentData = componentDatas[index];
|
|
5695
|
+
if (componentData === void 0 || relationshipRole(componentData.component)?.kind !== "source") {
|
|
5696
|
+
continue;
|
|
5697
|
+
}
|
|
5698
|
+
const filled = fillComponentDefaults(
|
|
5699
|
+
componentData.component,
|
|
5700
|
+
componentData.data
|
|
5701
|
+
);
|
|
5702
|
+
const target = this.relationshipTargetEntity(
|
|
5703
|
+
componentData.component,
|
|
5704
|
+
filled
|
|
5705
|
+
);
|
|
5706
|
+
const targetRecord = target === null ? void 0 : this.records[entityIndex(target)];
|
|
5707
|
+
if (target !== null && targetRecord !== void 0 && this.recordIsLive(targetRecord, entityGeneration(target))) {
|
|
5708
|
+
const prepared = this.prepareRelationshipInsert(
|
|
5709
|
+
componentData.component,
|
|
5710
|
+
filled
|
|
5711
|
+
);
|
|
5712
|
+
if (!prepared.ok) {
|
|
5713
|
+
for (const reservation of relationshipPreparations) {
|
|
5714
|
+
this.releaseRelationshipPreparation(reservation);
|
|
5715
|
+
}
|
|
5716
|
+
return prepared;
|
|
5717
|
+
}
|
|
5718
|
+
relationshipPreparations[index] = prepared.value;
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5350
5721
|
const componentIds = componentDatas.map((cd) => componentId(cd.component));
|
|
5351
5722
|
const components = componentDatas.map((cd) => cd.component);
|
|
5352
5723
|
const arch = getOrCreateArchetype(this.graph, componentIds, components);
|
|
@@ -5364,15 +5735,21 @@ var WorldComponentAccess = class {
|
|
|
5364
5735
|
this.storage.writeRow(arch, cd.component, tableRow2, filled);
|
|
5365
5736
|
}
|
|
5366
5737
|
this.storage.writeEntitySelf(arch, tableRow2, entity);
|
|
5367
|
-
for (
|
|
5738
|
+
for (let index = 0; index < componentDatas.length; index += 1) {
|
|
5739
|
+
const cd = componentDatas[index];
|
|
5740
|
+
if (cd === void 0) continue;
|
|
5368
5741
|
if (relationshipRole(cd.component)?.kind === "source") {
|
|
5369
5742
|
const filled = fillComponentDefaults(cd.component, cd.data);
|
|
5370
5743
|
const relationshipResult = this.relationshipOnInsert(
|
|
5371
5744
|
entity,
|
|
5372
5745
|
cd.component,
|
|
5373
|
-
filled
|
|
5746
|
+
filled,
|
|
5747
|
+
relationshipPreparations[index]
|
|
5374
5748
|
);
|
|
5375
5749
|
if (!relationshipResult.ok) {
|
|
5750
|
+
for (const reservation of relationshipPreparations) {
|
|
5751
|
+
this.releaseRelationshipPreparation(reservation);
|
|
5752
|
+
}
|
|
5376
5753
|
return relationshipResult;
|
|
5377
5754
|
}
|
|
5378
5755
|
}
|
|
@@ -5472,10 +5849,10 @@ var BufferPool = class {
|
|
|
5472
5849
|
* Allocate a managed buffer slot of at least `byteLength` bytes.
|
|
5473
5850
|
*
|
|
5474
5851
|
* Routes:
|
|
5475
|
-
* - byteLength
|
|
5852
|
+
* - invalid byteLength -> structured out-of-bounds error.
|
|
5476
5853
|
* - byteLength == 0 -> ok({ id, view: zero-length Uint8Array }) (no bucket).
|
|
5477
5854
|
* - byteLength <= 262144 -> ok({ id, view }), bucket = smallest >= byteLength.
|
|
5478
|
-
* - byteLength
|
|
5855
|
+
* - byteLength > 262144 -> a dedicated allocation; allocation failure is structured.
|
|
5479
5856
|
*
|
|
5480
5857
|
* D-5: size classes are radix-4 (16 / 64 / 256 / 1K / 4K / 16K / 64K / 256K).
|
|
5481
5858
|
* Free-list pop reuses the most recently released slot id at the same bucket;
|
|
@@ -5489,11 +5866,20 @@ var BufferPool = class {
|
|
|
5489
5866
|
this.slots.set(id2, { sizeClassIdx: -1, buffer: buffer2, view: view2, byteLength: 0, live: true });
|
|
5490
5867
|
return ok({ id: id2, view: view2 });
|
|
5491
5868
|
}
|
|
5869
|
+
if (!Number.isSafeInteger(byteLength) || byteLength < 0) {
|
|
5870
|
+
return err(new ManagedBufferOutOfBoundsError(byteLength, 0));
|
|
5871
|
+
}
|
|
5492
5872
|
const idx = bucketIndex(byteLength);
|
|
5493
5873
|
if (idx === SIZE_CLASSES.length) {
|
|
5494
|
-
|
|
5495
|
-
new
|
|
5496
|
-
|
|
5874
|
+
try {
|
|
5875
|
+
const buffer2 = new ArrayBuffer(byteLength);
|
|
5876
|
+
const view2 = new Uint8Array(buffer2);
|
|
5877
|
+
const id2 = this.nextId++;
|
|
5878
|
+
this.slots.set(id2, { sizeClassIdx: idx, buffer: buffer2, view: view2, byteLength, live: true });
|
|
5879
|
+
return ok({ id: id2, view: view2 });
|
|
5880
|
+
} catch {
|
|
5881
|
+
return err(new ManagedBufferOutOfBoundsError(byteLength, 0));
|
|
5882
|
+
}
|
|
5497
5883
|
}
|
|
5498
5884
|
const bucketBytes = SIZE_CLASSES[idx];
|
|
5499
5885
|
if (bucketBytes === void 0) {
|
|
@@ -5537,7 +5923,7 @@ var BufferPool = class {
|
|
|
5537
5923
|
* the caller become detached / orphaned - callers must use `pool.view(id)`
|
|
5538
5924
|
* after grow to read the refreshed view (the `release` loop refreshes
|
|
5539
5925
|
* automatically).
|
|
5540
|
-
* - newBytes
|
|
5926
|
+
* - newBytes beyond the last pooled class -> dedicated allocation.
|
|
5541
5927
|
*/
|
|
5542
5928
|
grow(id, newBytes) {
|
|
5543
5929
|
const slot = this.slots.get(id);
|
|
@@ -5550,25 +5936,27 @@ var BufferPool = class {
|
|
|
5550
5936
|
if (newBytes === slot.byteLength) {
|
|
5551
5937
|
return ok(slot.view);
|
|
5552
5938
|
}
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
return err(
|
|
5556
|
-
new ManagedBufferOutOfBoundsError(newBytes, SIZE_CLASSES[SIZE_CLASSES.length - 1] ?? 0)
|
|
5557
|
-
);
|
|
5939
|
+
if (!Number.isSafeInteger(newBytes) || newBytes < 0) {
|
|
5940
|
+
return err(new ManagedBufferOutOfBoundsError(newBytes, slot.buffer.byteLength));
|
|
5558
5941
|
}
|
|
5559
|
-
|
|
5942
|
+
const newIdx = bucketIndex(newBytes);
|
|
5943
|
+
if (newBytes <= slot.buffer.byteLength) {
|
|
5560
5944
|
slot.byteLength = newBytes;
|
|
5561
5945
|
slot.view = new Uint8Array(slot.buffer, 0, newBytes);
|
|
5562
5946
|
return ok(slot.view);
|
|
5563
5947
|
}
|
|
5564
|
-
const newBucketBytes = SIZE_CLASSES[newIdx];
|
|
5948
|
+
const newBucketBytes = SIZE_CLASSES[newIdx] ?? Math.max(newBytes, slot.buffer.byteLength * 2);
|
|
5565
5949
|
const oldByteLength = slot.byteLength;
|
|
5566
5950
|
let nextBuffer;
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5951
|
+
try {
|
|
5952
|
+
if (HAS_TRANSFER2) {
|
|
5953
|
+
nextBuffer = slot.buffer.transfer(newBucketBytes);
|
|
5954
|
+
} else {
|
|
5955
|
+
nextBuffer = new ArrayBuffer(newBucketBytes);
|
|
5956
|
+
new Uint8Array(nextBuffer).set(new Uint8Array(slot.buffer, 0, oldByteLength));
|
|
5957
|
+
}
|
|
5958
|
+
} catch {
|
|
5959
|
+
return err(new ManagedBufferOutOfBoundsError(newBytes, slot.buffer.byteLength));
|
|
5572
5960
|
}
|
|
5573
5961
|
slot.sizeClassIdx = newIdx;
|
|
5574
5962
|
slot.buffer = nextBuffer;
|
|
@@ -5587,7 +5975,7 @@ var BufferPool = class {
|
|
|
5587
5975
|
if (slot === void 0) return ok(void 0);
|
|
5588
5976
|
if (!slot.live) return ok(void 0);
|
|
5589
5977
|
slot.live = false;
|
|
5590
|
-
if (slot.sizeClassIdx >= 0) {
|
|
5978
|
+
if (slot.sizeClassIdx >= 0 && slot.sizeClassIdx < SIZE_CLASSES.length) {
|
|
5591
5979
|
const bucket = this.freeBuckets[slot.sizeClassIdx];
|
|
5592
5980
|
if (bucket !== void 0) bucket.push(id);
|
|
5593
5981
|
} else {
|
|
@@ -5616,7 +6004,7 @@ var BufferPool = class {
|
|
|
5616
6004
|
const slot = this.slots.get(id);
|
|
5617
6005
|
if (slot === void 0 || !slot.live) return 0;
|
|
5618
6006
|
if (slot.sizeClassIdx < 0) return 0;
|
|
5619
|
-
return
|
|
6007
|
+
return slot.buffer.byteLength;
|
|
5620
6008
|
}
|
|
5621
6009
|
/**
|
|
5622
6010
|
* Reset the logical byteLength of slot `id` to `newByteLength` while
|
|
@@ -5644,7 +6032,7 @@ var BufferPool = class {
|
|
|
5644
6032
|
}
|
|
5645
6033
|
return err(new ManagedBufferOutOfBoundsError(newByteLength, 0));
|
|
5646
6034
|
}
|
|
5647
|
-
const bucketBytes =
|
|
6035
|
+
const bucketBytes = slot.buffer.byteLength;
|
|
5648
6036
|
if (newByteLength > bucketBytes) {
|
|
5649
6037
|
return err(new ManagedBufferOutOfBoundsError(newByteLength, bucketBytes));
|
|
5650
6038
|
}
|
|
@@ -6137,6 +6525,27 @@ function spawnCore(world, componentDatas, internal) {
|
|
|
6137
6525
|
if (enumErr !== null) return err(enumErr);
|
|
6138
6526
|
filledData.push(filled);
|
|
6139
6527
|
}
|
|
6528
|
+
const relationshipPreparations = [];
|
|
6529
|
+
if (!internal) {
|
|
6530
|
+
for (let index = 0; index < componentDatas.length; index += 1) {
|
|
6531
|
+
const componentData = componentDatas[index];
|
|
6532
|
+
const value = filledData[index];
|
|
6533
|
+
if (componentData === void 0 || value === void 0 || relationshipRole(componentData.component)?.kind !== "source") {
|
|
6534
|
+
continue;
|
|
6535
|
+
}
|
|
6536
|
+
const prepared = world[worldInternal].prepareRelationshipInsert(
|
|
6537
|
+
componentData.component,
|
|
6538
|
+
value
|
|
6539
|
+
);
|
|
6540
|
+
if (!prepared.ok) {
|
|
6541
|
+
for (const reservation of relationshipPreparations) {
|
|
6542
|
+
world[worldInternal].releaseRelationshipPreparation(reservation);
|
|
6543
|
+
}
|
|
6544
|
+
return prepared;
|
|
6545
|
+
}
|
|
6546
|
+
relationshipPreparations[index] = prepared.value;
|
|
6547
|
+
}
|
|
6548
|
+
}
|
|
6140
6549
|
const indexSlot = world[worldInternal].allocateIndex();
|
|
6141
6550
|
const record = world[worldInternal].getRecords()[indexSlot];
|
|
6142
6551
|
if (record === void 0)
|
|
@@ -6172,9 +6581,15 @@ function spawnCore(world, componentDatas, internal) {
|
|
|
6172
6581
|
const relation = world[worldInternal].relationshipOnInsert(
|
|
6173
6582
|
spawnedEntity,
|
|
6174
6583
|
cd.component,
|
|
6175
|
-
filled
|
|
6584
|
+
filled,
|
|
6585
|
+
relationshipPreparations[i]
|
|
6176
6586
|
);
|
|
6177
|
-
if (!relation.ok)
|
|
6587
|
+
if (!relation.ok) {
|
|
6588
|
+
for (const reservation of relationshipPreparations) {
|
|
6589
|
+
world[worldInternal].releaseRelationshipPreparation(reservation);
|
|
6590
|
+
}
|
|
6591
|
+
return relation;
|
|
6592
|
+
}
|
|
6178
6593
|
}
|
|
6179
6594
|
}
|
|
6180
6595
|
world[worldInternal].markStructureChanged();
|
|
@@ -6248,7 +6663,8 @@ function despawnCore(world, entity, internal) {
|
|
|
6248
6663
|
}
|
|
6249
6664
|
function worldAddChild(world, parent, child, component, data) {
|
|
6250
6665
|
const holderComp = component;
|
|
6251
|
-
|
|
6666
|
+
const role = relationshipRole(holderComp);
|
|
6667
|
+
if (role?.kind !== "source") {
|
|
6252
6668
|
return err(new ComponentNotPresentError(child, component.name));
|
|
6253
6669
|
}
|
|
6254
6670
|
const parentSlot = entityIndex(parent);
|
|
@@ -6277,7 +6693,6 @@ function worldAddChild(world, parent, child, component, data) {
|
|
|
6277
6693
|
})
|
|
6278
6694
|
);
|
|
6279
6695
|
}
|
|
6280
|
-
const role = relationshipRole(holderComp);
|
|
6281
6696
|
if (child === parent && !(role?.kind === "source" && role.allowSelf)) {
|
|
6282
6697
|
return err(new RelationshipSelfCycleError(component.name, child, child));
|
|
6283
6698
|
}
|
|
@@ -6328,10 +6743,10 @@ function worldRemoveChild(world, parent, child, component) {
|
|
|
6328
6743
|
}
|
|
6329
6744
|
function worldReparent(world, child, newParent, component, data) {
|
|
6330
6745
|
const holderComp = component;
|
|
6331
|
-
|
|
6746
|
+
const role = relationshipRole(holderComp);
|
|
6747
|
+
if (role?.kind !== "source") {
|
|
6332
6748
|
return err(new ComponentNotPresentError(child, component.name));
|
|
6333
6749
|
}
|
|
6334
|
-
const role = relationshipRole(holderComp);
|
|
6335
6750
|
if (child === newParent && !(role?.kind === "source" && role.allowSelf)) {
|
|
6336
6751
|
return err(
|
|
6337
6752
|
new RelationshipSelfCycleError(component.name, child, newParent)
|
|
@@ -6361,11 +6776,14 @@ function worldReparent(world, child, newParent, component, data) {
|
|
|
6361
6776
|
})
|
|
6362
6777
|
);
|
|
6363
6778
|
}
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6779
|
+
const payload = {
|
|
6780
|
+
...data,
|
|
6781
|
+
[role.sourceField]: newParent
|
|
6782
|
+
};
|
|
6783
|
+
if (childArch.components.some((candidate) => componentId(candidate) === componentId(holderComp))) {
|
|
6784
|
+
return world.set(child, component, payload);
|
|
6367
6785
|
}
|
|
6368
|
-
return world.addComponent(child, { component, data });
|
|
6786
|
+
return world.addComponent(child, { component, data: payload });
|
|
6369
6787
|
}
|
|
6370
6788
|
function worldIterAncestors(world, entity) {
|
|
6371
6789
|
return {
|
|
@@ -6986,6 +7404,9 @@ var World = class {
|
|
|
6986
7404
|
cancelPendingEntity: this.internalcancelPendingEntity.bind(this),
|
|
6987
7405
|
despawnCore: this.internaldespawnCore.bind(this),
|
|
6988
7406
|
getArrayView: this.internalgetArrayView.bind(this),
|
|
7407
|
+
getArrayLength: this.internalgetArrayLength.bind(this),
|
|
7408
|
+
getArrayElement: this.internalgetArrayElement.bind(this),
|
|
7409
|
+
getFieldValue: this.internalgetFieldValue.bind(this),
|
|
6989
7410
|
getBufferPool: this.internalgetBufferPool.bind(this),
|
|
6990
7411
|
getClockWriter: this.internalgetClockWriter.bind(this),
|
|
6991
7412
|
getComponentChange: this.internalgetComponentChange.bind(this),
|
|
@@ -7016,6 +7437,7 @@ var World = class {
|
|
|
7016
7437
|
materializePendingEntity: this.internalmaterializePendingEntity.bind(this),
|
|
7017
7438
|
nextMutationEpoch: this.internalnextMutationEpoch.bind(this),
|
|
7018
7439
|
poisonExecution: this.internalpoisonExecution.bind(this),
|
|
7440
|
+
prepareRelationshipInsert: this.internalprepareRelationshipInsert.bind(this),
|
|
7019
7441
|
publishDerivedRange: this.internalpublishDerivedRange.bind(this),
|
|
7020
7442
|
preflightComponentData: this.internalpreflightComponentData.bind(this),
|
|
7021
7443
|
readRow: this.internalreadRow.bind(this),
|
|
@@ -7024,6 +7446,7 @@ var World = class {
|
|
|
7024
7446
|
relationshipOnInsert: this.internalrelationshipOnInsert.bind(this),
|
|
7025
7447
|
relationshipOnRemove: this.internalrelationshipOnRemove.bind(this),
|
|
7026
7448
|
releaseManagedRefsOnRow: this.internalreleaseManagedRefsOnRow.bind(this),
|
|
7449
|
+
releaseRelationshipPreparation: this.internalreleaseRelationshipPreparation.bind(this),
|
|
7027
7450
|
removeComponentCore: this.internalremoveComponentCore.bind(this),
|
|
7028
7451
|
routeError: this.internalrouteError.bind(this),
|
|
7029
7452
|
restoreMutationEpoch: this.internalrestoreMutationEpoch.bind(this),
|
|
@@ -7175,7 +7598,12 @@ var World = class {
|
|
|
7175
7598
|
}
|
|
7176
7599
|
/** Query facade write after the facade has already marked evidence. */
|
|
7177
7600
|
internalsetQueryRow(entity, component, value) {
|
|
7178
|
-
return this.componentAccess.set(
|
|
7601
|
+
return this.componentAccess.set(
|
|
7602
|
+
entity,
|
|
7603
|
+
component,
|
|
7604
|
+
value,
|
|
7605
|
+
relationshipRole(component)?.kind === "source"
|
|
7606
|
+
);
|
|
7179
7607
|
}
|
|
7180
7608
|
/** Query facade read that does not re-enter the public World API. */
|
|
7181
7609
|
internalgetQueryRow(entity, component) {
|
|
@@ -7523,6 +7951,15 @@ var World = class {
|
|
|
7523
7951
|
internalgetArrayView(entity, component, fieldName) {
|
|
7524
7952
|
return this.componentAccess._getArrayView(entity, component, fieldName);
|
|
7525
7953
|
}
|
|
7954
|
+
internalgetArrayLength(entity, component, fieldName) {
|
|
7955
|
+
return this.componentAccess._getArrayLength(entity, component, fieldName);
|
|
7956
|
+
}
|
|
7957
|
+
internalgetArrayElement(entity, component, fieldName, index) {
|
|
7958
|
+
return this.componentAccess._getArrayElement(entity, component, fieldName, index);
|
|
7959
|
+
}
|
|
7960
|
+
internalgetFieldValue(entity, component, fieldName) {
|
|
7961
|
+
return this.componentAccess._getFieldValue(entity, component, fieldName);
|
|
7962
|
+
}
|
|
7526
7963
|
set(entity, component, value) {
|
|
7527
7964
|
if (isRelationshipTarget(component)) return this.relationshipTargetWriteError(component, "set");
|
|
7528
7965
|
return this.componentAccess.set(entity, component, value);
|
|
@@ -7591,8 +8028,16 @@ var World = class {
|
|
|
7591
8028
|
this.componentAccess.releaseManagedRefsOnRow(a, c, r);
|
|
7592
8029
|
}
|
|
7593
8030
|
/** */
|
|
7594
|
-
internalrelationshipOnInsert(h, c, v) {
|
|
7595
|
-
return this.componentAccess.relationshipOnInsert(h, c, v);
|
|
8031
|
+
internalrelationshipOnInsert(h, c, v, preparation) {
|
|
8032
|
+
return this.componentAccess.relationshipOnInsert(h, c, v, preparation);
|
|
8033
|
+
}
|
|
8034
|
+
/** */
|
|
8035
|
+
internalprepareRelationshipInsert(c, v) {
|
|
8036
|
+
return this.componentAccess.prepareRelationshipInsert(c, v);
|
|
8037
|
+
}
|
|
8038
|
+
/** */
|
|
8039
|
+
internalreleaseRelationshipPreparation(preparation) {
|
|
8040
|
+
this.componentAccess.releaseRelationshipPreparation(preparation);
|
|
7596
8041
|
}
|
|
7597
8042
|
/** */
|
|
7598
8043
|
internalrelationshipOnRemove(h, c, v) {
|