Entities · class

ComponentStore

Explained in Entities.

class ComponentStore
import { ComponentStore } from '@driftengine/entities';

Constructor

new

constructor(type: ComponentType, options?: ComponentStoreOptions)
ParameterTypeDescription
typeComponentType
options?ComponentStoreOptions

Properties

NameTypeDescription
typereadonlyComponentType

Accessors

NameTypeDescription
sizegetnumber
densegetFloat64ArrayThe entities this store holds, oldest first. Valid up to size; the rest is capacity.

Methods

view

view(): ComponentView

The live columns of this store, addressable by field name.

More

The same object every call, refreshed after every reallocation — see view.ts for why that is the contract rather than a convenience.

column

column(field: string): NumericColumn | unknown[]

One field's storage, for a caller iterating it directly rather than through read.

ParameterTypeDescription
fieldstring

has

has(entity: Entity): boolean
ParameterTypeDescription
entityEntity

add

add(entity: Entity, values?: Readonly<Record<string, unknown>>): void
ParameterTypeDescription
entityEntity
values?Readonly<Record<string, unknown>>

remove

remove(entity: Entity): boolean

Drop an entity's component. false when it never had one, which is not an error.

ParameterTypeDescription
entityEntity
More

The last entry is swapped into the hole and its sparse entry is fixed — the fix-up being the whole of what makes a constant-time removal correct.

The order of the two sparse writes is load-bearing, and it is the only thing that is. When the entry being removed is the last, the swap is with itself and moved is entity: writing the position first and then the -1 leaves it absent, which is right; the other way round leaves it pointing into the space past size.

There was a position < size guard in positionOf as well, which made the order not matter — and made a perturbation of the order pass every test. Two defences for one case is one defence and one comment that cannot be checked, so the guard is gone and the order is what the test for removing the last entry actually holds.

read

read(entity: Entity, field: string): unknown
ParameterTypeDescription
entityEntity
fieldstring

write

write(entity: Entity, field: string, value: unknown): void
ParameterTypeDescription
entityEntity
fieldstring
valueunknown

saveInto

saveInto(slot: StoreSnapshot): void

Copy this store's contents into a slot, growing the slot to match if it is short.

ParameterTypeDescription
slotStoreSnapshot
More

count bounds every copy, not capacity. A store that once held ten thousand entities and now holds four has ten thousand slots of stale numbers behind the live four, and copying them would make a snapshot's cost depend on the world's history instead of its size.

A boxed column copies references. That is correct rather than a shortcut: the one boxed field type is String, and a string cannot be mutated behind the snapshot's back.

loadFrom

loadFrom(slot: StoreSnapshot): void

Put this store back to a saved slot, rebuilding sparse from the restored dense.

ParameterTypeDescription
slotStoreSnapshot
More

The order of the two sparse passes is load-bearing. The entries this store's current live set occupies are cleared first, while dense still says which they are; overwriting dense first loses that record and leaves stale positions behind, and a stale position is worse than a missing one because positionOf would find a live slot holding a different handle.