New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More โ†’
Socket
Sign inDemoInstall
Socket

bitecs

Package Overview
Dependencies
Maintainers
1
Versions
133
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bitecs - npm Package Compare versions

Comparing version 0.3.2 to 0.3.3-a

21

dist/index.d.ts

@@ -38,2 +38,19 @@ declare module 'bitecs' {

type ArrayByType = {
['bool']: boolean[];
[Types.i8]: Int8Array;
[Types.ui8]: Uint8Array;
[Types.ui8c]: Uint8ClampedArray;
[Types.i16]: Int16Array;
[Types.ui16]: Uint16Array;
[Types.i32]: Int32Array;
[Types.ui32]: Uint32Array;
[Types.f32]: Float32Array;
[Types.f64]: Float64Array;
}
type ComponentType<T extends ISchema> = {
[key in keyof T]: T[key] extends Type ? ArrayByType[T[key]] : T[key] extends ISchema ? ComponentType<T[key]> : unknown;
}
interface IWorld {

@@ -61,3 +78,3 @@ [key: string]: any

export function createWorld (size?: number): IWorld

@@ -68,3 +85,3 @@ export function addEntity (world: IWorld): number

export function registerComponents (world: IWorld, components: IComponent[]): void
export function defineComponent (schema: ISchema): IComponent
export function defineComponent <T extends ISchema>(schema: T): ComponentType<T>
export function addComponent (world: IWorld, component: IComponent, eid: number): void

@@ -71,0 +88,0 @@ export function removeComponent (world: IWorld, component: IComponent, eid: number): void

650

dist/index.es.js

@@ -51,2 +51,3 @@ const TYPES_ENUM = {

const $subarray = Symbol('subarray');
const $tagStore = Symbol('tagStore');
const $queryShadow = Symbol('queryShadow');

@@ -64,11 +65,53 @@ const $serializeShadow = Symbol('serializeShadow');

const resizeRecursive = (store, size) => {
const resizeSubarray = (metadata, store, size) => {
const cursors = metadata[$subarrayCursors];
const type = store[$storeType];
const length = store[0].length;
const indexType = length < UNSIGNED_MAX.uint8 ? 'ui8' : length < UNSIGNED_MAX.uint16 ? 'ui16' : 'ui32';
const arrayCount = metadata[$storeArrayCounts][type];
const summedLength = Array(arrayCount).fill(0).reduce((a, p) => a + length, 0); // for threaded impl
// const summedBytesPerElement = Array(arrayCount).fill(0).reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0)
// const totalBytes = roundToMultiple4(summedBytesPerElement * summedLength * size)
// const buffer = new ArrayBuffer(totalBytes)
const array = new TYPES[type](summedLength * size);
array.set(metadata[$storeSubarrays][type]);
metadata[$storeSubarrays][type] = array;
metadata[$storeSubarrays][type][$queryShadow] = array.slice(0);
metadata[$storeSubarrays][type][$serializeShadow] = array.slice(0);
array[$indexType] = TYPES_NAMES[indexType];
array[$indexBytes] = TYPES[indexType].BYTES_PER_ELEMENT;
let end = 0;
for (let eid = 0; eid < size; eid++) {
const from = cursors[type] + eid * length;
const to = from + length;
store[eid] = metadata[$storeSubarrays][type].subarray(from, to);
store[eid].from = from;
store[eid].to = to;
store[eid][$queryShadow] = metadata[$storeSubarrays][type][$queryShadow].subarray(from, to);
store[eid][$serializeShadow] = metadata[$storeSubarrays][type][$serializeShadow].subarray(from, to);
store[eid][$subarray] = true;
store[eid][$indexType] = array[$indexType];
store[eid][$indexBytes] = array[$indexBytes];
end = to;
}
cursors[type] = end;
};
const resizeRecursive = (metadata, store, size) => {
Object.keys(store).forEach(key => {
const ta = store[key];
if (ta[$subarray]) return;else if (ArrayBuffer.isView(ta)) {
if (Array.isArray(ta)) {
resizeSubarray(metadata, ta, size);
store[$storeFlattened].push(ta);
} else if (ArrayBuffer.isView(ta)) {
store[key] = resize(ta, size);
store[$storeFlattened].push(store[key]);
store[key][$queryShadow] = resize(ta[$queryShadow], size);
store[key][$serializeShadow] = resize(ta[$serializeShadow], size);
} else if (typeof ta === 'object') {
resizeRecursive(store[key], size);
resizeRecursive(metadata, store[key], size);
}

@@ -78,46 +121,17 @@ });

const resizeSubarrays = (metadata, size) => {
Object.keys(metadata[$subarrayCursors]).forEach(k => {
metadata[$subarrayCursors][k] = 0;
});
const cursors = metadata[$subarrayCursors];
metadata[$storeFlattened].filter(store => !ArrayBuffer.isView(store)).forEach(store => {
const type = store[$storeType];
const length = store[0].length;
const arrayCount = metadata[$storeArrayCounts][type];
const summedLength = Array(arrayCount).fill(0).reduce((a, p) => a + length, 0); // for threaded impl
// const summedBytesPerElement = Array(arrayCount).fill(0).reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0)
// const totalBytes = roundToMultiple4(summedBytesPerElement * summedLength * size)
// const buffer = new ArrayBuffer(totalBytes)
const array = new TYPES[type](summedLength * size);
array.set(metadata[$storeSubarrays][type]);
metadata[$storeSubarrays][type] = array;
metadata[$storeSubarrays][type][$queryShadow] = array.slice(0);
metadata[$storeSubarrays][type][$serializeShadow] = array.slice(0);
for (let eid = 0; eid < size; eid++) {
const from = cursors[type] + eid * length;
const to = from + length;
store[eid] = metadata[$storeSubarrays][type].subarray(from, to);
store[eid].from = from;
store[eid].to = to;
store[eid][$queryShadow] = metadata[$storeSubarrays][type][$queryShadow].subarray(from, to);
store[eid][$serializeShadow] = metadata[$storeSubarrays][type][$serializeShadow].subarray(from, to);
store[eid][$subarray] = true;
store[eid][$indexType] = array[$indexType];
store[eid][$indexBytes] = array[$indexBytes];
}
});
};
const resizeStore = (store, size) => {
if (store[$tagStore]) return;
store[$storeSize] = size;
resizeRecursive(store, size);
resizeSubarrays(store, size);
store[$storeFlattened].length = 0;
Object.keys(store[$subarrayCursors]).forEach(k => {
store[$subarrayCursors][k] = 0;
});
resizeRecursive(store, store, size); // resizeSubarrays(store, size)
};
const resetStoreFor = (store, eid) => {
store[$storeFlattened].forEach(ta => {
if (ArrayBuffer.isView(ta)) ta[eid] = 0;else ta[eid].fill(0);
});
if (store[$storeFlattened]) {
store[$storeFlattened].forEach(ta => {
if (ArrayBuffer.isView(ta)) ta[eid] = 0;else ta[eid].fill(0);
});
}
};

@@ -183,5 +197,15 @@

const createStore = (schema, size = 10000) => {
const createStore = (schema, size) => {
const $store = Symbol('store');
if (!schema) return {};
if (!schema || !Object.keys(schema).length) {
// tag component
stores[$store] = {
[$storeSize]: size,
[$tagStore]: true,
[$storeBase]: () => stores[$store]
};
return stores[$store];
}
schema = JSON.parse(JSON.stringify(schema));

@@ -213,4 +237,4 @@ const arrayCounts = {};

}), {}),
[$storeArrayCounts]: arrayCounts,
[$storeFlattened]: []
[$storeFlattened]: [],
[$storeArrayCounts]: arrayCounts
};

@@ -222,2 +246,3 @@

a[k] = createTypeStore(a[k], size);
createShadows(a[k]);

@@ -227,3 +252,2 @@ a[k][$storeBase] = () => stores[$store];

metadata[$storeFlattened].push(a[k]);
createShadows(a[k]);
} else if (isArrayType(a[k])) {

@@ -249,12 +273,223 @@ const [type, length] = a[k];

return stores[$store];
} // tag component
}
};
let resized = false;
const setSerializationResized = v => {
resized = v;
};
stores[$store] = metadata;
const canonicalize = target => {
let componentProps = [];
let changedProps = new Set();
stores[$store][$storeBase] = () => stores[$store];
if (Array.isArray(target)) {
componentProps = target.map(p => {
if (typeof p === 'function' && p.name === 'QueryChanged') {
p()[$storeFlattened].forEach(prop => {
changedProps.add(prop);
});
return p()[$storeFlattened];
}
return stores[$store];
if (Object.getOwnPropertySymbols(p).includes($storeFlattened)) {
return p[$storeFlattened];
}
if (Object.getOwnPropertySymbols(p).includes($storeBase)) {
return p;
}
}).reduce((a, v) => a.concat(v), []);
}
return [componentProps, changedProps];
};
const defineSerializer = (target, maxBytes = 20000000) => {
const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap);
let [componentProps, changedProps] = canonicalize(target); // TODO: calculate max bytes based on target
const buffer = new ArrayBuffer(maxBytes);
const view = new DataView(buffer);
return ents => {
if (resized) {
[componentProps, changedProps] = canonicalize(target);
resized = false;
}
if (isWorld) {
componentProps = [];
target[$componentMap].forEach((c, component) => {
componentProps.push(...component[$storeFlattened]);
});
}
let world;
if (Object.getOwnPropertySymbols(ents).includes($componentMap)) {
world = ents;
ents = ents[$entityArray];
} else {
world = eidToWorld.get(ents[0]);
}
if (!ents.length) return;
let where = 0; // iterate over component props
for (let pid = 0; pid < componentProps.length; pid++) {
const prop = componentProps[pid];
const diff = changedProps.has(prop); // write pid
view.setUint8(where, pid);
where += 1; // save space for entity count
const countWhere = where;
where += 4;
let count = 0; // write eid,val
for (let i = 0; i < ents.length; i++) {
const eid = ents[i]; // skip if entity doesn't have this component
if (!hasComponent(world, prop[$storeBase](), eid)) {
continue;
} // skip if diffing and no change
if (diff && prop[eid] === prop[$serializeShadow][eid]) {
continue;
}
count++; // write eid
view.setUint32(where, eid);
where += 4;
if (prop[$tagStore]) {
continue;
} // if property is an array
if (ArrayBuffer.isView(prop[eid])) {
const type = prop[eid].constructor.name.replace('Array', '');
const indexType = prop[eid][$indexType];
const indexBytes = prop[eid][$indexBytes]; // add space for count of dirty array elements
const countWhere2 = where;
where += 1;
let count2 = 0; // write index,value
for (let i = 0; i < prop[eid].length; i++) {
const value = prop[eid][i];
if (diff && prop[eid][i] === prop[eid][$serializeShadow][i]) {
continue;
} // write array index
view[`set${indexType}`](where, i);
where += indexBytes; // write value at that index
view[`set${type}`](where, value);
where += prop[eid].BYTES_PER_ELEMENT;
count2++;
} // write total element count
view[`set${indexType}`](countWhere2, count2);
} else {
// regular property values
const type = prop.constructor.name.replace('Array', ''); // set value next [type] bytes
view[`set${type}`](where, prop[eid]);
where += prop.BYTES_PER_ELEMENT; // sync shadow state
prop[$serializeShadow][eid] = prop[eid];
}
}
view.setUint32(countWhere, count);
}
return buffer.slice(0, where);
};
};
const defineDeserializer = target => {
const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap);
let [componentProps] = canonicalize(target);
return (world, packet) => {
if (resized) {
[componentProps] = canonicalize(target);
resized = false;
}
if (isWorld) {
componentProps = [];
target[$componentMap].forEach((c, component) => {
componentProps.push(...component[$storeFlattened]);
});
}
const view = new DataView(packet);
let where = 0;
const newEntities = new Map();
while (where < packet.byteLength) {
// pid
const pid = view.getUint8(where);
where += 1; // entity count
const entityCount = view.getUint32(where);
where += 4; // component property
const prop = componentProps[pid]; // Get the entities and set their prop values
for (let i = 0; i < entityCount; i++) {
let eid = view.getUint32(where);
where += 4;
let newEid = newEntities.get(eid);
if (newEid !== undefined) {
eid = newEid;
} // if this world hasn't seen this eid yet
if (!world[$entityEnabled][eid]) {
// make a new entity for the data
const newEid = addEntity(world);
newEntities.set(eid, newEid);
eid = newEid;
}
const component = prop[$storeBase]();
if (!hasComponent(world, component, eid)) {
addComponent(world, component, eid); // console.log('hi',eid)
}
if (component[$tagStore]) {
continue;
}
if (ArrayBuffer.isView(prop[eid])) {
const array = prop[eid];
const count = view[`get${array[$indexType]}`](where);
where += array[$indexBytes]; // iterate over count
for (let i = 0; i < count; i++) {
const index = view[`get${array[$indexType]}`](where);
where += array[$indexBytes];
const value = view[`get${array.constructor.name.replace('Array', '')}`](where);
where += array.BYTES_PER_ELEMENT;
prop[eid][index] = value;
}
} else {
const value = view[`get${prop.constructor.name.replace('Array', '')}`](where);
where += prop.BYTES_PER_ELEMENT;
prop[eid] = value;
}
}
}
};
};
const $entityMasks = Symbol('entityMasks');

