@forgeax/engine-ecs 0.0.0-dev.8d955ade1c79
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/LICENSE +202 -0
- package/README.md +278 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__fixtures__/field-shape-fixture.d.ts +16 -0
- package/dist/__fixtures__/field-shape-fixture.d.ts.map +1 -0
- package/dist/__tests__/__fixtures__/inspect-scripts.snapshot.d.ts +3 -0
- package/dist/__tests__/__fixtures__/inspect-scripts.snapshot.d.ts.map +1 -0
- package/dist/__tests__/archetype.unit.test.d.ts +2 -0
- package/dist/__tests__/archetype.unit.test.d.ts.map +1 -0
- package/dist/__tests__/array-handle-element.test-d.d.ts +2 -0
- package/dist/__tests__/array-handle-element.test-d.d.ts.map +1 -0
- package/dist/__tests__/buffer-array-vocab.test-d.d.ts +2 -0
- package/dist/__tests__/buffer-array-vocab.test-d.d.ts.map +1 -0
- package/dist/__tests__/buffer-field-input-widen.unit.test.d.ts +2 -0
- package/dist/__tests__/buffer-field-input-widen.unit.test.d.ts.map +1 -0
- package/dist/__tests__/buffer-pool.test-d.d.ts +2 -0
- package/dist/__tests__/buffer-pool.test-d.d.ts.map +1 -0
- package/dist/__tests__/change-detection.unit.test.d.ts +2 -0
- package/dist/__tests__/change-detection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/command-buffer.test.d.ts +2 -0
- package/dist/__tests__/command-buffer.test.d.ts.map +1 -0
- package/dist/__tests__/commands-deferred.unit.test.d.ts +2 -0
- package/dist/__tests__/commands-deferred.unit.test.d.ts.map +1 -0
- package/dist/__tests__/component-catalog.test.d.ts +2 -0
- package/dist/__tests__/component-catalog.test.d.ts.map +1 -0
- package/dist/__tests__/component-definition-identity.unit.test.d.ts +2 -0
- package/dist/__tests__/component-definition-identity.unit.test.d.ts.map +1 -0
- package/dist/__tests__/component-nan-preflight.unit.test.d.ts +2 -0
- package/dist/__tests__/component-nan-preflight.unit.test.d.ts.map +1 -0
- package/dist/__tests__/component-schema.test-d.d.ts +2 -0
- package/dist/__tests__/component-schema.test-d.d.ts.map +1 -0
- package/dist/__tests__/component-schema.test.d.ts +2 -0
- package/dist/__tests__/component-schema.test.d.ts.map +1 -0
- package/dist/__tests__/define-component-options.test-d.d.ts +2 -0
- package/dist/__tests__/define-component-options.test-d.d.ts.map +1 -0
- package/dist/__tests__/deprecated-vocab-rejection.unit.test.d.ts +2 -0
- package/dist/__tests__/deprecated-vocab-rejection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/despawn-all.unit.test.d.ts +2 -0
- package/dist/__tests__/despawn-all.unit.test.d.ts.map +1 -0
- package/dist/__tests__/ecs-core-reduction.characterization.test.d.ts +2 -0
- package/dist/__tests__/ecs-core-reduction.characterization.test.d.ts.map +1 -0
- package/dist/__tests__/ecs-core-reduction.perf.test.d.ts +2 -0
- package/dist/__tests__/ecs-core-reduction.perf.test.d.ts.map +1 -0
- package/dist/__tests__/ecs-error-code-31.test-d.d.ts +2 -0
- package/dist/__tests__/ecs-error-code-31.test-d.d.ts.map +1 -0
- package/dist/__tests__/entity-disabling.unit.test.d.ts +2 -0
- package/dist/__tests__/entity-disabling.unit.test.d.ts.map +1 -0
- package/dist/__tests__/entity-handle-codec-parity.unit.test.d.ts +2 -0
- package/dist/__tests__/entity-handle-codec-parity.unit.test.d.ts.map +1 -0
- package/dist/__tests__/errors.unit.test.d.ts +2 -0
- package/dist/__tests__/errors.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-conflict-boundary.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-conflict-boundary.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-shared-storage.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-shared-storage.unit.test.d.ts.map +1 -0
- package/dist/__tests__/externalization-portable-value.unit.test.d.ts +2 -0
- package/dist/__tests__/externalization-portable-value.unit.test.d.ts.map +1 -0
- package/dist/__tests__/externalization-projection.unit.test.d.ts +2 -0
- package/dist/__tests__/externalization-projection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/externalization-render-read-lease.test-d.d.ts +2 -0
- package/dist/__tests__/externalization-render-read-lease.test-d.d.ts.map +1 -0
- package/dist/__tests__/externalization-render-read-lease.unit.test.d.ts +2 -0
- package/dist/__tests__/externalization-render-read-lease.unit.test.d.ts.map +1 -0
- package/dist/__tests__/externalization-root-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/externalization-root-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/field-enum-labels-reflection.test.d.ts +2 -0
- package/dist/__tests__/field-enum-labels-reflection.test.d.ts.map +1 -0
- package/dist/__tests__/field-shape-fixture.test.d.ts +2 -0
- package/dist/__tests__/field-shape-fixture.test.d.ts.map +1 -0
- package/dist/__tests__/field-transient-reflection.test.d.ts +2 -0
- package/dist/__tests__/field-transient-reflection.test.d.ts.map +1 -0
- package/dist/__tests__/field-value-type.test-d.d.ts +2 -0
- package/dist/__tests__/field-value-type.test-d.d.ts.map +1 -0
- package/dist/__tests__/fixed-update.unit.test.d.ts +2 -0
- package/dist/__tests__/fixed-update.unit.test.d.ts.map +1 -0
- package/dist/__tests__/handle.test-d.d.ts +2 -0
- package/dist/__tests__/handle.test-d.d.ts.map +1 -0
- package/dist/__tests__/hierarchy.unit.test.d.ts +2 -0
- package/dist/__tests__/hierarchy.unit.test.d.ts.map +1 -0
- package/dist/__tests__/managed-array-vocab.test-d.d.ts +2 -0
- package/dist/__tests__/managed-array-vocab.test-d.d.ts.map +1 -0
- package/dist/__tests__/minimal-example.test-d.d.ts +2 -0
- package/dist/__tests__/minimal-example.test-d.d.ts.map +1 -0
- package/dist/__tests__/name-read-types.test-d.d.ts +2 -0
- package/dist/__tests__/name-read-types.test-d.d.ts.map +1 -0
- package/dist/__tests__/plugin-service.test.d.ts +2 -0
- package/dist/__tests__/plugin-service.test.d.ts.map +1 -0
- package/dist/__tests__/query-combinations.unit.test.d.ts +2 -0
- package/dist/__tests__/query-combinations.unit.test.d.ts.map +1 -0
- package/dist/__tests__/query-light-extract-flat-column-ratio.perf.test.d.ts +2 -0
- package/dist/__tests__/query-light-extract-flat-column-ratio.perf.test.d.ts.map +1 -0
- package/dist/__tests__/query-storage-trends.perf.test.d.ts +2 -0
- package/dist/__tests__/query-storage-trends.perf.test.d.ts.map +1 -0
- package/dist/__tests__/query-trs-flat-column-ratio.perf.test.d.ts +2 -0
- package/dist/__tests__/query-trs-flat-column-ratio.perf.test.d.ts.map +1 -0
- package/dist/__tests__/query.unit.test.d.ts +2 -0
- package/dist/__tests__/query.unit.test.d.ts.map +1 -0
- package/dist/__tests__/relationship-index.perf.test.d.ts +2 -0
- package/dist/__tests__/relationship-index.perf.test.d.ts.map +1 -0
- package/dist/__tests__/relationship-index.test.d.ts +2 -0
- package/dist/__tests__/relationship-index.test.d.ts.map +1 -0
- package/dist/__tests__/scalar-field-type-owner.test-d.d.ts +2 -0
- package/dist/__tests__/scalar-field-type-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/schedule-determinism.unit.test.d.ts +2 -0
- package/dist/__tests__/schedule-determinism.unit.test.d.ts.map +1 -0
- package/dist/__tests__/schedule-inspection.unit.test.d.ts +2 -0
- package/dist/__tests__/schedule-inspection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/schedule-scope-error.unit.test.d.ts +2 -0
- package/dist/__tests__/schedule-scope-error.unit.test.d.ts.map +1 -0
- package/dist/__tests__/schedule-token.unit.test.d.ts +2 -0
- package/dist/__tests__/schedule-token.unit.test.d.ts.map +1 -0
- package/dist/__tests__/schema-vocab.test-d.d.ts +2 -0
- package/dist/__tests__/schema-vocab.test-d.d.ts.map +1 -0
- package/dist/__tests__/serialization.unit.test.d.ts +2 -0
- package/dist/__tests__/serialization.unit.test.d.ts.map +1 -0
- package/dist/__tests__/shared-kernel-eligibility.test-d.d.ts +2 -0
- package/dist/__tests__/shared-kernel-eligibility.test-d.d.ts.map +1 -0
- package/dist/__tests__/shared-kernel-eligibility.unit.test.d.ts +2 -0
- package/dist/__tests__/shared-kernel-eligibility.unit.test.d.ts.map +1 -0
- package/dist/__tests__/shared-ref-store.unit.test.d.ts +2 -0
- package/dist/__tests__/shared-ref-store.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sparse-tag.unit.test.d.ts +2 -0
- package/dist/__tests__/sparse-tag.unit.test.d.ts.map +1 -0
- package/dist/__tests__/spawn-field-error.test-d.d.ts +2 -0
- package/dist/__tests__/spawn-field-error.test-d.d.ts.map +1 -0
- package/dist/__tests__/stale-error.test-d.d.ts +2 -0
- package/dist/__tests__/stale-error.test-d.d.ts.map +1 -0
- package/dist/__tests__/string-view-symmetry.test-d.d.ts +2 -0
- package/dist/__tests__/string-view-symmetry.test-d.d.ts.map +1 -0
- package/dist/__tests__/string-vocab.test-d.d.ts +2 -0
- package/dist/__tests__/string-vocab.test-d.d.ts.map +1 -0
- package/dist/__tests__/structure-epoch-boundaries.unit.test.d.ts +2 -0
- package/dist/__tests__/structure-epoch-boundaries.unit.test.d.ts.map +1 -0
- package/dist/__tests__/structure-epoch.unit.test.d.ts +2 -0
- package/dist/__tests__/structure-epoch.unit.test.d.ts.map +1 -0
- package/dist/__tests__/table-archetype-mapping.unit.test.d.ts +2 -0
- package/dist/__tests__/table-archetype-mapping.unit.test.d.ts.map +1 -0
- package/dist/__tests__/time.unit.test.d.ts +2 -0
- package/dist/__tests__/time.unit.test.d.ts.map +1 -0
- package/dist/__tests__/types.test-d.d.ts +2 -0
- package/dist/__tests__/types.test-d.d.ts.map +1 -0
- package/dist/__tests__/unique-ref-store.unit.test.d.ts +2 -0
- package/dist/__tests__/unique-ref-store.unit.test.d.ts.map +1 -0
- package/dist/__tests__/utils/handle-numeric.d.ts +14 -0
- package/dist/__tests__/utils/handle-numeric.d.ts.map +1 -0
- package/dist/__tests__/validation-root-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/validation-root-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-atomic-write.unit.test.d.ts +2 -0
- package/dist/__tests__/visibility-atomic-write.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world-change-journal.unit.test.d.ts +2 -0
- package/dist/__tests__/world-change-journal.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world-characterization.unit.test.d.ts +2 -0
- package/dist/__tests__/world-characterization.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world-consumer-inference.test-d.d.ts +2 -0
- package/dist/__tests__/world-consumer-inference.test-d.d.ts.map +1 -0
- package/dist/__tests__/world-health.contract.test.d.ts +2 -0
- package/dist/__tests__/world-health.contract.test.d.ts.map +1 -0
- package/dist/__tests__/world-inspection.test.d.ts +2 -0
- package/dist/__tests__/world-inspection.test.d.ts.map +1 -0
- package/dist/__tests__/world-internal-identity.unit.test.d.ts +2 -0
- package/dist/__tests__/world-internal-identity.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world-managed-roundtrip.unit.test.d.ts +2 -0
- package/dist/__tests__/world-managed-roundtrip.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world-scheduling.m4.test.d.ts +2 -0
- package/dist/__tests__/world-scheduling.m4.test.d.ts.map +1 -0
- package/dist/__tests__/world-unique-roundtrip.unit.test.d.ts +2 -0
- package/dist/__tests__/world-unique-roundtrip.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world.test-d.d.ts +2 -0
- package/dist/__tests__/world.test-d.d.ts.map +1 -0
- package/dist/buffer-pool.d.ts +109 -0
- package/dist/buffer-pool.d.ts.map +1 -0
- package/dist/commands.d.ts +93 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/component-default-fallback.d.ts +88 -0
- package/dist/component-default-fallback.d.ts.map +1 -0
- package/dist/component-schema.d.ts +34 -0
- package/dist/component-schema.d.ts.map +1 -0
- package/dist/component-value-validate.d.ts +29 -0
- package/dist/component-value-validate.d.ts.map +1 -0
- package/dist/component.d.ts +581 -0
- package/dist/component.d.ts.map +1 -0
- package/dist/entity-handle.d.ts +68 -0
- package/dist/entity-handle.d.ts.map +1 -0
- package/dist/entity.d.ts +86 -0
- package/dist/entity.d.ts.map +1 -0
- package/dist/errors/query-and-component-errors.d.ts +132 -0
- package/dist/errors/query-and-component-errors.d.ts.map +1 -0
- package/dist/errors/relationship-errors.d.ts +112 -0
- package/dist/errors/relationship-errors.d.ts.map +1 -0
- package/dist/errors/sprite-and-shared-errors.d.ts +107 -0
- package/dist/errors/sprite-and-shared-errors.d.ts.map +1 -0
- package/dist/errors/validation-errors.d.ts +229 -0
- package/dist/errors/validation-errors.d.ts.map +1 -0
- package/dist/errors.d.ts +763 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/execution/shared-kernel.d.ts +106 -0
- package/dist/execution/shared-kernel.d.ts.map +1 -0
- package/dist/externalization/index.d.ts +31 -0
- package/dist/externalization/index.d.ts.map +1 -0
- package/dist/externalization/index.mjs +298 -0
- package/dist/externalization/index.mjs.map +1 -0
- package/dist/index.d.ts +261 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +7422 -0
- package/dist/index.mjs.map +1 -0
- package/dist/internal.mjs +479 -0
- package/dist/internal.mjs.map +1 -0
- package/dist/plugin-service.d.ts +11 -0
- package/dist/plugin-service.d.ts.map +1 -0
- package/dist/projection/index.d.ts +103 -0
- package/dist/projection/index.d.ts.map +1 -0
- package/dist/projection/index.mjs +615 -0
- package/dist/projection/index.mjs.map +1 -0
- package/dist/query/query.d.ts +58 -0
- package/dist/query/query.d.ts.map +1 -0
- package/dist/relationship-index.d.ts +81 -0
- package/dist/relationship-index.d.ts.map +1 -0
- package/dist/resource.d.ts +70 -0
- package/dist/resource.d.ts.map +1 -0
- package/dist/schedule-token.d.ts +16 -0
- package/dist/schedule-token.d.ts.map +1 -0
- package/dist/schedule.d.ts +271 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/shared-ref-store.d.ts +144 -0
- package/dist/shared-ref-store.d.ts.map +1 -0
- package/dist/shared.d.ts +3 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.mjs +364 -0
- package/dist/shared.mjs.map +1 -0
- package/dist/storage/archetype-graph.d.ts +54 -0
- package/dist/storage/archetype-graph.d.ts.map +1 -0
- package/dist/storage/archetype.d.ts +23 -0
- package/dist/storage/archetype.d.ts.map +1 -0
- package/dist/storage/change-detection.d.ts +40 -0
- package/dist/storage/change-detection.d.ts.map +1 -0
- package/dist/storage/column.d.ts +111 -0
- package/dist/storage/column.d.ts.map +1 -0
- package/dist/storage/table.d.ts +29 -0
- package/dist/storage/table.d.ts.map +1 -0
- package/dist/time.d.ts +57 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/unique-ref-store.d.ts +105 -0
- package/dist/unique-ref-store.d.ts.map +1 -0
- package/dist/world-change-journal.d.ts +34 -0
- package/dist/world-change-journal.d.ts.map +1 -0
- package/dist/world-component-access.d.ts +265 -0
- package/dist/world-component-access.d.ts.map +1 -0
- package/dist/world-component-storage.d.ts +282 -0
- package/dist/world-component-storage.d.ts.map +1 -0
- package/dist/world-core.d.ts +39 -0
- package/dist/world-core.d.ts.map +1 -0
- package/dist/world-entity-lifecycle.d.ts +30 -0
- package/dist/world-entity-lifecycle.d.ts.map +1 -0
- package/dist/world-internal.d.ts +14 -0
- package/dist/world-internal.d.ts.map +1 -0
- package/dist/world-scheduling.d.ts +30 -0
- package/dist/world-scheduling.d.ts.map +1 -0
- package/dist/world.d.ts +620 -0
- package/dist/world.d.ts.map +1 -0
- package/package.json +77 -0
- package/src/__fixtures__/field-shape-fixture.ts +30 -0
- package/src/__tests__/__fixtures__/inspect-scripts.snapshot.ts +2 -0
- package/src/__tests__/archetype.unit.test.ts +1837 -0
- package/src/__tests__/array-handle-element.test-d.ts +34 -0
- package/src/__tests__/buffer-array-vocab.test-d.ts +53 -0
- package/src/__tests__/buffer-field-input-widen.unit.test.ts +99 -0
- package/src/__tests__/buffer-pool.test-d.ts +32 -0
- package/src/__tests__/change-detection.unit.test.ts +86 -0
- package/src/__tests__/command-buffer.test.ts +179 -0
- package/src/__tests__/commands-deferred.unit.test.ts +68 -0
- package/src/__tests__/component-catalog.test.ts +59 -0
- package/src/__tests__/component-definition-identity.unit.test.ts +24 -0
- package/src/__tests__/component-nan-preflight.unit.test.ts +186 -0
- package/src/__tests__/component-schema.test-d.ts +119 -0
- package/src/__tests__/component-schema.test.ts +55 -0
- package/src/__tests__/define-component-options.test-d.ts +35 -0
- package/src/__tests__/deprecated-vocab-rejection.unit.test.ts +59 -0
- package/src/__tests__/despawn-all.unit.test.ts +17 -0
- package/src/__tests__/ecs-core-reduction.characterization.test.ts +130 -0
- package/src/__tests__/ecs-core-reduction.perf.test.ts +260 -0
- package/src/__tests__/ecs-error-code-31.test-d.ts +42 -0
- package/src/__tests__/entity-disabling.unit.test.ts +37 -0
- package/src/__tests__/entity-handle-codec-parity.unit.test.ts +126 -0
- package/src/__tests__/errors.unit.test.ts +1456 -0
- package/src/__tests__/execution-conflict-boundary.unit.test.ts +123 -0
- package/src/__tests__/execution-shared-storage.unit.test.ts +40 -0
- package/src/__tests__/externalization-portable-value.unit.test.ts +275 -0
- package/src/__tests__/externalization-projection.unit.test.ts +325 -0
- package/src/__tests__/externalization-render-read-lease.test-d.ts +30 -0
- package/src/__tests__/externalization-render-read-lease.unit.test.ts +79 -0
- package/src/__tests__/externalization-root-surface.unit.test.ts +33 -0
- package/src/__tests__/field-enum-labels-reflection.test.ts +66 -0
- package/src/__tests__/field-shape-fixture.test.ts +61 -0
- package/src/__tests__/field-transient-reflection.test.ts +59 -0
- package/src/__tests__/field-value-type.test-d.ts +57 -0
- package/src/__tests__/fixed-update.unit.test.ts +78 -0
- package/src/__tests__/handle.test-d.ts +86 -0
- package/src/__tests__/hierarchy.unit.test.ts +2534 -0
- package/src/__tests__/managed-array-vocab.test-d.ts +137 -0
- package/src/__tests__/minimal-example.test-d.ts +42 -0
- package/src/__tests__/name-read-types.test-d.ts +72 -0
- package/src/__tests__/plugin-service.test.ts +13 -0
- package/src/__tests__/query-combinations.unit.test.ts +42 -0
- package/src/__tests__/query-light-extract-flat-column-ratio.perf.test.ts +252 -0
- package/src/__tests__/query-storage-trends.perf.test.ts +104 -0
- package/src/__tests__/query-trs-flat-column-ratio.perf.test.ts +194 -0
- package/src/__tests__/query.unit.test.ts +196 -0
- package/src/__tests__/relationship-index.perf.test.ts +126 -0
- package/src/__tests__/relationship-index.test.ts +70 -0
- package/src/__tests__/scalar-field-type-owner.test-d.ts +71 -0
- package/src/__tests__/schedule-determinism.unit.test.ts +36 -0
- package/src/__tests__/schedule-inspection.unit.test.ts +81 -0
- package/src/__tests__/schedule-scope-error.unit.test.ts +63 -0
- package/src/__tests__/schedule-token.unit.test.ts +54 -0
- package/src/__tests__/schema-vocab.test-d.ts +103 -0
- package/src/__tests__/serialization.unit.test.ts +2740 -0
- package/src/__tests__/shared-kernel-eligibility.test-d.ts +18 -0
- package/src/__tests__/shared-kernel-eligibility.unit.test.ts +78 -0
- package/src/__tests__/shared-ref-store.unit.test.ts +646 -0
- package/src/__tests__/sparse-tag.unit.test.ts +201 -0
- package/src/__tests__/spawn-field-error.test-d.ts +41 -0
- package/src/__tests__/stale-error.test-d.ts +208 -0
- package/src/__tests__/string-view-symmetry.test-d.ts +26 -0
- package/src/__tests__/string-vocab.test-d.ts +49 -0
- package/src/__tests__/structure-epoch-boundaries.unit.test.ts +39 -0
- package/src/__tests__/structure-epoch.unit.test.ts +48 -0
- package/src/__tests__/table-archetype-mapping.unit.test.ts +78 -0
- package/src/__tests__/time.unit.test.ts +79 -0
- package/src/__tests__/types.test-d.ts +38 -0
- package/src/__tests__/unique-ref-store.unit.test.ts +348 -0
- package/src/__tests__/utils/handle-numeric.ts +30 -0
- package/src/__tests__/validation-root-surface.unit.test.ts +15 -0
- package/src/__tests__/visibility-atomic-write.unit.test.ts +82 -0
- package/src/__tests__/world-change-journal.unit.test.ts +97 -0
- package/src/__tests__/world-characterization.unit.test.ts +408 -0
- package/src/__tests__/world-consumer-inference.test-d.ts +16 -0
- package/src/__tests__/world-health.contract.test.ts +87 -0
- package/src/__tests__/world-inspection.test.ts +37 -0
- package/src/__tests__/world-internal-identity.unit.test.ts +17 -0
- package/src/__tests__/world-managed-roundtrip.unit.test.ts +98 -0
- package/src/__tests__/world-scheduling.m4.test.ts +52 -0
- package/src/__tests__/world-unique-roundtrip.unit.test.ts +357 -0
- package/src/__tests__/world.test-d.ts +156 -0
- package/src/buffer-pool.ts +357 -0
- package/src/commands.ts +644 -0
- package/src/component-default-fallback.ts +236 -0
- package/src/component-schema.ts +92 -0
- package/src/component-value-validate.ts +135 -0
- package/src/component.ts +1268 -0
- package/src/entity-handle.ts +104 -0
- package/src/entity.ts +140 -0
- package/src/errors/query-and-component-errors.ts +200 -0
- package/src/errors/relationship-errors.ts +198 -0
- package/src/errors/sprite-and-shared-errors.ts +231 -0
- package/src/errors/validation-errors.ts +506 -0
- package/src/errors.ts +1464 -0
- package/src/execution/shared-kernel.ts +392 -0
- package/src/externalization/index.ts +170 -0
- package/src/index.ts +414 -0
- package/src/plugin-service.ts +35 -0
- package/src/projection/index.ts +318 -0
- package/src/query/query.ts +606 -0
- package/src/relationship-index.ts +175 -0
- package/src/resource.ts +106 -0
- package/src/schedule-token.ts +40 -0
- package/src/schedule.ts +846 -0
- package/src/shared-ref-store.ts +418 -0
- package/src/shared.ts +23 -0
- package/src/storage/archetype-graph.ts +166 -0
- package/src/storage/archetype.ts +78 -0
- package/src/storage/change-detection.ts +231 -0
- package/src/storage/column.ts +247 -0
- package/src/storage/table.ts +153 -0
- package/src/time.ts +122 -0
- package/src/unique-ref-store.ts +278 -0
- package/src/world-change-journal.ts +84 -0
- package/src/world-component-access.ts +1421 -0
- package/src/world-component-storage.ts +1178 -0
- package/src/world-core.ts +74 -0
- package/src/world-entity-lifecycle.ts +539 -0
- package/src/world-internal.ts +75 -0
- package/src/world-scheduling.ts +499 -0
- package/src/world.ts +1399 -0
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
2
|
+
import {
|
|
3
|
+
type Component,
|
|
4
|
+
type ComponentId,
|
|
5
|
+
type ComponentSchema,
|
|
6
|
+
componentId,
|
|
7
|
+
componentSchema,
|
|
8
|
+
isManagedField,
|
|
9
|
+
type ShapeOf,
|
|
10
|
+
type TypedArrayFor,
|
|
11
|
+
} from '../component';
|
|
12
|
+
import { Disabled, Entity } from '../entity';
|
|
13
|
+
import type { EntityHandle } from '../entity-handle';
|
|
14
|
+
import {
|
|
15
|
+
QueryDataRequiresFieldsError,
|
|
16
|
+
QueryDescriptorConflictError,
|
|
17
|
+
QueryIterationActiveError,
|
|
18
|
+
QueryIterationInvalidatedError,
|
|
19
|
+
QuerySpanUnavailableError,
|
|
20
|
+
type QuerySpanUnavailableReason,
|
|
21
|
+
} from '../errors';
|
|
22
|
+
import type { Archetype, ArchetypeId } from '../storage/archetype';
|
|
23
|
+
import type { ArchetypeGraph } from '../storage/archetype-graph';
|
|
24
|
+
import { sparseTagIndex } from '../storage/change-detection';
|
|
25
|
+
import type { FieldView, ManagedColumnReader } from '../storage/column';
|
|
26
|
+
import type { Table } from '../storage/table';
|
|
27
|
+
import { type WorldInternal, worldInternal } from '../world-internal';
|
|
28
|
+
|
|
29
|
+
export interface QueryDescriptor<
|
|
30
|
+
R extends readonly Component[] = readonly Component[],
|
|
31
|
+
W extends readonly Component[] = readonly Component[],
|
|
32
|
+
O extends readonly Component[] = readonly Component[],
|
|
33
|
+
> {
|
|
34
|
+
readonly read?: R;
|
|
35
|
+
readonly write?: W;
|
|
36
|
+
readonly optional?: O;
|
|
37
|
+
readonly with?: readonly Component[];
|
|
38
|
+
readonly without?: readonly Component[];
|
|
39
|
+
readonly changed?: readonly Component[];
|
|
40
|
+
readonly added?: readonly Component[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type ReadonlyRowShape<C> =
|
|
44
|
+
C extends Component<string, infer S> ? Readonly<ShapeOf<S>> : never;
|
|
45
|
+
export type MutableRowShape<C> =
|
|
46
|
+
C extends Component<string, infer S>
|
|
47
|
+
? { -readonly [K in keyof ShapeOf<S>]: ShapeOf<S>[K] }
|
|
48
|
+
: never;
|
|
49
|
+
|
|
50
|
+
type ColumnShapeForSchema<S extends ComponentSchema> = {
|
|
51
|
+
[K in keyof S]: TypedArrayFor<S[K]>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type ReadonlyColumnShape<C> =
|
|
55
|
+
C extends Component<string, infer S> ? Readonly<ColumnShapeForSchema<S>> : never;
|
|
56
|
+
export type MutableColumnShape<C> =
|
|
57
|
+
C extends Component<string, infer S>
|
|
58
|
+
? { -readonly [K in keyof ColumnShapeForSchema<S>]: ColumnShapeForSchema<S>[K] }
|
|
59
|
+
: never;
|
|
60
|
+
|
|
61
|
+
export interface QueryRow<
|
|
62
|
+
R extends readonly Component[] = readonly Component[],
|
|
63
|
+
W extends readonly Component[] = readonly Component[],
|
|
64
|
+
O extends readonly Component[] = readonly Component[],
|
|
65
|
+
> {
|
|
66
|
+
readonly entity: EntityHandle;
|
|
67
|
+
/** Test component presence without materialising its row object. */
|
|
68
|
+
has(component: R[number] | W[number] | O[number]): boolean;
|
|
69
|
+
get<C extends R[number]>(component: C): ReadonlyRowShape<C>;
|
|
70
|
+
get<C extends O[number]>(component: C): ReadonlyRowShape<C> | undefined;
|
|
71
|
+
mut<C extends W[number]>(component: C): MutableRowShape<C>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface QuerySpan<
|
|
75
|
+
R extends readonly Component[] = readonly Component[],
|
|
76
|
+
W extends readonly Component[] = readonly Component[],
|
|
77
|
+
> {
|
|
78
|
+
/** Packed entity identities for this contiguous table range. */
|
|
79
|
+
readonly entities: Readonly<Uint32Array>;
|
|
80
|
+
readonly length: number;
|
|
81
|
+
get<C extends R[number]>(component: C): ReadonlyColumnShape<C>;
|
|
82
|
+
mut<C extends W[number]>(component: C): MutableColumnShape<C>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface Query<
|
|
86
|
+
R extends readonly Component[] = readonly Component[],
|
|
87
|
+
W extends readonly Component[] = readonly Component[],
|
|
88
|
+
O extends readonly Component[] = readonly Component[],
|
|
89
|
+
> extends Iterable<QueryRow<R, W, O>> {
|
|
90
|
+
/**
|
|
91
|
+
* Read one known entity without iterating the query's matching tables.
|
|
92
|
+
* Structural descriptor constraints are applied; temporal `changed` and
|
|
93
|
+
* `added` filters are intentionally not evaluated because this is an
|
|
94
|
+
* identity-addressed read, not a journal drain.
|
|
95
|
+
*/
|
|
96
|
+
at(entity: EntityHandle): QueryRow<R, W, O> | undefined;
|
|
97
|
+
spans(): Result<Iterable<QuerySpan<R, W>>, QuerySpanUnavailableError>;
|
|
98
|
+
combinations(k?: number): Iterable<readonly QueryRow<R, W, O>[]>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type QueryCreationError = QueryDescriptorConflictError | QueryDataRequiresFieldsError;
|
|
102
|
+
|
|
103
|
+
interface WorldQueryAccess {
|
|
104
|
+
readonly [worldInternal]: WorldInternal;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface CompiledDescriptor<
|
|
108
|
+
R extends readonly Component[],
|
|
109
|
+
W extends readonly Component[],
|
|
110
|
+
O extends readonly Component[],
|
|
111
|
+
> {
|
|
112
|
+
readonly descriptor: QueryDescriptor<R, W, O>;
|
|
113
|
+
readonly requiredIds: readonly ComponentId[];
|
|
114
|
+
readonly withoutIds: readonly ComponentId[];
|
|
115
|
+
readonly changedIds: readonly ComponentId[];
|
|
116
|
+
readonly addedIds: readonly ComponentId[];
|
|
117
|
+
readonly sparseRoute: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
type Role = 'read' | 'write' | 'optional' | 'with' | 'without';
|
|
121
|
+
|
|
122
|
+
function compileDescriptor<
|
|
123
|
+
const R extends readonly Component[],
|
|
124
|
+
const W extends readonly Component[],
|
|
125
|
+
const O extends readonly Component[],
|
|
126
|
+
>(descriptor: QueryDescriptor<R, W, O>): Result<CompiledDescriptor<R, W, O>, QueryCreationError> {
|
|
127
|
+
const read = descriptor.read ?? ([] as unknown as R);
|
|
128
|
+
const write = descriptor.write ?? ([] as unknown as W);
|
|
129
|
+
const optional = descriptor.optional ?? ([] as unknown as O);
|
|
130
|
+
const withComponents = descriptor.with ?? [];
|
|
131
|
+
const withoutComponents = [...(descriptor.without ?? [])];
|
|
132
|
+
const roles = new Map<Component, Role[]>();
|
|
133
|
+
const addRole = (component: Component, role: Role): void => {
|
|
134
|
+
const current = roles.get(component);
|
|
135
|
+
if (current === undefined) roles.set(component, [role]);
|
|
136
|
+
else current.push(role);
|
|
137
|
+
};
|
|
138
|
+
for (const component of read) addRole(component, 'read');
|
|
139
|
+
for (const component of write) addRole(component, 'write');
|
|
140
|
+
for (const component of optional) addRole(component, 'optional');
|
|
141
|
+
for (const component of withComponents) addRole(component, 'with');
|
|
142
|
+
for (const component of withoutComponents) addRole(component, 'without');
|
|
143
|
+
|
|
144
|
+
for (const [component, componentRoles] of roles) {
|
|
145
|
+
if (componentRoles.length > 1) {
|
|
146
|
+
return err(new QueryDescriptorConflictError(component.name, componentRoles));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const component of [...read, ...write, ...optional]) {
|
|
150
|
+
if (Object.keys(componentSchema(component)).length === 0) {
|
|
151
|
+
return err(new QueryDataRequiresFieldsError(component.name));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const required = [
|
|
156
|
+
...read,
|
|
157
|
+
...write,
|
|
158
|
+
...withComponents,
|
|
159
|
+
...(descriptor.changed ?? []),
|
|
160
|
+
...(descriptor.added ?? []),
|
|
161
|
+
];
|
|
162
|
+
const requiredIds = [...new Set(required.map((component) => componentId(component)))];
|
|
163
|
+
const withoutIds = [...new Set(withoutComponents.map((component) => componentId(component)))];
|
|
164
|
+
if (!requiredIds.includes(componentId(Disabled)) && !withoutIds.includes(componentId(Disabled))) {
|
|
165
|
+
withoutIds.push(componentId(Disabled));
|
|
166
|
+
}
|
|
167
|
+
return ok({
|
|
168
|
+
descriptor,
|
|
169
|
+
requiredIds,
|
|
170
|
+
withoutIds,
|
|
171
|
+
changedIds: [...new Set((descriptor.changed ?? []).map((component) => componentId(component)))],
|
|
172
|
+
addedIds: [...new Set((descriptor.added ?? []).map((component) => componentId(component)))],
|
|
173
|
+
sparseRoute: [
|
|
174
|
+
...required,
|
|
175
|
+
...withoutComponents,
|
|
176
|
+
...(descriptor.changed ?? []),
|
|
177
|
+
...(descriptor.added ?? []),
|
|
178
|
+
].some((component) => component.storage === 'sparse'),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
class QueryRowFacade<
|
|
183
|
+
R extends readonly Component[],
|
|
184
|
+
W extends readonly Component[],
|
|
185
|
+
O extends readonly Component[],
|
|
186
|
+
> implements QueryRow<R, W, O>
|
|
187
|
+
{
|
|
188
|
+
entity = 0 as EntityHandle;
|
|
189
|
+
private archetype: Archetype | undefined;
|
|
190
|
+
|
|
191
|
+
constructor(private readonly world: WorldQueryAccess) {}
|
|
192
|
+
|
|
193
|
+
bind(entity: EntityHandle, archetype: Archetype): this {
|
|
194
|
+
this.entity = entity;
|
|
195
|
+
this.archetype = archetype;
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
snapshot(): QueryRowFacade<R, W, O> {
|
|
200
|
+
if (this.archetype === undefined) throw new Error('Query row is not bound.');
|
|
201
|
+
return new QueryRowFacade<R, W, O>(this.world).bind(this.entity, this.archetype);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
has(component: R[number] | W[number] | O[number]): boolean {
|
|
205
|
+
return (
|
|
206
|
+
this.archetype?.components.some(
|
|
207
|
+
(candidate) => componentId(candidate) === componentId(component),
|
|
208
|
+
) === true
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
get<C extends R[number]>(component: C): ReadonlyRowShape<C>;
|
|
213
|
+
get<C extends O[number]>(component: C): ReadonlyRowShape<C> | undefined;
|
|
214
|
+
get(component: Component): Record<string, unknown> | undefined {
|
|
215
|
+
if (
|
|
216
|
+
!this.archetype?.components.some(
|
|
217
|
+
(candidate) => componentId(candidate) === componentId(component),
|
|
218
|
+
)
|
|
219
|
+
) {
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
const result = this.world[worldInternal].getQueryRow(this.entity, component);
|
|
223
|
+
if (!result.ok) throw result.error;
|
|
224
|
+
return result.value;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
mut<C extends W[number]>(component: C): MutableRowShape<C> {
|
|
228
|
+
const current = this.get(component);
|
|
229
|
+
if (current === undefined) throw new Error(`Query row lacks ${component.name}.`);
|
|
230
|
+
this.world[worldInternal].markComponentChanged(this.entity, componentId(component));
|
|
231
|
+
return new Proxy(current, {
|
|
232
|
+
set: (target, property, value) => {
|
|
233
|
+
if (typeof property !== 'string') return false;
|
|
234
|
+
const result = this.world[worldInternal].setQueryRow(this.entity, component, {
|
|
235
|
+
[property]: value,
|
|
236
|
+
});
|
|
237
|
+
if (!result.ok) throw result.error;
|
|
238
|
+
(target as Record<string, unknown>)[property] = value;
|
|
239
|
+
return true;
|
|
240
|
+
},
|
|
241
|
+
}) as MutableRowShape<C>;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
class QuerySpanFacade<R extends readonly Component[], W extends readonly Component[]>
|
|
246
|
+
implements QuerySpan<R, W>
|
|
247
|
+
{
|
|
248
|
+
readonly entities: Readonly<Uint32Array>;
|
|
249
|
+
|
|
250
|
+
constructor(
|
|
251
|
+
private readonly world: WorldQueryAccess,
|
|
252
|
+
private readonly table: Table,
|
|
253
|
+
private readonly rowStart: number,
|
|
254
|
+
readonly length: number,
|
|
255
|
+
) {
|
|
256
|
+
const entityColumn = table.storage.get(componentId(Entity))?.fields.get('self');
|
|
257
|
+
this.entities = (entityColumn?.view.subarray(rowStart, rowStart + length) ??
|
|
258
|
+
new Uint32Array(0)) as Readonly<Uint32Array>;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
get<C extends R[number]>(component: C): ReadonlyColumnShape<C> {
|
|
262
|
+
return buildColumnShape(
|
|
263
|
+
this.table,
|
|
264
|
+
component,
|
|
265
|
+
this.rowStart,
|
|
266
|
+
this.length,
|
|
267
|
+
) as ReadonlyColumnShape<C>;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
mut<C extends W[number]>(component: C): MutableColumnShape<C> {
|
|
271
|
+
this.world[worldInternal].markComponentRangeChanged(
|
|
272
|
+
this.table,
|
|
273
|
+
componentId(component),
|
|
274
|
+
this.rowStart,
|
|
275
|
+
this.length,
|
|
276
|
+
);
|
|
277
|
+
return buildColumnShape(
|
|
278
|
+
this.table,
|
|
279
|
+
component,
|
|
280
|
+
this.rowStart,
|
|
281
|
+
this.length,
|
|
282
|
+
) as MutableColumnShape<C>;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function makeManagedColumnReader(
|
|
287
|
+
view: FieldView,
|
|
288
|
+
length: number,
|
|
289
|
+
fieldType: string,
|
|
290
|
+
): ManagedColumnReader<string> {
|
|
291
|
+
const slots = view.subarray(0, length);
|
|
292
|
+
return Object.freeze({
|
|
293
|
+
length,
|
|
294
|
+
get(i: number): number {
|
|
295
|
+
return slots[i] ?? 0;
|
|
296
|
+
},
|
|
297
|
+
__managed: fieldType,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function buildColumnShape(
|
|
302
|
+
table: Table,
|
|
303
|
+
component: Component,
|
|
304
|
+
rowStart: number,
|
|
305
|
+
rowCount: number,
|
|
306
|
+
): Record<string, FieldView | ManagedColumnReader<string>> {
|
|
307
|
+
const shape: Record<string, FieldView | ManagedColumnReader<string>> = {};
|
|
308
|
+
const fields = table.storage.get(componentId(component))?.fields;
|
|
309
|
+
if (fields === undefined) return shape;
|
|
310
|
+
for (const [fieldName, column] of fields) {
|
|
311
|
+
const start = rowStart * column.arity;
|
|
312
|
+
const end = start + rowCount * column.arity;
|
|
313
|
+
const view = column.view.subarray(start, end);
|
|
314
|
+
const fieldType = componentSchema(component)[fieldName];
|
|
315
|
+
shape[fieldName] =
|
|
316
|
+
fieldType !== undefined && isManagedField(fieldType)
|
|
317
|
+
? makeManagedColumnReader(view, view.length, fieldType)
|
|
318
|
+
: view;
|
|
319
|
+
}
|
|
320
|
+
return shape;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
class ExecutableQuery<
|
|
324
|
+
R extends readonly Component[],
|
|
325
|
+
W extends readonly Component[],
|
|
326
|
+
O extends readonly Component[],
|
|
327
|
+
> implements Query<R, W, O>
|
|
328
|
+
{
|
|
329
|
+
private matchedArchetypes: ArchetypeId[] = [];
|
|
330
|
+
private matchedTables: number[] = [];
|
|
331
|
+
private lastGraphGeneration = -1;
|
|
332
|
+
private lastObservedEpoch = 0;
|
|
333
|
+
private active = false;
|
|
334
|
+
|
|
335
|
+
constructor(
|
|
336
|
+
private readonly world: WorldQueryAccess,
|
|
337
|
+
private readonly compiled: CompiledDescriptor<R, W, O>,
|
|
338
|
+
) {}
|
|
339
|
+
|
|
340
|
+
[Symbol.iterator](): Iterator<QueryRow<R, W, O>> {
|
|
341
|
+
this.beginIteration();
|
|
342
|
+
this.refreshMatches();
|
|
343
|
+
const structureEpoch = this.world[worldInternal].getStructureEpoch();
|
|
344
|
+
const upperBound = this.world[worldInternal].getMutationEpoch();
|
|
345
|
+
const row = new QueryRowFacade<R, W, O>(this.world);
|
|
346
|
+
let archetypeIndex = 0;
|
|
347
|
+
let tableIndex = 0;
|
|
348
|
+
let rowIndex = 0;
|
|
349
|
+
let finished = false;
|
|
350
|
+
|
|
351
|
+
const close = (commit: boolean): void => {
|
|
352
|
+
if (finished) return;
|
|
353
|
+
finished = true;
|
|
354
|
+
this.active = false;
|
|
355
|
+
if (commit) this.lastObservedEpoch = upperBound;
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
next: (): IteratorResult<QueryRow<R, W, O>> => {
|
|
360
|
+
if (finished) return { done: true, value: undefined };
|
|
361
|
+
if (this.world[worldInternal].getStructureEpoch() !== structureEpoch) {
|
|
362
|
+
close(false);
|
|
363
|
+
throw new QueryIterationInvalidatedError(
|
|
364
|
+
structureEpoch,
|
|
365
|
+
this.world[worldInternal].getStructureEpoch(),
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
if (!this.compiled.sparseRoute) {
|
|
369
|
+
while (tableIndex < this.matchedTables.length) {
|
|
370
|
+
const table =
|
|
371
|
+
this.world[worldInternal].getGraph().tables[this.matchedTables[tableIndex] ?? -1];
|
|
372
|
+
if (table === undefined || rowIndex >= table.size) {
|
|
373
|
+
tableIndex += 1;
|
|
374
|
+
rowIndex = 0;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
const currentTableRow = rowIndex++;
|
|
378
|
+
const entityColumn = table.storage.get(componentId(Entity))?.fields.get('self');
|
|
379
|
+
if (entityColumn === undefined) continue;
|
|
380
|
+
const entity = (entityColumn.view[currentTableRow] ?? 0) as EntityHandle;
|
|
381
|
+
if (!this.changeMatches(entity, table, currentTableRow, upperBound)) continue;
|
|
382
|
+
const recordArchetype = this.world[worldInternal].getEntityArchetype(entity);
|
|
383
|
+
if (recordArchetype === undefined) continue;
|
|
384
|
+
return { done: false, value: row.bind(entity, recordArchetype) };
|
|
385
|
+
}
|
|
386
|
+
close(true);
|
|
387
|
+
return { done: true, value: undefined };
|
|
388
|
+
}
|
|
389
|
+
while (archetypeIndex < this.matchedArchetypes.length) {
|
|
390
|
+
const archetype =
|
|
391
|
+
this.world[worldInternal].getGraph().archetypes[
|
|
392
|
+
this.matchedArchetypes[archetypeIndex] ?? -1
|
|
393
|
+
];
|
|
394
|
+
if (archetype === undefined || rowIndex >= archetype.size) {
|
|
395
|
+
archetypeIndex += 1;
|
|
396
|
+
rowIndex = 0;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const currentArchetypeRow = rowIndex++;
|
|
400
|
+
const currentTableRow = archetype.rows[currentArchetypeRow] ?? 0;
|
|
401
|
+
const table = this.world[worldInternal].getGraph().tables[archetype.tableId];
|
|
402
|
+
if (table === undefined) continue;
|
|
403
|
+
const entityColumn = table.storage.get(componentId(Entity))?.fields.get('self');
|
|
404
|
+
if (entityColumn === undefined) continue;
|
|
405
|
+
const entity = (entityColumn.view[currentTableRow] ?? 0) as EntityHandle;
|
|
406
|
+
if (!this.changeMatches(entity, table, currentTableRow, upperBound)) continue;
|
|
407
|
+
return { done: false, value: row.bind(entity, archetype) };
|
|
408
|
+
}
|
|
409
|
+
close(true);
|
|
410
|
+
return { done: true, value: undefined };
|
|
411
|
+
},
|
|
412
|
+
return: (): IteratorResult<QueryRow<R, W, O>> => {
|
|
413
|
+
close(false);
|
|
414
|
+
return { done: true, value: undefined };
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
at(entity: EntityHandle): QueryRow<R, W, O> | undefined {
|
|
420
|
+
const archetype = this.world[worldInternal].getEntityArchetype(entity);
|
|
421
|
+
if (archetype === undefined || !this.archetypeMatches(archetype)) return undefined;
|
|
422
|
+
return new QueryRowFacade<R, W, O>(this.world).bind(entity, archetype).snapshot();
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
spans(): Result<Iterable<QuerySpan<R, W>>, QuerySpanUnavailableError> {
|
|
426
|
+
const reason = this.spanUnavailableReason();
|
|
427
|
+
if (reason !== undefined) return err(new QuerySpanUnavailableError(reason));
|
|
428
|
+
const query = this;
|
|
429
|
+
return ok({
|
|
430
|
+
[Symbol.iterator](): Iterator<QuerySpan<R, W>> {
|
|
431
|
+
query.beginIteration();
|
|
432
|
+
query.refreshMatches();
|
|
433
|
+
const structureEpoch = query.world[worldInternal].getStructureEpoch();
|
|
434
|
+
const upperBound = query.world[worldInternal].getMutationEpoch();
|
|
435
|
+
let index = 0;
|
|
436
|
+
let finished = false;
|
|
437
|
+
const close = (commit: boolean): void => {
|
|
438
|
+
if (finished) return;
|
|
439
|
+
finished = true;
|
|
440
|
+
query.active = false;
|
|
441
|
+
if (commit) query.lastObservedEpoch = upperBound;
|
|
442
|
+
};
|
|
443
|
+
return {
|
|
444
|
+
next(): IteratorResult<QuerySpan<R, W>> {
|
|
445
|
+
if (finished) return { done: true, value: undefined };
|
|
446
|
+
if (query.world[worldInternal].getStructureEpoch() !== structureEpoch) {
|
|
447
|
+
close(false);
|
|
448
|
+
throw new QueryIterationInvalidatedError(
|
|
449
|
+
structureEpoch,
|
|
450
|
+
query.world[worldInternal].getStructureEpoch(),
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
while (index < query.matchedTables.length) {
|
|
454
|
+
const table =
|
|
455
|
+
query.world[worldInternal].getGraph().tables[query.matchedTables[index++] ?? -1];
|
|
456
|
+
if (table === undefined || table.size === 0) continue;
|
|
457
|
+
return {
|
|
458
|
+
done: false,
|
|
459
|
+
value: new QuerySpanFacade<R, W>(query.world, table, 0, table.size),
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
close(true);
|
|
463
|
+
return { done: true, value: undefined };
|
|
464
|
+
},
|
|
465
|
+
return(): IteratorResult<QuerySpan<R, W>> {
|
|
466
|
+
close(false);
|
|
467
|
+
return { done: true, value: undefined };
|
|
468
|
+
},
|
|
469
|
+
};
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
combinations(k = 2): Iterable<readonly QueryRow<R, W, O>[]> {
|
|
475
|
+
const query = this;
|
|
476
|
+
return {
|
|
477
|
+
*[Symbol.iterator](): Iterator<readonly QueryRow<R, W, O>[]> {
|
|
478
|
+
if (!Number.isInteger(k) || k < 1) return;
|
|
479
|
+
const previousObservedEpoch = query.lastObservedEpoch;
|
|
480
|
+
const structureEpoch = query.world[worldInternal].getStructureEpoch();
|
|
481
|
+
const rows: QueryRow<R, W, O>[] = [];
|
|
482
|
+
for (const row of query) {
|
|
483
|
+
rows.push((row as QueryRowFacade<R, W, O>).snapshot());
|
|
484
|
+
}
|
|
485
|
+
query.active = true;
|
|
486
|
+
let completed = false;
|
|
487
|
+
try {
|
|
488
|
+
if (k <= rows.length) {
|
|
489
|
+
const indices = Array.from({ length: k }, (_, index) => index);
|
|
490
|
+
while (true) {
|
|
491
|
+
if (query.world[worldInternal].getStructureEpoch() !== structureEpoch) {
|
|
492
|
+
throw new QueryIterationInvalidatedError(
|
|
493
|
+
structureEpoch,
|
|
494
|
+
query.world[worldInternal].getStructureEpoch(),
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
yield indices.map((index) => rows[index] as QueryRow<R, W, O>);
|
|
498
|
+
let pivot = k - 1;
|
|
499
|
+
while (pivot >= 0 && (indices[pivot] ?? 0) === rows.length - k + pivot) pivot -= 1;
|
|
500
|
+
if (pivot < 0) break;
|
|
501
|
+
indices[pivot] = (indices[pivot] ?? 0) + 1;
|
|
502
|
+
for (let index = pivot + 1; index < k; index++) {
|
|
503
|
+
indices[index] = (indices[index - 1] ?? 0) + 1;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
completed = true;
|
|
508
|
+
} finally {
|
|
509
|
+
query.active = false;
|
|
510
|
+
if (!completed) query.lastObservedEpoch = previousObservedEpoch;
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
private beginIteration(): void {
|
|
517
|
+
if (this.active) throw new QueryIterationActiveError();
|
|
518
|
+
this.active = true;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
private refreshMatches(): void {
|
|
522
|
+
const graph = this.world[worldInternal].getGraph() as ArchetypeGraph;
|
|
523
|
+
if (this.lastGraphGeneration === graph.generation) return;
|
|
524
|
+
this.matchedArchetypes = graph.archetypes
|
|
525
|
+
.filter((archetype) => this.archetypeMatches(archetype))
|
|
526
|
+
.map((archetype) => archetype.id);
|
|
527
|
+
this.matchedTables = graph.tables
|
|
528
|
+
.filter((table) => this.tableMatches(table))
|
|
529
|
+
.map((table) => table.id);
|
|
530
|
+
this.lastGraphGeneration = graph.generation;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
private archetypeMatches(archetype: Archetype): boolean {
|
|
534
|
+
for (const requiredId of this.compiled.requiredIds) {
|
|
535
|
+
if (!archetype.components.some((component) => componentId(component) === requiredId))
|
|
536
|
+
return false;
|
|
537
|
+
}
|
|
538
|
+
for (const excludedId of this.compiled.withoutIds) {
|
|
539
|
+
if (archetype.components.some((component) => componentId(component) === excludedId))
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
private tableMatches(table: Table): boolean {
|
|
546
|
+
for (const componentId of this.compiled.requiredIds) {
|
|
547
|
+
if (!table.storage.has(componentId)) return false;
|
|
548
|
+
}
|
|
549
|
+
for (const componentId of this.compiled.withoutIds) {
|
|
550
|
+
if (table.storage.has(componentId)) return false;
|
|
551
|
+
}
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
private changeMatches(
|
|
556
|
+
entity: EntityHandle,
|
|
557
|
+
table: Table,
|
|
558
|
+
row: number,
|
|
559
|
+
upperBound: number,
|
|
560
|
+
): boolean {
|
|
561
|
+
const graph = this.world[worldInternal].getGraph();
|
|
562
|
+
for (const componentId of this.compiled.changedIds) {
|
|
563
|
+
const sparseSet = graph.sparseTags.get(componentId);
|
|
564
|
+
const denseIndex = sparseSet === undefined ? -1 : sparseTagIndex(sparseSet, entity);
|
|
565
|
+
const epoch =
|
|
566
|
+
denseIndex >= 0
|
|
567
|
+
? (sparseSet?.changed[denseIndex] ?? 0)
|
|
568
|
+
: (table.storage.get(componentId)?.epochs.changed[row] ?? 0);
|
|
569
|
+
if (epoch <= this.lastObservedEpoch || epoch > upperBound) return false;
|
|
570
|
+
}
|
|
571
|
+
for (const componentId of this.compiled.addedIds) {
|
|
572
|
+
const sparseSet = graph.sparseTags.get(componentId);
|
|
573
|
+
const denseIndex = sparseSet === undefined ? -1 : sparseTagIndex(sparseSet, entity);
|
|
574
|
+
const epoch =
|
|
575
|
+
denseIndex >= 0
|
|
576
|
+
? (sparseSet?.added[denseIndex] ?? 0)
|
|
577
|
+
: (table.storage.get(componentId)?.epochs.added[row] ?? 0);
|
|
578
|
+
if (epoch <= this.lastObservedEpoch || epoch > upperBound) return false;
|
|
579
|
+
}
|
|
580
|
+
return true;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
private spanUnavailableReason(): QuerySpanUnavailableReason | undefined {
|
|
584
|
+
if (this.compiled.sparseRoute) return 'sparse-component';
|
|
585
|
+
if ((this.compiled.descriptor.optional?.length ?? 0) > 0) return 'optional-data';
|
|
586
|
+
if (
|
|
587
|
+
(this.compiled.descriptor.changed?.length ?? 0) > 0 ||
|
|
588
|
+
(this.compiled.descriptor.added?.length ?? 0) > 0
|
|
589
|
+
)
|
|
590
|
+
return 'row-change-filter';
|
|
591
|
+
return undefined;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
export function createQuery<
|
|
596
|
+
const R extends readonly Component[] = readonly [],
|
|
597
|
+
const W extends readonly Component[] = readonly [],
|
|
598
|
+
const O extends readonly Component[] = readonly [],
|
|
599
|
+
>(
|
|
600
|
+
world: WorldQueryAccess,
|
|
601
|
+
descriptor: QueryDescriptor<R, W, O>,
|
|
602
|
+
): Result<Query<R, W, O>, QueryCreationError> {
|
|
603
|
+
const compiled = compileDescriptor(descriptor);
|
|
604
|
+
if (!compiled.ok) return compiled;
|
|
605
|
+
return ok(new ExecutableQuery(world, compiled.value));
|
|
606
|
+
}
|