Comparing version 0.2.22 to 0.2.23
@@ -112,2 +112,10 @@ const TYPES_ENUM = { | ||
}; | ||
const resetStoreFor = (store, eid) => { | ||
store[$storeFlattened].forEach(ta => { | ||
ta[eid] = 0; | ||
}); | ||
Object.keys(store[$storeSubarrays]).forEach(key => { | ||
store[$storeSubarrays][key][eid].fill(0); | ||
}); | ||
}; | ||
@@ -169,3 +177,2 @@ const createTypeStore = (type, length) => { | ||
const $store = Symbol('store'); | ||
schema = JSON.parse(JSON.stringify(schema)); | ||
@@ -177,2 +184,4 @@ if (schema.constructor.name === 'Map') { | ||
schema = JSON.parse(JSON.stringify(schema)); | ||
const collectArrayCount = (count, key) => { | ||
@@ -188,3 +197,3 @@ if (isArrayType(schema[key])) { | ||
const arrayCount = isArrayType(schema) ? 1 : Object.keys(schema).reduce(collectArrayCount, 0); | ||
const arrayCount = Object.keys(schema).reduce(collectArrayCount, 0); | ||
const metadata = { | ||
@@ -241,7 +250,10 @@ [$storeSize]: size, | ||
const $entityEnabled = Symbol('entityEnabled'); | ||
const $deferredEntityRemovals = Symbol('deferredEntityRemovals'); | ||
const $removedEntities = Symbol('removedEntities'); // need a global EID cursor which all worlds and all components know about | ||
const $entityArray = Symbol('entityArray'); | ||
const $entityIndices = Symbol('entityIndices'); | ||
const NONE = 2 ** 32; // need a global EID cursor which all worlds and all components know about | ||
// so that world entities can posess entire rows spanning all component tables | ||
let globalEntityCursor = 0; | ||
let globalEntityCursor = 0; // removed eids should also be global to prevent memory leaks | ||
const removed = []; | ||
const getEntityCursor = () => globalEntityCursor; | ||
@@ -258,2 +270,3 @@ const resizeWorld = (world, size) => { | ||
world[$entityEnabled] = resize(world[$entityEnabled], size); | ||
world[$entityIndices] = resize(world[$entityIndices], size); | ||
@@ -266,10 +279,10 @@ for (let i = 0; i < world[$entityMasks].length; i++) { | ||
const addEntity = world => { | ||
const removed = world[$removedEntities]; | ||
const size = world[$size]; | ||
const enabled = world[$entityEnabled]; // if data stores are 80% full | ||
if (globalEntityCursor >= size - size / 5) { | ||
if (globalEntityCursor >= world[$warningSize]) { | ||
// grow by half the original size rounded up to a multiple of 4 | ||
const size = world[$size]; | ||
const amount = Math.ceil(size / 2 / 4) * 4; | ||
resizeWorld(world, size + amount); | ||
world[$warningSize] = world[$size] - world[$size] / 5; | ||
} | ||
@@ -280,7 +293,6 @@ | ||
globalEntityCursor++; | ||
world[$entityIndices][eid] = world[$entityArray].push(eid) - 1; | ||
return eid; | ||
}; | ||
const removeEntity = (world, eid) => { | ||
const queries = world[$queries]; | ||
const removed = world[$removedEntities]; | ||
const enabled = world[$entityEnabled]; // Check if entity is already removed | ||
@@ -291,3 +303,3 @@ | ||
queries.forEach(query => { | ||
world[$queries].forEach(query => { | ||
queryRemoveEntity(world, query, eid); | ||
@@ -297,4 +309,14 @@ }); // Free the entity | ||
removed.push(eid); | ||
enabled[eid] = 0; // Clear entity bitmasks | ||
enabled[eid] = 0; // pop swap | ||
const index = world[$entityIndices][eid]; | ||
const swapped = world[$entityArray].pop(); | ||
if (swapped !== eid) { | ||
world[$entityArray][index] = swapped; | ||
world[$entityIndices][swapped] = index; | ||
} | ||
world[$entityIndices][eid] = NONE; // Clear entity bitmasks | ||
for (let i = 0; i < world[$entityMasks].length; i++) world[$entityMasks][i][eid] = 0; | ||
@@ -337,3 +359,3 @@ }; | ||
const canonicalize = target => { | ||
let componentProps; | ||
let componentProps = []; | ||
let changedProps = new Set(); | ||
@@ -353,4 +375,4 @@ | ||
} else { | ||
target[$componentMap].forEach(c => { | ||
componentProps = componentProps.concat(c[$storeFlattened]); | ||
target[$componentMap].forEach((c, component) => { | ||
componentProps = componentProps.concat(component[$storeFlattened]); | ||
}); | ||
@@ -368,2 +390,3 @@ } | ||
return ents => { | ||
if (Object.getOwnPropertySymbols(ents).includes($entityArray)) ents = ents[$entityArray]; | ||
if (!ents.length) return; | ||
@@ -402,3 +425,3 @@ let where = 0; // iterate over component props | ||
where += 1; | ||
let count2 = 0; // write array values | ||
let count2 = 0; // write index,value | ||
@@ -419,4 +442,5 @@ for (let i = 0; i < prop[eid].length; i++) { | ||
count2++; | ||
} | ||
} // write total element count | ||
view[`set${indexType}`](countWhere2, count2); | ||
@@ -507,3 +531,3 @@ } else { | ||
const $queryComponents = Symbol('queryComponents'); | ||
const NONE = 2 ** 32; | ||
const NONE$1 = 2 ** 32; | ||
const enterQuery = (world, query, fn) => { | ||
@@ -518,3 +542,2 @@ if (!world[$queryMap].has(query)) registerQuery(world, query); | ||
const registerQuery = (world, query) => { | ||
if (!world[$queryMap].has(query)) world[$queryMap].set(query, {}); | ||
let components = []; | ||
@@ -540,6 +563,6 @@ let notComponents = []; | ||
const size = components.reduce((a, c) => c[$storeSize] > a ? c[$storeSize] : a, 0); | ||
const size = components.concat(notComponents).reduce((a, c) => c[$storeSize] > a ? c[$storeSize] : a, 0); | ||
const entities = []; | ||
const changed = []; | ||
const indices = new Uint32Array(size).fill(NONE); | ||
const indices = new Uint32Array(size).fill(NONE$1); | ||
const enabled = new Uint8Array(size); | ||
@@ -570,5 +593,5 @@ const generations = components.concat(notComponents).map(c => { | ||
}, {}); | ||
const flatProps = components.map(c => c._flatten ? c._flatten() : [c]).reduce((a, v) => a.concat(v), []); | ||
const flatProps = components.map(c => Object.getOwnPropertySymbols(c).includes($storeFlattened) ? c[$storeFlattened] : [c]).reduce((a, v) => a.concat(v), []); | ||
const toRemove = []; | ||
Object.assign(world[$queryMap].get(query), { | ||
world[$queryMap].set(query, { | ||
entities, | ||
@@ -600,4 +623,4 @@ changed, | ||
if (!world[$queryMap].has(query)) registerQuery(world, query); | ||
queryCommitRemovals(world, query); | ||
const q = world[$queryMap].get(query); | ||
queryCommitRemovals(world, q); | ||
if (q.changedComponents.length) return diff(world, query); | ||
@@ -635,3 +658,2 @@ return q.entities; | ||
}; | ||
const queryCheckComponent = (world, query, component) => { | ||
@@ -648,6 +670,2 @@ const { | ||
}; | ||
const queryCheckComponents = (world, query, components) => { | ||
return components.every(c => queryCheckComponent(world, query, c)); | ||
}; | ||
const queryAddEntity = (world, query, eid) => { | ||
@@ -661,9 +679,8 @@ const q = world[$queryMap].get(query); | ||
}; | ||
const queryCommitRemovals = (world, query) => { | ||
const q = world[$queryMap].get(query); | ||
const queryCommitRemovals = (world, q) => { | ||
while (q.toRemove.length) { | ||
const eid = q.toRemove.pop(); | ||
const index = q.indices[eid]; | ||
if (index === NONE) continue; | ||
if (index === NONE$1) continue; | ||
const swapped = q.entities.pop(); | ||
@@ -676,3 +693,3 @@ | ||
q.indices[eid] = NONE; | ||
q.indices[eid] = NONE$1; | ||
} | ||
@@ -682,5 +699,6 @@ | ||
}; | ||
const commitRemovals = world => { | ||
world[$dirtyQueries].forEach(q => { | ||
queryCommitRemovals(q); | ||
queryCommitRemovals(world, q); | ||
}); | ||
@@ -698,3 +716,2 @@ }; | ||
const $componentMap = Symbol('componentMap'); | ||
const $deferredComponentRemovals = Symbol('de$deferredComponentRemovals'); | ||
const defineComponent = schema => createStore(schema); | ||
@@ -715,2 +732,7 @@ const incrementBitflag = world => { | ||
}); | ||
if (component[$storeSize] < world[$size]) { | ||
resizeStore(component, world[$size]); | ||
} | ||
incrementBitflag(world); | ||
@@ -738,9 +760,7 @@ }; | ||
world[$entityMasks][generationId][eid] |= bitflag; // Zero out each property value | ||
// component._reset(eid) | ||
// todo: archetype graph | ||
const queries = world[$queries]; | ||
queries.forEach(query => { | ||
const components = query[$queryComponents]; | ||
if (!queryCheckComponents(world, query, components)) return; | ||
resetStoreFor(component, eid); // todo: archetype graph | ||
world[$queries].forEach(query => { | ||
if (!queryCheckComponent(world, query, component)) return; | ||
const match = queryCheckEntity(world, query, eid); | ||
@@ -757,6 +777,4 @@ if (match) queryAddEntity(world, query, eid); | ||
const queries = world[$queries]; | ||
queries.forEach(query => { | ||
const components = query[$queryComponents]; | ||
if (!queryCheckComponents(world, query, components)) return; | ||
world[$queries].forEach(query => { | ||
if (!queryCheckComponent(world, query, component)) return; | ||
const match = queryCheckEntity(world, query, eid); | ||
@@ -770,2 +788,3 @@ if (match) queryRemoveEntity(world, query, eid); | ||
const $size = Symbol('size'); | ||
const $warningSize = Symbol('warningSize'); | ||
const $bitflag = Symbol('bitflag'); | ||
@@ -777,3 +796,4 @@ const createWorld = (size = 1000000) => { | ||
world[$entityMasks] = [new Uint32Array(size)]; | ||
world[$removedEntities] = []; | ||
world[$entityArray] = []; | ||
world[$entityIndices] = new Uint32Array(size); | ||
world[$bitflag] = 1; | ||
@@ -784,4 +804,3 @@ world[$componentMap] = new Map(); | ||
world[$dirtyQueries] = new Set(); | ||
world[$deferredComponentRemovals] = []; | ||
world[$deferredEntityRemovals] = []; | ||
world[$warningSize] = size - size / 5; | ||
return world; | ||
@@ -794,2 +813,3 @@ }; | ||
commitRemovals(world); | ||
return world; | ||
}; | ||
@@ -804,8 +824,9 @@ | ||
const pipe = (...fns) => world => { | ||
const pipe = (...fns) => input => { | ||
fns = Array.isArray(fns[0]) ? fns[0] : fns; | ||
let tmp = input; | ||
for (let i = 0; i < fns.length; i++) { | ||
const fn = fns[i]; | ||
fn(world); | ||
tmp = fn(tmp); | ||
} | ||
@@ -812,0 +833,0 @@ }; |
{ | ||
"name": "bitecs", | ||
"version": "0.2.22", | ||
"version": "0.2.23", | ||
"description": "Tiny, data-driven, high performance ECS library written in Javascript", | ||
@@ -5,0 +5,0 @@ "license": "MPL-2.0", |
@@ -261,3 +261,4 @@ # ๐พ bitECS ๐พ | ||
let packet = pipe(movementQuery, serializeOnlyChangedPositions)(world) | ||
const serializeChangedMovementQuery = pipe(movementQuery, serializeOnlyChangedPositions) | ||
let packet = serializeChangedMovementQuery(world) | ||
console.log(packet) // => undefined | ||
@@ -267,4 +268,4 @@ | ||
packet = serializeOnlyChangedPositions(movementQuery(world)) | ||
packet = serializeChangedMovementQuery(world) | ||
console.log(packet.byteLength) // => 13 | ||
``` |
@@ -39,2 +39,18 @@ import assert from 'assert' | ||
}) | ||
it('should only remove the component specified', () => { | ||
const world = createWorld() | ||
const TestComponent = defineComponent({ value: Types.f32 }) | ||
const TestComponent2 = defineComponent({ value: Types.f32 }) | ||
const eid = addEntity(world) | ||
addComponent(world, TestComponent, eid) | ||
addComponent(world, TestComponent2, eid) | ||
assert(hasComponent(world, TestComponent, eid)) | ||
assert(hasComponent(world, TestComponent2, eid)) | ||
removeComponent(world, TestComponent, eid) | ||
assert(hasComponent(world, TestComponent, eid) === false) | ||
assert(hasComponent(world, TestComponent2, eid) === true) | ||
}) | ||
}) |
@@ -5,3 +5,3 @@ import { strictEqual } from 'assert' | ||
import { addComponent, defineComponent } from '../src/Component.js' | ||
import { addEntity, resetGlobals } from '../src/Entity.js' | ||
import { addEntity, removeEntity, resetGlobals } from '../src/Entity.js' | ||
import { Changed, defineQuery, Not } from '../src/Query.js' | ||
@@ -19,6 +19,12 @@ | ||
addComponent(world, TestComponent, eid) | ||
const ents = query(world) | ||
let ents = query(world) | ||
strictEqual(ents.length, 1) | ||
strictEqual(ents[0], 0) | ||
removeEntity(world, eid) | ||
ents = query(world) | ||
strictEqual(ents.length, 0) | ||
}) | ||
@@ -29,9 +35,17 @@ it('should define a query with Not and return matching eids', () => { | ||
const query = defineQuery([Not(TestComponent)]) | ||
const eid1 = addEntity(world) | ||
addComponent(world, TestComponent, eid1) | ||
const eid2 = addEntity(world) | ||
addComponent(world, TestComponent, eid1) | ||
const ents = query(world) | ||
let ents = query(world) | ||
strictEqual(ents.length, 1) | ||
strictEqual(ents[0], eid2) | ||
removeEntity(world, eid2) | ||
ents = query(world) | ||
strictEqual(ents.length, 0) | ||
}) | ||
@@ -38,0 +52,0 @@ it('should define a query with Changed and return matching eids whose component state has changed', () => { |
import assert, { strictEqual } from 'assert' | ||
import { $componentMap, $deferredComponentRemovals } from '../src/Component.js' | ||
import { $deferredEntityRemovals, $entityEnabled, $entityMasks, resetGlobals, resizeWorld, addEntity } from '../src/Entity.js' | ||
import { $componentMap } from '../src/Component.js' | ||
import { $entityEnabled, $entityMasks, resetGlobals, resizeWorld, addEntity } from '../src/Entity.js' | ||
import { $dirtyQueries, $queries, $queryMap } from '../src/Query.js' | ||
@@ -34,5 +34,2 @@ import { createWorld, $size, $bitflag } from '../src/World.js' | ||
strictEqual(world[$dirtyQueries].constructor.name, 'Set') | ||
assert(Array.isArray(world[$deferredComponentRemovals])) | ||
assert(Array.isArray(world[$deferredEntityRemovals])) | ||
}) | ||
@@ -39,0 +36,0 @@ it('should resize on-demand', () => { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
323015
17
2392
269