@@ -264,26 +499,16 @@ const $entityEnabled = Symbol('entityEnabled');

const $entityIndices = Symbol('entityIndices');
const NONE$1 = 2 ** 32; // need a global EID cursor which all worlds and all components know about
const NONE$1 = 2 ** 32;
const defaultSize = 100000; // 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; // removed eids should also be global to prevent memory leaks
let globalEntityCursor = 0;
let globalSize = defaultSize;
let resizeThreshold = () => globalSize - globalSize / 5;
const getGlobalSize = () => globalSize; // removed eids should also be global to prevent memory leaks
const removed = [];
const getEntityCursor = () => globalEntityCursor;
const resizeWorld = (world, size) => {
world[$size] = size;
world[$componentMap].forEach(c => {
resizeStore(c.store, 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);
for (let i = 0; i < world[$entityMasks].length; i++) {
const masks = world[$entityMasks][i];
world[$entityMasks][i] = resize(masks, size);
}
};
const eidToWorld = new Map();
const addEntity = world => {

@@ -293,11 +518,15 @@ const enabled = world[$entityEnabled];

enabled[eid] = 1;
world[$entityIndices][eid] = world[$entityArray].push(eid) - 1; // if data stores are 80% full
world[$entityIndices][eid] = world[$entityArray].push(eid) - 1;
eidToWorld.set(eid, world); // if data stores are 80% full
if (globalEntityCursor >= world[$warningSize]) {
if (globalEntityCursor >= resizeThreshold()) {
// grow by half the original size rounded up to a multiple of 4
const size = world[$size];
const size = globalSize;
const amount = Math.ceil(size / 2 / 4) * 4;
resizeWorld(world, size + amount);
world[$warningSize] = world[$size] - world[$size] / 5;
console.info(`๐Ÿ‘พ bitECS - resizing world from ${size} to ${size + amount}`);
const newSize = size + amount;
globalSize = newSize;
resizeWorlds(newSize);
resizeComponents(newSize);
setSerializationResized(true);
console.info(`๐Ÿ‘พ bitECS - resizing all worlds from ${size} to ${size + amount}`);
}

@@ -307,2 +536,16 @@

};
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) => {

@@ -321,12 +564,4 @@ const enabled = world[$entityEnabled]; // Check if entity is already removed

const index = world[$entityIndices][eid];
const swapped = world[$entityArray].pop();
popSwap(world, eid); // Clear entity bitmasks
if (swapped !== eid) {
world[$entityArray][index] = swapped;
world[$entityIndices][swapped] = index;
}
world[$entityIndices][eid] = NONE$1; // Clear entity bitmasks
for (let i = 0; i < world[$entityMasks].length; i++) world[$entityMasks][i][eid] = 0;

@@ -439,6 +674,3 @@ };

}
}; // const queryHooks = (q) => {
// while (q.entered.length) if (q.enter) { q.enter(q.entered.shift()) } else q.entered.length = 0
// while (q.exited.length) if (q.exit) { q.exit(q.exited.shift()) } else q.exited.length = 0
// }
};

