Comparing version 0.3.9 to 0.3.10
@@ -276,2 +276,33 @@ const TYPES_ENUM = { | ||
const SparseSet = () => { | ||
const dense = []; | ||
const sparse = []; | ||
const has = val => dense[sparse[val]] === val; | ||
const add = val => { | ||
if (has(val)) return; | ||
sparse[val] = dense.push(val) - 1; | ||
}; | ||
const remove = val => { | ||
if (!has(val)) return; | ||
const index = sparse[val]; | ||
const swapped = dense.pop(); | ||
if (swapped !== val) { | ||
dense[index] = swapped; | ||
sparse[swapped] = index; | ||
} | ||
}; | ||
return { | ||
add, | ||
remove, | ||
has, | ||
sparse, | ||
dense | ||
}; | ||
}; | ||
let resized = false; | ||
@@ -458,3 +489,3 @@ const setSerializationResized = v => { | ||
if (!world[$entityEnabled][eid] || !overwrite) { | ||
if (!world[$entitySparseSet].has(eid) || !overwrite) { | ||
// make a new entity for the data | ||
@@ -500,5 +531,5 @@ const newEid = addEntity(world); | ||
const $entityEnabled = Symbol('entityEnabled'); | ||
const $entitySparseSet = Symbol('entitySparseSet'); | ||
const $entityArray = Symbol('entityArray'); | ||
const $entityIndices = Symbol('entityIndices'); | ||
const NONE$1 = 2 ** 32 - 1; | ||
let defaultSize = 100000; // need a global EID cursor which all worlds and all components know about | ||
@@ -509,4 +540,5 @@ // so that world entities can posess entire rows spanning all component tables | ||
let globalSize = defaultSize; | ||
const threshold = globalSize - globalSize / 5; | ||
let resizeThreshold = () => globalSize - globalSize / 5; | ||
let resizeThreshold = () => threshold; | ||
@@ -529,6 +561,4 @@ const getGlobalSize = () => globalSize; // removed eids should also be global to prevent memory leaks | ||
const addEntity = world => { | ||
const enabled = world[$entityEnabled]; | ||
const eid = removed.length > 0 ? removed.shift() : globalEntityCursor++; | ||
enabled[eid] = 1; | ||
world[$entityIndices][eid] = world[$entityArray].push(eid) - 1; | ||
world[$entitySparseSet].add(eid); | ||
eidToWorld.set(eid, world); // if data stores are 80% full | ||
@@ -550,32 +580,15 @@ | ||
}; | ||
const popSwap = (world, eid) => { | ||
// 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$1; | ||
}; | ||
const removeEntity = (world, eid) => { | ||
const enabled = world[$entityEnabled]; // Check if entity is already removed | ||
if (enabled[eid] === 0) return; // Remove entity from all queries | ||
// Check if entity is already removed | ||
if (!world[$entitySparseSet].has(eid)) return; // Remove entity from all queries | ||
// TODO: archetype graph | ||
// world[$queries].forEach(q => { | ||
// queryRemoveEntity(world, q, eid) | ||
// }) | ||
// Free the entity | ||
world[$queries].forEach(query => { | ||
queryRemoveEntity(world, query, eid); | ||
}); // Free the entity | ||
removed.push(eid); // pop swap | ||
removed.push(eid); | ||
enabled[eid] = 0; // pop swap | ||
popSwap(world, eid); // Clear entity bitmasks | ||
for (let i = 0; i < world[$entityMasks].length; i++) world[$entityMasks][i][eid] = 0; | ||
world[$entitySparseSet].remove(eid); // Clear entity bitmasks | ||
// for (let i = 0; i < world[$entityMasks].length; i++) world[$entityMasks][i][eid] = 0 | ||
}; | ||
@@ -597,3 +610,2 @@ | ||
const $queryComponents = Symbol('queryComponents'); | ||
const NONE = 2 ** 32 - 1; | ||
const enterQuery = query => world => { | ||
@@ -610,6 +622,8 @@ if (!world[$queryMap].has(query)) registerQuery(world, query); | ||
const registerQuery = (world, query) => { | ||
let components = []; | ||
let notComponents = []; | ||
let changedComponents = []; | ||
const components = []; | ||
const notComponents = []; | ||
const changedComponents = []; | ||
query[$queryComponents].forEach(c => { | ||
if (!world[$componentMap].has(c)) registerComponent(world, c); | ||
if (typeof c === 'function') { | ||
@@ -631,12 +645,9 @@ if (c.name === 'QueryNot') { | ||
const size = components.concat(notComponents).reduce((a, c) => c[$storeSize] > a ? c[$storeSize] : a, 0); | ||
const entities = []; | ||
const sparseSet = SparseSet(); | ||
const archetypes = []; | ||
const changed = []; | ||
const indices = new Uint32Array(size).fill(NONE); | ||
const enabled = new Uint8Array(size); | ||
const generations = components.concat(notComponents).map(c => { | ||
if (!world[$componentMap].has(c)) registerComponent(world, c); | ||
return c; | ||
}).map(mapComponents).map(c => c.generationId).reduce((a, v) => { | ||
const toRemove = []; | ||
const entered = []; | ||
const exited = []; | ||
const generations = components.concat(notComponents).map(mapComponents).map(c => c.generationId).reduce((a, v) => { | ||
if (a.includes(v)) return a; | ||
@@ -666,9 +677,5 @@ a.push(v); | ||
const flatProps = components.filter(c => !c[$tagStore]).map(c => Object.getOwnPropertySymbols(c).includes($storeFlattened) ? c[$storeFlattened] : [c]).reduce((a, v) => a.concat(v), []); | ||
const toRemove = []; | ||
const entered = []; | ||
const exited = []; | ||
world[$queryMap].set(query, { | ||
entities, | ||
const q = Object.assign(sparseSet, { | ||
archetypes, | ||
changed, | ||
enabled, | ||
components, | ||
@@ -681,16 +688,15 @@ notComponents, | ||
generations, | ||
indices, | ||
flatProps, | ||
toRemove, | ||
entered, | ||
exited, | ||
archetypes | ||
exited | ||
}); | ||
world[$queries].add(query); | ||
world[$queryMap].set(query, q); | ||
world[$queries].add(q); | ||
for (let eid = 0; eid < getEntityCursor(); eid++) { | ||
if (!world[$entityEnabled][eid]) continue; | ||
if (!world[$entitySparseSet].has(eid)) continue; | ||
if (queryCheckEntity(world, query, eid)) { | ||
queryAddEntity(world, query, eid); | ||
if (queryCheckEntity(world, q, eid)) { | ||
queryAddEntity(world, q, eid); | ||
} | ||
@@ -704,4 +710,4 @@ } | ||
for (let i = 0; i < q.entities.length; i++) { | ||
const eid = q.entities[i]; | ||
for (let i = 0; i < q.dense.length; i++) { | ||
const eid = q.dense[i]; | ||
let dirty = false; | ||
@@ -743,3 +749,3 @@ | ||
if (q.changedComponents.length) return diff(q, clearDiff); | ||
return q.entities; | ||
return q.dense; | ||
}; | ||
@@ -751,3 +757,3 @@ | ||
const queryCheckEntity = (world, query, eid) => { | ||
const queryCheckEntity = (world, q, eid) => { | ||
const { | ||
@@ -757,3 +763,3 @@ masks, | ||
generations | ||
} = world[$queryMap].get(query); // let or = true | ||
} = q; // let or = true | ||
@@ -781,3 +787,3 @@ for (let i = 0; i < generations.length; i++) { | ||
}; | ||
const queryCheckComponent = (world, query, component) => { | ||
const queryCheckComponent = (world, q, component) => { | ||
const { | ||
@@ -789,12 +795,9 @@ generationId, | ||
masks | ||
} = world[$queryMap].get(query); | ||
} = q; | ||
const mask = masks[generationId]; | ||
return (mask & bitflag) === bitflag; | ||
}; | ||
const queryAddEntity = (world, query, eid) => { | ||
const q = world[$queryMap].get(query); | ||
if (q.enabled[eid]) return; | ||
q.enabled[eid] = true; | ||
q.entities.push(eid); | ||
q.indices[eid] = q.entities.length - 1; | ||
const queryAddEntity = (world, q, eid) => { | ||
if (q.has(eid)) return; | ||
q.add(eid); | ||
q.entered.push(eid); | ||
@@ -805,13 +808,3 @@ }; | ||
while (q.toRemove.length) { | ||
const eid = q.toRemove.pop(); | ||
const index = q.indices[eid]; | ||
if (index === NONE) continue; | ||
const swapped = q.entities.pop(); | ||
if (swapped !== eid) { | ||
q.entities[index] = swapped; | ||
q.indices[swapped] = index; | ||
} | ||
q.indices[eid] = NONE; | ||
q.remove(q.toRemove.pop()); | ||
} | ||
@@ -827,6 +820,5 @@ | ||
}; | ||
const queryRemoveEntity = (world, query, eid) => { | ||
const q = world[$queryMap].get(query); | ||
if (!q.enabled[eid]) return; | ||
q.enabled[eid] = false; | ||
const queryRemoveEntity = (world, q, eid) => { | ||
if (!q.has(eid)) return; | ||
q.remove(eid); | ||
q.toRemove.push(eid); | ||
@@ -841,2 +833,4 @@ world[$dirtyQueries].add(q); | ||
const removeQuery = (world, query) => { | ||
const q = world[$queryMap].get(query); | ||
world[$queries].delete(q); | ||
world[$queryMap].delete(query); | ||
@@ -905,6 +899,6 @@ }; | ||
world[$queries].forEach(query => { | ||
if (!queryCheckComponent(world, query, component)) return; | ||
const match = queryCheckEntity(world, query, eid); | ||
if (match) queryAddEntity(world, query, eid); | ||
world[$queries].forEach(q => { | ||
if (!queryCheckComponent(world, q, component)) return; | ||
const match = queryCheckEntity(world, q, eid); | ||
if (match) queryAddEntity(world, q, eid); | ||
}); // Zero out each property value | ||
@@ -927,6 +921,6 @@ | ||
world[$queries].forEach(query => { | ||
if (!queryCheckComponent(world, query, component)) return; | ||
const match = queryCheckEntity(world, query, eid); | ||
if (match) queryRemoveEntity(world, query, eid); | ||
world[$queries].forEach(q => { | ||
if (!queryCheckComponent(world, q, component)) return; | ||
const match = queryCheckEntity(world, q, eid); | ||
if (match) queryRemoveEntity(world, q, eid); | ||
}); // Remove flag from entity bitmask | ||
@@ -946,8 +940,2 @@ | ||
world[$size] = size; | ||
world[$queryMap].forEach(q => { | ||
q.indices = resize(q.indices, size); | ||
q.enabled = resize(q.enabled, size); | ||
}); | ||
world[$entityEnabled] = resize(world[$entityEnabled], size); | ||
world[$entityIndices] = resize(world[$entityIndices], size); | ||
@@ -971,6 +959,6 @@ for (let i = 0; i < world[$entityMasks].length; i++) { | ||
world[$size] = size; | ||
world[$entityEnabled] = new Uint8Array(size); | ||
world[$entityMasks] = [new Uint32Array(size)]; | ||
world[$entityArray] = []; | ||
world[$entityIndices] = new Uint32Array(size); | ||
if (world[$entityArray]) world[$entityArray].forEach(eid => removeEntity(world, eid)); | ||
world[$entitySparseSet] = SparseSet(); | ||
world[$entityArray] = world[$entitySparseSet].dense; | ||
world[$bitflag] = 1; | ||
@@ -977,0 +965,0 @@ world[$componentMap] = new Map(); |
@@ -280,2 +280,33 @@ 'use strict'; | ||
const SparseSet = () => { | ||
const dense = []; | ||
const sparse = []; | ||
const has = val => dense[sparse[val]] === val; | ||
const add = val => { | ||
if (has(val)) return; | ||
sparse[val] = dense.push(val) - 1; | ||
}; | ||
const remove = val => { | ||
if (!has(val)) return; | ||
const index = sparse[val]; | ||
const swapped = dense.pop(); | ||
if (swapped !== val) { | ||
dense[index] = swapped; | ||
sparse[swapped] = index; | ||
} | ||
}; | ||
return { | ||
add, | ||
remove, | ||
has, | ||
sparse, | ||
dense | ||
}; | ||
}; | ||
let resized = false; | ||
@@ -462,3 +493,3 @@ const setSerializationResized = v => { | ||
if (!world[$entityEnabled][eid] || !overwrite) { | ||
if (!world[$entitySparseSet].has(eid) || !overwrite) { | ||
// make a new entity for the data | ||
@@ -504,5 +535,5 @@ const newEid = addEntity(world); | ||
const $entityEnabled = Symbol('entityEnabled'); | ||
const $entitySparseSet = Symbol('entitySparseSet'); | ||
const $entityArray = Symbol('entityArray'); | ||
const $entityIndices = Symbol('entityIndices'); | ||
const NONE$1 = 2 ** 32 - 1; | ||
let defaultSize = 100000; // need a global EID cursor which all worlds and all components know about | ||
@@ -513,4 +544,5 @@ // so that world entities can posess entire rows spanning all component tables | ||
let globalSize = defaultSize; | ||
const threshold = globalSize - globalSize / 5; | ||
let resizeThreshold = () => globalSize - globalSize / 5; | ||
let resizeThreshold = () => threshold; | ||
@@ -533,6 +565,4 @@ const getGlobalSize = () => globalSize; // removed eids should also be global to prevent memory leaks | ||
const addEntity = world => { | ||
const enabled = world[$entityEnabled]; | ||
const eid = removed.length > 0 ? removed.shift() : globalEntityCursor++; | ||
enabled[eid] = 1; | ||
world[$entityIndices][eid] = world[$entityArray].push(eid) - 1; | ||
world[$entitySparseSet].add(eid); | ||
eidToWorld.set(eid, world); // if data stores are 80% full | ||
@@ -554,32 +584,15 @@ | ||
}; | ||
const popSwap = (world, eid) => { | ||
// 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$1; | ||
}; | ||
const removeEntity = (world, eid) => { | ||
const enabled = world[$entityEnabled]; // Check if entity is already removed | ||
if (enabled[eid] === 0) return; // Remove entity from all queries | ||
// Check if entity is already removed | ||
if (!world[$entitySparseSet].has(eid)) return; // Remove entity from all queries | ||
// TODO: archetype graph | ||
// world[$queries].forEach(q => { | ||
// queryRemoveEntity(world, q, eid) | ||
// }) | ||
// Free the entity | ||
world[$queries].forEach(query => { | ||
queryRemoveEntity(world, query, eid); | ||
}); // Free the entity | ||
removed.push(eid); // pop swap | ||
removed.push(eid); | ||
enabled[eid] = 0; // pop swap | ||
popSwap(world, eid); // Clear entity bitmasks | ||
for (let i = 0; i < world[$entityMasks].length; i++) world[$entityMasks][i][eid] = 0; | ||
world[$entitySparseSet].remove(eid); // Clear entity bitmasks | ||
// for (let i = 0; i < world[$entityMasks].length; i++) world[$entityMasks][i][eid] = 0 | ||
}; | ||
@@ -601,3 +614,2 @@ | ||
const $queryComponents = Symbol('queryComponents'); | ||
const NONE = 2 ** 32 - 1; | ||
const enterQuery = query => world => { | ||
@@ -614,6 +626,8 @@ if (!world[$queryMap].has(query)) registerQuery(world, query); | ||
const registerQuery = (world, query) => { | ||
let components = []; | ||
let notComponents = []; | ||
let changedComponents = []; | ||
const components = []; | ||
const notComponents = []; | ||
const changedComponents = []; | ||
query[$queryComponents].forEach(c => { | ||
if (!world[$componentMap].has(c)) registerComponent(world, c); | ||
if (typeof c === 'function') { | ||
@@ -635,12 +649,9 @@ if (c.name === 'QueryNot') { | ||
const size = components.concat(notComponents).reduce((a, c) => c[$storeSize] > a ? c[$storeSize] : a, 0); | ||
const entities = []; | ||
const sparseSet = SparseSet(); | ||
const archetypes = []; | ||
const changed = []; | ||
const indices = new Uint32Array(size).fill(NONE); | ||
const enabled = new Uint8Array(size); | ||
const generations = components.concat(notComponents).map(c => { | ||
if (!world[$componentMap].has(c)) registerComponent(world, c); | ||
return c; | ||
}).map(mapComponents).map(c => c.generationId).reduce((a, v) => { | ||
const toRemove = []; | ||
const entered = []; | ||
const exited = []; | ||
const generations = components.concat(notComponents).map(mapComponents).map(c => c.generationId).reduce((a, v) => { | ||
if (a.includes(v)) return a; | ||
@@ -670,9 +681,5 @@ a.push(v); | ||
const flatProps = components.filter(c => !c[$tagStore]).map(c => Object.getOwnPropertySymbols(c).includes($storeFlattened) ? c[$storeFlattened] : [c]).reduce((a, v) => a.concat(v), []); | ||
const toRemove = []; | ||
const entered = []; | ||
const exited = []; | ||
world[$queryMap].set(query, { | ||
entities, | ||
const q = Object.assign(sparseSet, { | ||
archetypes, | ||
changed, | ||
enabled, | ||
components, | ||
@@ -685,16 +692,15 @@ notComponents, | ||
generations, | ||
indices, | ||
flatProps, | ||
toRemove, | ||
entered, | ||
exited, | ||
archetypes | ||
exited | ||
}); | ||
world[$queries].add(query); | ||
world[$queryMap].set(query, q); | ||
world[$queries].add(q); | ||
for (let eid = 0; eid < getEntityCursor(); eid++) { | ||
if (!world[$entityEnabled][eid]) continue; | ||
if (!world[$entitySparseSet].has(eid)) continue; | ||
if (queryCheckEntity(world, query, eid)) { | ||
queryAddEntity(world, query, eid); | ||
if (queryCheckEntity(world, q, eid)) { | ||
queryAddEntity(world, q, eid); | ||
} | ||
@@ -708,4 +714,4 @@ } | ||
for (let i = 0; i < q.entities.length; i++) { | ||
const eid = q.entities[i]; | ||
for (let i = 0; i < q.dense.length; i++) { | ||
const eid = q.dense[i]; | ||
let dirty = false; | ||
@@ -747,3 +753,3 @@ | ||
if (q.changedComponents.length) return diff(q, clearDiff); | ||
return q.entities; | ||
return q.dense; | ||
}; | ||
@@ -755,3 +761,3 @@ | ||
const queryCheckEntity = (world, query, eid) => { | ||
const queryCheckEntity = (world, q, eid) => { | ||
const { | ||
@@ -761,3 +767,3 @@ masks, | ||
generations | ||
} = world[$queryMap].get(query); // let or = true | ||
} = q; // let or = true | ||
@@ -785,3 +791,3 @@ for (let i = 0; i < generations.length; i++) { | ||
}; | ||
const queryCheckComponent = (world, query, component) => { | ||
const queryCheckComponent = (world, q, component) => { | ||
const { | ||
@@ -793,12 +799,9 @@ generationId, | ||
masks | ||
} = world[$queryMap].get(query); | ||
} = q; | ||
const mask = masks[generationId]; | ||
return (mask & bitflag) === bitflag; | ||
}; | ||
const queryAddEntity = (world, query, eid) => { | ||
const q = world[$queryMap].get(query); | ||
if (q.enabled[eid]) return; | ||
q.enabled[eid] = true; | ||
q.entities.push(eid); | ||
q.indices[eid] = q.entities.length - 1; | ||
const queryAddEntity = (world, q, eid) => { | ||
if (q.has(eid)) return; | ||
q.add(eid); | ||
q.entered.push(eid); | ||
@@ -809,13 +812,3 @@ }; | ||
while (q.toRemove.length) { | ||
const eid = q.toRemove.pop(); | ||
const index = q.indices[eid]; | ||
if (index === NONE) continue; | ||
const swapped = q.entities.pop(); | ||
if (swapped !== eid) { | ||
q.entities[index] = swapped; | ||
q.indices[swapped] = index; | ||
} | ||
q.indices[eid] = NONE; | ||
q.remove(q.toRemove.pop()); | ||
} | ||
@@ -831,6 +824,5 @@ | ||
}; | ||
const queryRemoveEntity = (world, query, eid) => { | ||
const q = world[$queryMap].get(query); | ||
if (!q.enabled[eid]) return; | ||
q.enabled[eid] = false; | ||
const queryRemoveEntity = (world, q, eid) => { | ||
if (!q.has(eid)) return; | ||
q.remove(eid); | ||
q.toRemove.push(eid); | ||
@@ -845,2 +837,4 @@ world[$dirtyQueries].add(q); | ||
const removeQuery = (world, query) => { | ||
const q = world[$queryMap].get(query); | ||
world[$queries].delete(q); | ||
world[$queryMap].delete(query); | ||
@@ -909,6 +903,6 @@ }; | ||
world[$queries].forEach(query => { | ||
if (!queryCheckComponent(world, query, component)) return; | ||
const match = queryCheckEntity(world, query, eid); | ||
if (match) queryAddEntity(world, query, eid); | ||
world[$queries].forEach(q => { | ||
if (!queryCheckComponent(world, q, component)) return; | ||
const match = queryCheckEntity(world, q, eid); | ||
if (match) queryAddEntity(world, q, eid); | ||
}); // Zero out each property value | ||
@@ -931,6 +925,6 @@ | ||
world[$queries].forEach(query => { | ||
if (!queryCheckComponent(world, query, component)) return; | ||
const match = queryCheckEntity(world, query, eid); | ||
if (match) queryRemoveEntity(world, query, eid); | ||
world[$queries].forEach(q => { | ||
if (!queryCheckComponent(world, q, component)) return; | ||
const match = queryCheckEntity(world, q, eid); | ||
if (match) queryRemoveEntity(world, q, eid); | ||
}); // Remove flag from entity bitmask | ||
@@ -950,8 +944,2 @@ | ||
world[$size] = size; | ||
world[$queryMap].forEach(q => { | ||
q.indices = resize(q.indices, size); | ||
q.enabled = resize(q.enabled, size); | ||
}); | ||
world[$entityEnabled] = resize(world[$entityEnabled], size); | ||
world[$entityIndices] = resize(world[$entityIndices], size); | ||
@@ -975,6 +963,6 @@ for (let i = 0; i < world[$entityMasks].length; i++) { | ||
world[$size] = size; | ||
world[$entityEnabled] = new Uint8Array(size); | ||
world[$entityMasks] = [new Uint32Array(size)]; | ||
world[$entityArray] = []; | ||
world[$entityIndices] = new Uint32Array(size); | ||
if (world[$entityArray]) world[$entityArray].forEach(eid => removeEntity(world, eid)); | ||
world[$entitySparseSet] = SparseSet(); | ||
world[$entityArray] = world[$entitySparseSet].dense; | ||
world[$bitflag] = 1; | ||
@@ -981,0 +969,0 @@ world[$componentMap] = new Map(); |
{ | ||
"name": "bitecs", | ||
"version": "0.3.9", | ||
"version": "0.3.10", | ||
"description": "Functional, minimal, data-driven, ultra-high performance ECS library written in Javascript", | ||
@@ -5,0 +5,0 @@ "license": "MPL-2.0", |
@@ -21,5 +21,2 @@ import { strictEqual } from 'assert' | ||
strictEqual(world[$entityEnabled][eid1], 1) | ||
strictEqual(world[$entityEnabled][eid2], 1) | ||
strictEqual(world[$entityEnabled][eid3], 1) | ||
strictEqual(eid1, 0) | ||
@@ -26,0 +23,0 @@ strictEqual(eid2, 1) |
@@ -23,4 +23,3 @@ import assert, { strictEqual } from 'assert' | ||
strictEqual(world[$entityMasks][0].length, growAmount) | ||
strictEqual(world[$entityEnabled].length, growAmount) | ||
}) | ||
}) |
@@ -20,5 +20,2 @@ import assert, { strictEqual } from 'assert' | ||
strictEqual(world[$entityEnabled].constructor.name, 'Uint8Array') | ||
strictEqual(world[$entityEnabled].length, defaultSize) | ||
assert(Array.isArray(world[$entityMasks])) | ||
@@ -25,0 +22,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
268152
2270