@@ -531,3 +763,4 @@ const diff = q => {

q.entities.push(eid);
q.indices[eid] = q.entities.length - 1;
q.indices[eid] = q.entities.length - 1; // TODO: pop swap so dupes don't enter
q.entered.push(eid);

@@ -564,3 +797,4 @@ };

q.toRemove.push(eid);
world[$dirtyQueries].add(q);
world[$dirtyQueries].add(q); // TODO: pop swap so dupes don't enter
q.exited.push(eid);

@@ -570,3 +804,11 @@ };

const $componentMap = Symbol('componentMap');
const defineComponent = schema => createStore(schema);
const components = [];
const resizeComponents = size => {
components.forEach(component => resizeStore(component, size));
};
const defineComponent = schema => {
const component = createStore(schema, defaultSize);
if (schema && Object.keys(schema).length) components.push(component);
return component;
};
const incrementBitflag = world => {

@@ -624,3 +866,3 @@ world[$bitflag] *= 2;

};
const removeComponent = (world, component, eid, reset = false) => {
const removeComponent = (world, component, eid, reset = true) => {
const {

@@ -644,6 +886,26 @@ generationId,

const $size = Symbol('size');
const $warningSize = Symbol('warningSize');
const $resizeThreshold = Symbol('resizeThreshold');
const $bitflag = Symbol('bitflag');
const createWorld = (size = 10000) => {
const worlds = [];
const resizeWorlds = size => {
worlds.forEach(world => {
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);
for (let i = 0; i < world[$entityMasks].length; i++) {
const masks = world[$entityMasks][i];
world[$entityMasks][i] = resize(masks, size);
}
world[$resizeThreshold] = world[$size] - world[$size] / 5;
});
};
const createWorld = () => {
const world = {};
const size = getGlobalSize();
world[$size] = size;

@@ -659,8 +921,17 @@ world[$entityEnabled] = new Uint8Array(size);

world[$dirtyQueries] = new Set();
world[$warningSize] = size - size / 5;
worlds.push(world);
return world;
};
const defineSystem = update => {
const defineSystem = (fn1, fn2) => {
const update = fn2 !== undefined ? fn2 : fn1;
const create = fn2 !== undefined ? fn1 : undefined;
const init = new Set();
const system = world => {
if (create && !init.has(world)) {
create(world);
init.add(world);
}
update(world);

@@ -678,178 +949,4 @@ commitRemovals(world);

const canonicalize = target => {
let componentProps = [];
let changedProps = new Set();
if (Array.isArray(target)) {
componentProps = target.map(p => {
if (typeof p === 'function' && p.name === 'QueryChanged') {
p()[$storeFlattened].forEach(prop => {
changedProps.add(prop);
});
return p()[$storeFlattened];
}
if (Object.getOwnPropertySymbols(p).includes($storeFlattened)) {
return p[$storeFlattened];
}
if (Object.getOwnPropertySymbols(p).includes($storeBase)) {
return p;
}
}).reduce((a, v) => a.concat(v), []);
}
return [componentProps, changedProps];
};
const defineSerializer = (target, maxBytes = 20_000_000) => {
const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap);
let [componentProps, changedProps] = canonicalize(target); // TODO: calculate max bytes based on target
const buffer = new ArrayBuffer(maxBytes);
const view = new DataView(buffer);
return ents => {
if (isWorld) {
componentProps = [];
target[$componentMap].forEach((c, component) => {
componentProps.push(...component[$storeFlattened]);
});
}
if (Object.getOwnPropertySymbols(ents).includes($componentMap)) {
ents = ents[$entityArray];
}
if (!ents.length) return;
let where = 0; // iterate over component props
for (let pid = 0; pid < componentProps.length; pid++) {
const prop = componentProps[pid];
const diff = changedProps.has(prop); // write pid
view.setUint8(where, pid);
where += 1; // save space for entity count
const countWhere = where;
where += 4;
let count = 0; // write eid,val
for (let i = 0; i < ents.length; i++) {
const eid = ents[i]; // skip if diffing and no change
if (diff && prop[eid] === prop[$serializeShadow][eid]) {
continue;
}
count++; // write eid
view.setUint32(where, eid);
where += 4; // if property is an array
if (ArrayBuffer.isView(prop[eid])) {
const type = prop[eid].constructor.name.replace('Array', '');
const indexType = prop[eid][$indexType];
const indexBytes = prop[eid][$indexBytes]; // add space for count of dirty array elements
const countWhere2 = where;
where += 1;
let count2 = 0; // write index,value
for (let i = 0; i < prop[eid].length; i++) {
const value = prop[eid][i];
if (diff && prop[eid][i] === prop[eid][$serializeShadow][i]) {
continue;
} // write array index
view[`set${indexType}`](where, i);
where += indexBytes; // write value at that index
view[`set${type}`](where, value);
where += prop[eid].BYTES_PER_ELEMENT;
count2++;
} // write total element count
view[`set${indexType}`](countWhere2, count2);
} else {
// regular property values
const type = prop.constructor.name.replace('Array', ''); // set value next [type] bytes
view[`set${type}`](where, prop[eid]);
where += prop.BYTES_PER_ELEMENT; // sync shadow state
prop[$serializeShadow][eid] = prop[eid];
}
}
view.setUint32(countWhere, count);
}
return buffer.slice(0, where);
};
};
const defineDeserializer = target => {
const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap);
let [componentProps] = canonicalize(target);
return (world, packet) => {
if (isWorld) {
componentProps = [];
target[$componentMap].forEach((c, component) => {
componentProps.push(...component[$storeFlattened]);
});
}
const view = new DataView(packet);
let where = 0;
while (where < packet.byteLength) {
// pid
const pid = view.getUint8(where);
where += 1; // entity count
const entityCount = view.getUint32(where);
where += 4; // typed array
const ta = componentProps[pid]; // Get the properties and set the new state
for (let i = 0; i < entityCount; i++) {
let eid = view.getUint32(where);
where += 4; // if this world hasn't seen this eid yet
if (!world[$entityEnabled][eid]) {
// make a new entity for the data
eid = addEntity(world);
}
const component = ta[$storeBase]();
if (!hasComponent(world, component, eid)) {
addComponent(world, component, eid);
}
if (ArrayBuffer.isView(ta[eid])) {
const array = ta[eid];
const count = view[`get${array[$indexType]}`](where);
where += array[$indexBytes]; // iterate over count
for (let i = 0; i < count; i++) {
const index = view[`get${array[$indexType]}`](where);
where += array[$indexBytes];
const value = view[`get${array.constructor.name.replace('Array', '')}`](where);
where += array.BYTES_PER_ELEMENT;
ta[eid][index] = value;
}
} else {
let value = view[`get${ta.constructor.name.replace('Array', '')}`](where);
where += ta.BYTES_PER_ELEMENT;
ta[eid] = value;
}
}
}
};
};
const pipe = (...fns) => input => {
if (!input || Array.isArray(input) && input.length === 0) return;
fns = Array.isArray(fns[0]) ? fns[0] : fns;

@@ -860,3 +957,8 @@ let tmp = input;

const fn = fns[i];
tmp = fn(tmp);
if (Array.isArray(tmp)) {
tmp = tmp.reduce((a, v) => a.concat(fn(v)), []);
} else {
tmp = fn(tmp);
}
}

@@ -863,0 +965,0 @@

@@ -55,2 +55,3 @@ 'use strict';

const $subarray = Symbol('subarray');
const $tagStore = Symbol('tagStore');
const $queryShadow = Symbol('queryShadow');

@@ -68,11 +69,53 @@ const $serializeShadow = Symbol('serializeShadow');

const resizeRecursive = (store, size) => {
const resizeSubarray = (metadata, store, size) => {
const cursors = metadata[$subarrayCursors];
const type = store[$storeType];
const length = store[0].length;
const indexType = length < UNSIGNED_MAX.uint8 ? 'ui8' : length < UNSIGNED_MAX.uint16 ? 'ui16' : 'ui32';
const arrayCount = metadata[$storeArrayCounts][type];
const summedLength = Array(arrayCount).fill(0).reduce((a, p) => a + length, 0); // for threaded impl
// const summedBytesPerElement = Array(arrayCount).fill(0).reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0)
// const totalBytes = roundToMultiple4(summedBytesPerElement * summedLength * size)
// const buffer = new ArrayBuffer(totalBytes)
const array = new TYPES[type](summedLength * size);
array.set(metadata[$storeSubarrays][type]);
metadata[$storeSubarrays][type] = array;
metadata[$storeSubarrays][type][$queryShadow] = array.slice(0);
metadata[$storeSubarrays][type][$serializeShadow] = array.slice(0);
array[$indexType] = TYPES_NAMES[indexType];
array[$indexBytes] = TYPES[indexType].BYTES_PER_ELEMENT;
let end = 0;
for (let eid = 0; eid < size; eid++) {
const from = cursors[type] + eid * length;
const to = from + length;
store[eid] = metadata[$storeSubarrays][type].subarray(from, to);
store[eid].from = from;
store[eid].to = to;
store[eid][$queryShadow] = metadata[$storeSubarrays][type][$queryShadow].subarray(from, to);
store[eid][$serializeShadow] = metadata[$storeSubarrays][type][$serializeShadow].subarray(from, to);
store[eid][$subarray] = true;
store[eid][$indexType] = array[$indexType];
store[eid][$indexBytes] = array[$indexBytes];
end = to;
}
cursors[type] = end;
};
const resizeRecursive = (metadata, store, size) => {
Object.keys(store).forEach(key => {
const ta = store[key];
if (ta[$subarray]) return;else if (ArrayBuffer.isView(ta)) {
if (Array.isArray(ta)) {
resizeSubarray(metadata, ta, size);
store[$storeFlattened].push(ta);
} else if (ArrayBuffer.isView(ta)) {
store[key] = resize(ta, size);
store[$storeFlattened].push(store[key]);
store[key][$queryShadow] = resize(ta[$queryShadow], size);
store[key][$serializeShadow] = resize(ta[$serializeShadow], size);
} else if (typeof ta === 'object') {
resizeRecursive(store[key], size);
resizeRecursive(metadata, store[key], size);
}

@@ -82,46 +125,17 @@ });

const resizeSubarrays = (metadata, size) => {
Object.keys(metadata[$subarrayCursors]).forEach(k => {
metadata[$subarrayCursors][k] = 0;
});
const cursors = metadata[$subarrayCursors];
metadata[$storeFlattened].filter(store => !ArrayBuffer.isView(store)).forEach(store => {
const type = store[$storeType];
const length = store[0].length;
const arrayCount = metadata[$storeArrayCounts][type];
const summedLength = Array(arrayCount).fill(0).reduce((a, p) => a + length, 0); // for threaded impl
// const summedBytesPerElement = Array(arrayCount).fill(0).reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0)
// const totalBytes = roundToMultiple4(summedBytesPerElement * summedLength * size)
// const buffer = new ArrayBuffer(totalBytes)
const array = new TYPES[type](summedLength * size);
array.set(metadata[$storeSubarrays][type]);
metadata[$storeSubarrays][type] = array;
metadata[$storeSubarrays][type][$queryShadow] = array.slice(0);
metadata[$storeSubarrays][type][$serializeShadow] = array.slice(0);
for (let eid = 0; eid < size; eid++) {
const from = cursors[type] + eid * length;
const to = from + length;
store[eid] = metadata[$storeSubarrays][type].subarray(from, to);
store[eid].from = from;
store[eid].to = to;
store[eid][$queryShadow] = metadata[$storeSubarrays][type][$queryShadow].subarray(from, to);
store[eid][$serializeShadow] = metadata[$storeSubarrays][type][$serializeShadow].subarray(from, to);
store[eid][$subarray] = true;
store[eid][$indexType] = array[$indexType];
store[eid][$indexBytes] = array[$indexBytes];
}
});
};
const resizeStore = (store, size) => {
if (store[$tagStore]) return;
store[$storeSize] = size;
resizeRecursive(store, size);
resizeSubarrays(store, size);
store[$storeFlattened].length = 0;
Object.keys(store[$subarrayCursors]).forEach(k => {
store[$subarrayCursors][k] = 0;
});
resizeRecursive(store, store, size); // resizeSubarrays(store, size)
};
const resetStoreFor = (store, eid) => {
store[$storeFlattened].forEach(ta => {
if (ArrayBuffer.isView(ta)) ta[eid] = 0;else ta[eid].fill(0);
});
if (store[$storeFlattened]) {
store[$storeFlattened].forEach(ta => {
if (ArrayBuffer.isView(ta)) ta[eid] = 0;else ta[eid].fill(0);
});
}
};

@@ -187,5 +201,15 @@

const createStore = (schema, size = 10000) => {
const createStore = (schema, size) => {
const $store = Symbol('store');
if (!schema) return {};
if (!schema || !Object.keys(schema).length) {
// tag component
stores[$store] = {
[$storeSize]: size,
[$tagStore]: true,
[$storeBase]: () => stores[$store]
};
return stores[$store];
}
schema = JSON.parse(JSON.stringify(schema));

@@ -217,4 +241,4 @@ const arrayCounts = {};

}), {}),
[$storeArrayCounts]: arrayCounts,
[$storeFlattened]: []
[$storeFlattened]: [],
[$storeArrayCounts]: arrayCounts
};

@@ -226,2 +250,3 @@

a[k] = createTypeStore(a[k], size);
createShadows(a[k]);

@@ -231,3 +256,2 @@ a[k][$storeBase] = () => stores[$store];

metadata[$storeFlattened].push(a[k]);
createShadows(a[k]);
} else if (isArrayType(a[k])) {

@@ -253,12 +277,223 @@ const [type, length] = a[k];

return stores[$store];
} // tag component
}
};
let resized = false;
const setSerializationResized = v => {
resized = v;
};
stores[$store] = metadata;
const canonicalize = target => {
let componentProps = [];
let changedProps = new Set();
stores[$store][$storeBase] = () => stores[$store];
if (Array.isArray(target)) {
componentProps = target.map(p => {
if (typeof p === 'function' && p.name === 'QueryChanged') {
p()[$storeFlattened].forEach(prop => {
changedProps.add(prop);
});
return p()[$storeFlattened];
}
return stores[$store];
if (Object.getOwnPropertySymbols(p).includes($storeFlattened)) {
return p[$storeFlattened];
}
if (Object.getOwnPropertySymbols(p).includes($storeBase)) {
return p;
}
}).reduce((a, v) => a.concat(v), []);
}
return [componentProps, changedProps];
};
const defineSerializer = (target, maxBytes = 20000000) => {
const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap);
let [componentProps, changedProps] = canonicalize(target); // TODO: calculate max bytes based on target
const buffer = new ArrayBuffer(maxBytes);
const view = new DataView(buffer);
return ents => {
if (resized) {
[componentProps, changedProps] = canonicalize(target);
resized = false;
}
if (isWorld) {
componentProps = [];
target[$componentMap].forEach((c, component) => {
componentProps.push(...component[$storeFlattened]);
});
}
let world;
if (Object.getOwnPropertySymbols(ents).includes($componentMap)) {
world = ents;
ents = ents[$entityArray];
} else {
world = eidToWorld.get(ents[0]);
}
if (!ents.length) return;
let where = 0; // iterate over component props
for (let pid = 0; pid < componentProps.length; pid++) {
const prop = componentProps[pid];
const diff = changedProps.has(prop); // write pid
view.setUint8(where, pid);
where += 1; // save space for entity count
const countWhere = where;
where += 4;
let count = 0; // write eid,val
for (let i = 0; i < ents.length; i++) {
const eid = ents[i]; // skip if entity doesn't have this component
if (!hasComponent(world, prop[$storeBase](), eid)) {
continue;
} // skip if diffing and no change
if (diff && prop[eid] === prop[$serializeShadow][eid]) {
continue;
}
count++; // write eid
view.setUint32(where, eid);
where += 4;
if (prop[$tagStore]) {
continue;
} // if property is an array
if (ArrayBuffer.isView(prop[eid])) {
const type = prop[eid].constructor.name.replace('Array', '');
const indexType = prop[eid][$indexType];
const indexBytes = prop[eid][$indexBytes]; // add space for count of dirty array elements
const countWhere2 = where;
where += 1;
let count2 = 0; // write index,value
for (let i = 0; i < prop[eid].length; i++) {
const value = prop[eid][i];
if (diff && prop[eid][i] === prop[eid][$serializeShadow][i]) {
continue;
} // write array index
view[`set${indexType}`](where, i);
where += indexBytes; // write value at that index
view[`set${type}`](where, value);
where += prop[eid].BYTES_PER_ELEMENT;
count2++;
} // write total element count
view[`set${indexType}`](countWhere2, count2);
} else {
// regular property values
const type = prop.constructor.name.replace('Array', ''); // set value next [type] bytes
view[`set${type}`](where, prop[eid]);
where += prop.BYTES_PER_ELEMENT; // sync shadow state
prop[$serializeShadow][eid] = prop[eid];
}
}
view.setUint32(countWhere, count);
}
return buffer.slice(0, where);
};
};
const defineDeserializer = target => {
const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap);
let [componentProps] = canonicalize(target);
return (world, packet) => {
if (resized) {
[componentProps] = canonicalize(target);
resized = false;
}
if (isWorld) {
componentProps = [];
target[$componentMap].forEach((c, component) => {
componentProps.push(...component[$storeFlattened]);
});
}
const view = new DataView(packet);
let where = 0;
const newEntities = new Map();
while (where < packet.byteLength) {
// pid
const pid = view.getUint8(where);
where += 1; // entity count
const entityCount = view.getUint32(where);
where += 4; // component property
const prop = componentProps[pid]; // Get the entities and set their prop values
for (let i = 0; i < entityCount; i++) {
let eid = view.getUint32(where);
where += 4;
let newEid = newEntities.get(eid);
if (newEid !== undefined) {
eid = newEid;
} // if this world hasn't seen this eid yet
if (!world[$entityEnabled][eid]) {
// make a new entity for the data
const newEid = addEntity(world);
newEntities.set(eid, newEid);
eid = newEid;
}
const component = prop[$storeBase]();
if (!hasComponent(world, component, eid)) {
addComponent(world, component, eid); // console.log('hi',eid)
}
if (component[$tagStore]) {
continue;
}
if (ArrayBuffer.isView(prop[eid])) {
const array = prop[eid];
const count = view[`get${array[$indexType]}`](where);
where += array[$indexBytes]; // iterate over count
for (let i = 0; i < count; i++) {
const index = view[`get${array[$indexType]}`](where);
where += array[$indexBytes];
const value = view[`get${array.constructor.name.replace('Array', '')}`](where);
where += array.BYTES_PER_ELEMENT;
prop[eid][index] = value;
}
} else {
const value = view[`get${prop.constructor.name.replace('Array', '')}`](where);
where += prop.BYTES_PER_ELEMENT;
prop[eid] = value;
}
}
}
};
};
const $entityMasks = Symbol('entityMasks');

@@ -268,26 +503,16 @@ const $entityEnabled = Symbol('entityEnabled');

const $entityIndices = Symbol('entityIndices');
const NONE$1 = 2 ** 32; // need a global EID cursor which all worlds and all components know about
const NONE$1 = 2 ** 32;
const defaultSize = 100000; // 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; // removed eids should also be global to prevent memory leaks
let globalEntityCursor = 0;
let globalSize = defaultSize;
let resizeThreshold = () => globalSize - globalSize / 5;
const getGlobalSize = () => globalSize; // removed eids should also be global to prevent memory leaks
const removed = [];
const getEntityCursor = () => globalEntityCursor;
const resizeWorld = (world, size) => {
world[$size] = size;
world[$componentMap].forEach(c => {
resizeStore(c.store, 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);
for (let i = 0; i < world[$entityMasks].length; i++) {
const masks = world[$entityMasks][i];
world[$entityMasks][i] = resize(masks, size);
}
};
const eidToWorld = new Map();
const addEntity = world => {

@@ -297,11 +522,15 @@ const enabled = world[$entityEnabled];

enabled[eid] = 1;
world[$entityIndices][eid] = world[$entityArray].push(eid) - 1; // if data stores are 80% full
world[$entityIndices][eid] = world[$entityArray].push(eid) - 1;
eidToWorld.set(eid, world); // if data stores are 80% full
if (globalEntityCursor >= world[$warningSize]) {
if (globalEntityCursor >= resizeThreshold()) {
// grow by half the original size rounded up to a multiple of 4
const size = world[$size];
const size = globalSize;
const amount = Math.ceil(size / 2 / 4) * 4;
resizeWorld(world, size + amount);
world[$warningSize] = world[$size] - world[$size] / 5;
console.info(`๐Ÿ‘พ bitECS - resizing world from ${size} to ${size + amount}`);
const newSize = size + amount;
globalSize = newSize;
resizeWorlds(newSize);
resizeComponents(newSize);
setSerializationResized(true);
console.info(`๐Ÿ‘พ bitECS - resizing all worlds from ${size} to ${size + amount}`);
}

@@ -311,2 +540,16 @@

};
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) => {

@@ -325,12 +568,4 @@ const enabled = world[$entityEnabled]; // Check if entity is already removed

const index = world[$entityIndices][eid];
const swapped = world[$entityArray].pop();
popSwap(world, eid); // Clear entity bitmasks
if (swapped !== eid) {
world[$entityArray][index] = swapped;
world[$entityIndices][swapped] = index;
}
world[$entityIndices][eid] = NONE$1; // Clear entity bitmasks
for (let i = 0; i < world[$entityMasks].length; i++) world[$entityMasks][i][eid] = 0;

@@ -443,6 +678,3 @@ };

}
}; // const queryHooks = (q) => {
// while (q.entered.length) if (q.enter) { q.enter(q.entered.shift()) } else q.entered.length = 0
// while (q.exited.length) if (q.exit) { q.exit(q.exited.shift()) } else q.exited.length = 0
// }
};

@@ -535,3 +767,4 @@ const diff = q => {

q.entities.push(eid);
q.indices[eid] = q.entities.length - 1;
q.indices[eid] = q.entities.length - 1; // TODO: pop swap so dupes don't enter
q.entered.push(eid);

@@ -568,3 +801,4 @@ };

q.toRemove.push(eid);
world[$dirtyQueries].add(q);
world[$dirtyQueries].add(q); // TODO: pop swap so dupes don't enter
q.exited.push(eid);

@@ -574,3 +808,11 @@ };

const $componentMap = Symbol('componentMap');
const defineComponent = schema => createStore(schema);
const components = [];
const resizeComponents = size => {
components.forEach(component => resizeStore(component, size));
};
const defineComponent = schema => {
const component = createStore(schema, defaultSize);
if (schema && Object.keys(schema).length) components.push(component);
return component;
};
const incrementBitflag = world => {

@@ -628,3 +870,3 @@ world[$bitflag] *= 2;

};
const removeComponent = (world, component, eid, reset = false) => {
const removeComponent = (world, component, eid, reset = true) => {
const {

@@ -648,6 +890,26 @@ generationId,

const $size = Symbol('size');
const $warningSize = Symbol('warningSize');
const $resizeThreshold = Symbol('resizeThreshold');
const $bitflag = Symbol('bitflag');
const createWorld = (size = 10000) => {
const worlds = [];
const resizeWorlds = size => {
worlds.forEach(world => {
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);
for (let i = 0; i < world[$entityMasks].length; i++) {
const masks = world[$entityMasks][i];
world[$entityMasks][i] = resize(masks, size);
}
world[$resizeThreshold] = world[$size] - world[$size] / 5;
});
};
const createWorld = () => {
const world = {};
const size = getGlobalSize();
world[$size] = size;

@@ -663,8 +925,17 @@ world[$entityEnabled] = new Uint8Array(size);

world[$dirtyQueries] = new Set();
world[$warningSize] = size - size / 5;
worlds.push(world);
return world;
};
const defineSystem = update => {
const defineSystem = (fn1, fn2) => {
const update = fn2 !== undefined ? fn2 : fn1;
const create = fn2 !== undefined ? fn1 : undefined;
const init = new Set();
const system = world => {
if (create && !init.has(world)) {
create(world);
init.add(world);
}
update(world);

@@ -682,178 +953,4 @@ commitRemovals(world);

const canonicalize = target => {
let componentProps = [];
let changedProps = new Set();
if (Array.isArray(target)) {
componentProps = target.map(p => {
if (typeof p === 'function' && p.name === 'QueryChanged') {
p()[$storeFlattened].forEach(prop => {
changedProps.add(prop);
});
return p()[$storeFlattened];
}
if (Object.getOwnPropertySymbols(p).includes($storeFlattened)) {
return p[$storeFlattened];
}
if (Object.getOwnPropertySymbols(p).includes($storeBase)) {
return p;
}
}).reduce((a, v) => a.concat(v), []);
}
return [componentProps, changedProps];
};
const defineSerializer = (target, maxBytes = 20_000_000) => {
const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap);
let [componentProps, changedProps] = canonicalize(target); // TODO: calculate max bytes based on target
const buffer = new ArrayBuffer(maxBytes);
const view = new DataView(buffer);
return ents => {
if (isWorld) {
componentProps = [];
target[$componentMap].forEach((c, component) => {
componentProps.push(...component[$storeFlattened]);
});
}
if (Object.getOwnPropertySymbols(ents).includes($componentMap)) {
ents = ents[$entityArray];
}
if (!ents.length) return;
let where = 0; // iterate over component props
for (let pid = 0; pid < componentProps.length; pid++) {
const prop = componentProps[pid];
const diff = changedProps.has(prop); // write pid
view.setUint8(where, pid);
where += 1; // save space for entity count
const countWhere = where;
where += 4;
let count = 0; // write eid,val
for (let i = 0; i < ents.length; i++) {
const eid = ents[i]; // skip if diffing and no change
if (diff && prop[eid] === prop[$serializeShadow][eid]) {
continue;
}
count++; // write eid
view.setUint32(where, eid);
where += 4; // if property is an array
if (ArrayBuffer.isView(prop[eid])) {
const type = prop[eid].constructor.name.replace('Array', '');
const indexType = prop[eid][$indexType];
const indexBytes = prop[eid][$indexBytes]; // add space for count of dirty array elements
const countWhere2 = where;
where += 1;
let count2 = 0; // write index,value
for (let i = 0; i < prop[eid].length; i++) {
const value = prop[eid][i];
if (diff && prop[eid][i] === prop[eid][$serializeShadow][i]) {
continue;
} // write array index
view[`set${indexType}`](where, i);
where += indexBytes; // write value at that index
view[`set${type}`](where, value);
where += prop[eid].BYTES_PER_ELEMENT;
count2++;
} // write total element count
view[`set${indexType}`](countWhere2, count2);
} else {
// regular property values
const type = prop.constructor.name.replace('Array', ''); // set value next [type] bytes
view[`set${type}`](where, prop[eid]);
where += prop.BYTES_PER_ELEMENT; // sync shadow state
prop[$serializeShadow][eid] = prop[eid];
}
}
view.setUint32(countWhere, count);
}
return buffer.slice(0, where);
};
};
const defineDeserializer = target => {
const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap);
let [componentProps] = canonicalize(target);
return (world, packet) => {
if (isWorld) {
componentProps = [];
target[$componentMap].forEach((c, component) => {
componentProps.push(...component[$storeFlattened]);
});
}
const view = new DataView(packet);
let where = 0;
while (where < packet.byteLength) {
// pid
const pid = view.getUint8(where);
where += 1; // entity count
const entityCount = view.getUint32(where);
where += 4; // typed array
const ta = componentProps[pid]; // Get the properties and set the new state
for (let i = 0; i < entityCount; i++) {
let eid = view.getUint32(where);
where += 4; // if this world hasn't seen this eid yet
if (!world[$entityEnabled][eid]) {
// make a new entity for the data
eid = addEntity(world);
}
const component = ta[$storeBase]();
if (!hasComponent(world, component, eid)) {
addComponent(world, component, eid);
}
if (ArrayBuffer.isView(ta[eid])) {
const array = ta[eid];
const count = view[`get${array[$indexType]}`](where);
where += array[$indexBytes]; // iterate over count
for (let i = 0; i < count; i++) {
const index = view[`get${array[$indexType]}`](where);
where += array[$indexBytes];
const value = view[`get${array.constructor.name.replace('Array', '')}`](where);
where += array.BYTES_PER_ELEMENT;
ta[eid][index] = value;
}
} else {
let value = view[`get${ta.constructor.name.replace('Array', '')}`](where);
where += ta.BYTES_PER_ELEMENT;
ta[eid] = value;
}
}
}
};
};
const pipe = (...fns) => input => {
if (!input || Array.isArray(input) && input.length === 0) return;
fns = Array.isArray(fns[0]) ? fns[0] : fns;

@@ -864,3 +961,8 @@ let tmp = input;

const fn = fns[i];
tmp = fn(tmp);
if (Array.isArray(tmp)) {
tmp = tmp.reduce((a, v) => a.concat(fn(v)), []);
} else {
tmp = fn(tmp);
}
}

@@ -867,0 +969,0 @@

@@ -38,2 +38,19 @@ declare module 'bitecs' {

type ArrayByType = {
['bool']: boolean[];
[Types.i8]: Int8Array;
[Types.ui8]: Uint8Array;
[Types.ui8c]: Uint8ClampedArray;
[Types.i16]: Int16Array;
[Types.ui16]: Uint16Array;
[Types.i32]: Int32Array;
[Types.ui32]: Uint32Array;
[Types.f32]: Float32Array;
[Types.f64]: Float64Array;
}
type ComponentType<T extends ISchema> = {
[key in keyof T]: T[key] extends Type ? ArrayByType[T[key]] : T[key] extends ISchema ? ComponentType<T[key]> : unknown;
}
interface IWorld {

@@ -61,3 +78,3 @@ [key: string]: any

export function createWorld (size?: number): IWorld

@@ -68,3 +85,3 @@ export function addEntity (world: IWorld): number

export function registerComponents (world: IWorld, components: IComponent[]): void
export function defineComponent (schema: ISchema): IComponent
export function defineComponent <T extends ISchema>(schema: T): ComponentType<T>
export function addComponent (world: IWorld, component: IComponent, eid: number): void

@@ -71,0 +88,0 @@ export function removeComponent (world: IWorld, component: IComponent, eid: number): void

{
"name": "bitecs",
"version": "0.3.2",
"version": "0.3.3-a",
"description": "Functional, minimal, data-driven, ultra-high performance ECS library written in Javascript",

@@ -5,0 +5,0 @@ "license": "MPL-2.0",

@@ -13,3 +13,3 @@ # ๐Ÿ‘พ bitECS ๐Ÿ‘พ

| ๐Ÿƒ Zero dependencies | ๐ŸŒ Node or browser |
| ๐Ÿค `~5kb` gzipped | ๐Ÿท TypeScript support |
| ๐Ÿค `~4kb` gzipped | ๐Ÿท TypeScript support |
| โค Made with love | ๐Ÿ”บ [glMatrix](https://github.com/toji/gl-matrix) support |

@@ -176,3 +176,3 @@

## โ˜„ System
## ๐Ÿ›ธ System

@@ -179,0 +179,0 @@ Systems are functions and are run against a world to update componenet state of entities, or anything else.

@@ -15,4 +15,4 @@ import { strictEqual } from 'assert'

const eid = addEntity(world)
// console.log(TestComponent)
addComponent(world, TestComponent, eid)
const serialize = defineSerializer(world)

@@ -19,0 +19,0 @@ const deserialize = defineDeserializer(world)

import assert, { strictEqual } from 'assert'
import { defaultSize } from '../../src/Entity.js'
import { Types } from '../../src/index.js'

@@ -8,5 +9,5 @@ import { createStore, TYPES } from '../../src/Storage.js'

describe('Storage Integration Tests', () => {
it('should default to size of 1MM', () => {
const store = createStore({ value: Types.i8 })
strictEqual(store.value.length, 10000)
it('should default to size of 100k', () => {
const store = createStore({ value: Types.i8 }, defaultSize)
strictEqual(store.value.length, defaultSize)
})

@@ -19,5 +20,5 @@ it('should allow custom size', () => {

it('should create a store with ' + type, () => {
const store = createStore({ value: type })
const store = createStore({ value: type }, defaultSize)
assert(store.value instanceof TYPES[type])
strictEqual(store.value.length, 10000)
strictEqual(store.value.length, defaultSize)
})

@@ -45,3 +46,3 @@ })

it('should create flat stores', () => {
const store = createStore({ value1: Types.i8, value2: Types.ui16, value3: Types.f32 })
const store = createStore({ value1: Types.i8, value2: Types.ui16, value3: Types.f32 }, defaultSize)
assert(store.value1 != undefined)

@@ -55,5 +56,5 @@ assert(store.value1 instanceof Int8Array)

it('should create nested stores', () => {
const store1 = createStore({ nest: { value: Types.i8 } })
const store2 = createStore({ nest: { nest: { value: Types.ui32 } } })
const store3 = createStore({ nest: { nest: { nest: { value: Types.i16 } } } })
const store1 = createStore({ nest: { value: Types.i8 } }, defaultSize)
const store2 = createStore({ nest: { nest: { value: Types.ui32 } } }, defaultSize)
const store3 = createStore({ nest: { nest: { nest: { value: Types.i16 } } } }, defaultSize)
assert(store1.nest.value instanceof Int8Array)

@@ -60,0 +61,0 @@ assert(store2.nest.nest.value instanceof Uint32Array)

import assert, { strictEqual } from 'assert'
import { $componentMap } from '../../src/Component.js'
import { $entityEnabled, $entityMasks, resetGlobals, resizeWorld, addEntity } from '../../src/Entity.js'
import { $entityEnabled, $entityMasks, resetGlobals, addEntity, defaultSize } from '../../src/Entity.js'
import { $dirtyQueries, $queries, $queryMap } from '../../src/Query.js'
import { createWorld, $size, $bitflag } from '../../src/World.js'
const defaultSize = 10000
const growAmount = defaultSize + defaultSize / 2

@@ -14,10 +13,4 @@

})
it('should resize on-demand', () => {
it('should resize automatically at 80% of 10k', () => {
const world = createWorld()
resizeWorld(world, growAmount)
strictEqual(world[$entityMasks][0].length, growAmount)
strictEqual(world[$entityEnabled].length, growAmount)
})
it('should resize automatically at 80% of 1MM', () => {
const world = createWorld()
const n = defaultSize * 0.8

@@ -24,0 +17,0 @@ for (let i = 0; i < n; i++) {

import assert, { strictEqual } from 'assert'
import { $componentMap } from '../../src/Component.js'
import { $entityEnabled, $entityMasks, resetGlobals, resizeWorld, addEntity } from '../../src/Entity.js'
import { $entityEnabled, $entityMasks, resetGlobals, addEntity, defaultSize } from '../../src/Entity.js'
import { $dirtyQueries, $queries, $queryMap } from '../../src/Query.js'
import { createWorld, $size, $bitflag } from '../../src/World.js'
const defaultSize = 10000
describe('World Integration Tests', () => {

@@ -10,0 +8,0 @@ afterEach(() => {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with โšก๏ธ by Socket Inc