Comparing version 0.2.29 to 0.2.30
@@ -67,2 +67,3 @@ declare module 'bitecs' { | ||
export function defineDeserializer (target: IWorld | IComponent | IComponentProp | QueryModifier): (world: IWorld, packet: ArrayBuffer) => void | ||
export function pipe(...fns: ((...args: any[]) => any)[]): (input: any) => any | ||
} |
const TYPES_ENUM = { | ||
bool: 'bool', | ||
i8: 'i8', | ||
@@ -14,3 +13,2 @@ ui8: 'ui8', | ||
const TYPES_NAMES = { | ||
bool: 'Uint8', | ||
i8: 'Int8', | ||
@@ -27,3 +25,2 @@ ui8: 'Uint8', | ||
const TYPES = { | ||
bool: 'bool', | ||
i8: Int8Array, | ||
@@ -52,3 +49,3 @@ ui8: Uint8Array, | ||
const $storeBase = Symbol('storeBase'); | ||
const $storeArrayCount = Symbol('storeArrayCount'); | ||
const $storeArrayCounts = Symbol('storeArrayCount'); | ||
const $storeSubarrays = Symbol('storeSubarrays'); | ||
@@ -86,3 +83,3 @@ const $storeCursor = Symbol('storeCursor'); | ||
Object.keys(store[$storeSubarrays]).forEach(type => { | ||
const arrayCount = store[$storeArrayCount]; | ||
const arrayCount = store[$storeArrayCounts]; | ||
const length = store[0].length; | ||
@@ -128,5 +125,6 @@ const summedBytesPerElement = Array(arrayCount).fill(0).reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0); | ||
const createArrayStore = (store, type, length) => { | ||
const size = store[$storeSize]; | ||
const cursors = store[$subarrayCursors]; | ||
const createArrayStore = (metadata, type, length) => { | ||
const size = metadata[$storeSize]; | ||
const store = Array(size); | ||
const cursors = metadata[$subarrayCursors]; | ||
const indexType = length < UNSIGNED_MAX.uint8 ? 'ui8' : length < UNSIGNED_MAX.uint16 ? 'ui16' : 'ui32'; | ||
@@ -136,12 +134,13 @@ if (!length) throw new Error('โ Must define a length for component array.'); | ||
if (!store[$storeSubarrays][type]) { | ||
const arrayCount = store[$storeArrayCount]; | ||
const summedBytesPerElement = Array(arrayCount).fill(0).reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0); | ||
const summedLength = Array(arrayCount).fill(0).reduce((a, p) => a + length, 0); | ||
const totalBytes = roundToMultiple4(summedBytesPerElement * summedLength * size); | ||
const buffer = new ArrayBuffer(totalBytes); | ||
const array = new TYPES[type](buffer); | ||
store[$storeSubarrays][type] = array; | ||
store[$storeSubarrays][type][$queryShadow] = array.slice(0); | ||
store[$storeSubarrays][type][$serializeShadow] = array.slice(0); | ||
if (!metadata[$storeSubarrays][type]) { | ||
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); | ||
metadata[$storeSubarrays][type] = array; | ||
metadata[$storeSubarrays][type][$queryShadow] = array.slice(0); | ||
metadata[$storeSubarrays][type][$serializeShadow] = array.slice(0); | ||
array[$indexType] = TYPES_NAMES[indexType]; | ||
@@ -157,5 +156,7 @@ array[$indexBytes] = TYPES[indexType].BYTES_PER_ELEMENT; | ||
const to = from + length; | ||
store[eid] = store[$storeSubarrays][type].subarray(from, to); | ||
store[eid][$queryShadow] = store[$storeSubarrays][type][$queryShadow].subarray(from, to); | ||
store[eid][$serializeShadow] = store[$storeSubarrays][type][$serializeShadow].subarray(from, to); | ||
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; | ||
@@ -180,21 +181,20 @@ store[eid][$indexType] = TYPES_NAMES[indexType]; | ||
const $store = Symbol('store'); | ||
if (!schema) return {}; | ||
schema = JSON.parse(JSON.stringify(schema)); | ||
const arrayCounts = {}; | ||
if (schema.constructor.name === 'Map') { | ||
schema[$storeSize] = size; | ||
return schema; | ||
} | ||
const collectArrayCounts = s => { | ||
const keys = Object.keys(s); | ||
schema = JSON.parse(JSON.stringify(schema)); | ||
const collectArrayCount = (count, key) => { | ||
if (isArrayType(schema[key])) { | ||
count++; | ||
} else if (schema[key] instanceof Object) { | ||
count += Object.keys(schema[key]).reduce(collectArrayCount, 0); | ||
for (const k of keys) { | ||
if (isArrayType(s[k])) { | ||
if (!arrayCounts[s[k][0]]) arrayCounts[s[k][0]] = 0; | ||
arrayCounts[s[k][0]]++; | ||
} else if (s[k] instanceof Object) { | ||
collectArrayCounts(s[k]); | ||
} | ||
} | ||
return count; | ||
}; | ||
const arrayCount = Object.keys(schema).reduce(collectArrayCount, 0); | ||
collectArrayCounts(schema); | ||
const metadata = { | ||
@@ -209,3 +209,3 @@ [$storeSize]: size, | ||
}), {}), | ||
[$storeArrayCount]: arrayCount, | ||
[$storeArrayCounts]: arrayCounts, | ||
[$storeFlattened]: [] | ||
@@ -224,4 +224,3 @@ }; | ||
} else if (isArrayType(a[k])) { | ||
const type = a[k][0]; | ||
const length = a[k][1]; | ||
const [type, length] = a[k]; | ||
a[k] = createArrayStore(metadata, type, length); | ||
@@ -245,5 +244,10 @@ | ||
return stores[$store]; | ||
} | ||
} // tag component | ||
return {}; | ||
stores[$store] = metadata; | ||
stores[$store][$storeBase] = () => stores[$store]; | ||
return stores[$store]; | ||
}; | ||
@@ -544,3 +548,3 @@ | ||
const $queryComponents = Symbol('queryComponents'); | ||
const NONE$1 = 2 ** 32; // TODO: add ability to have multiple hooks by wrapping last hook in a function that calls this hook and last hook(s) | ||
const NONE$1 = 2 ** 32; // TODO: linked list of functions | ||
@@ -770,3 +774,3 @@ const enterQuery = (world, query, fn) => { | ||
}; | ||
const addComponent = (world, component, eid) => { | ||
const addComponent = (world, component, eid, reset = false) => { | ||
if (!world[$componentMap].has(component)) registerComponent(world, component); | ||
@@ -779,6 +783,4 @@ if (hasComponent(world, component, eid)) return; // Add bitflag to entity bitmask | ||
} = world[$componentMap].get(component); | ||
world[$entityMasks][generationId][eid] |= bitflag; // Zero out each property value | ||
world[$entityMasks][generationId][eid] |= bitflag; // todo: archetype graph | ||
resetStoreFor(component, eid); // todo: archetype graph | ||
world[$queries].forEach(query => { | ||
@@ -788,5 +790,7 @@ if (!queryCheckComponent(world, query, component)) return; | ||
if (match) queryAddEntity(world, query, eid); | ||
}); | ||
}); // Zero out each property value | ||
if (reset) resetStoreFor(component, eid); | ||
}; | ||
const removeComponent = (world, component, eid) => { | ||
const removeComponent = (world, component, eid, reset = false) => { | ||
const { | ||
@@ -804,3 +808,5 @@ generationId, | ||
world[$entityMasks][generationId][eid] &= ~bitflag; | ||
world[$entityMasks][generationId][eid] &= ~bitflag; // Zero out each property value | ||
if (reset) resetStoreFor(component, eid); | ||
}; | ||
@@ -849,2 +855,4 @@ | ||
} | ||
return tmp; | ||
}; | ||
@@ -851,0 +859,0 @@ const Types = TYPES_ENUM; |
@@ -6,3 +6,2 @@ 'use strict'; | ||
const TYPES_ENUM = { | ||
bool: 'bool', | ||
i8: 'i8', | ||
@@ -19,3 +18,2 @@ ui8: 'ui8', | ||
const TYPES_NAMES = { | ||
bool: 'Uint8', | ||
i8: 'Int8', | ||
@@ -32,3 +30,2 @@ ui8: 'Uint8', | ||
const TYPES = { | ||
bool: 'bool', | ||
i8: Int8Array, | ||
@@ -57,3 +54,3 @@ ui8: Uint8Array, | ||
const $storeBase = Symbol('storeBase'); | ||
const $storeArrayCount = Symbol('storeArrayCount'); | ||
const $storeArrayCounts = Symbol('storeArrayCount'); | ||
const $storeSubarrays = Symbol('storeSubarrays'); | ||
@@ -91,3 +88,3 @@ const $storeCursor = Symbol('storeCursor'); | ||
Object.keys(store[$storeSubarrays]).forEach(type => { | ||
const arrayCount = store[$storeArrayCount]; | ||
const arrayCount = store[$storeArrayCounts]; | ||
const length = store[0].length; | ||
@@ -133,5 +130,6 @@ const summedBytesPerElement = Array(arrayCount).fill(0).reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0); | ||
const createArrayStore = (store, type, length) => { | ||
const size = store[$storeSize]; | ||
const cursors = store[$subarrayCursors]; | ||
const createArrayStore = (metadata, type, length) => { | ||
const size = metadata[$storeSize]; | ||
const store = Array(size); | ||
const cursors = metadata[$subarrayCursors]; | ||
const indexType = length < UNSIGNED_MAX.uint8 ? 'ui8' : length < UNSIGNED_MAX.uint16 ? 'ui16' : 'ui32'; | ||
@@ -141,12 +139,13 @@ if (!length) throw new Error('โ Must define a length for component array.'); | ||
if (!store[$storeSubarrays][type]) { | ||
const arrayCount = store[$storeArrayCount]; | ||
const summedBytesPerElement = Array(arrayCount).fill(0).reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0); | ||
const summedLength = Array(arrayCount).fill(0).reduce((a, p) => a + length, 0); | ||
const totalBytes = roundToMultiple4(summedBytesPerElement * summedLength * size); | ||
const buffer = new ArrayBuffer(totalBytes); | ||
const array = new TYPES[type](buffer); | ||
store[$storeSubarrays][type] = array; | ||
store[$storeSubarrays][type][$queryShadow] = array.slice(0); | ||
store[$storeSubarrays][type][$serializeShadow] = array.slice(0); | ||
if (!metadata[$storeSubarrays][type]) { | ||
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); | ||
metadata[$storeSubarrays][type] = array; | ||
metadata[$storeSubarrays][type][$queryShadow] = array.slice(0); | ||
metadata[$storeSubarrays][type][$serializeShadow] = array.slice(0); | ||
array[$indexType] = TYPES_NAMES[indexType]; | ||
@@ -162,5 +161,7 @@ array[$indexBytes] = TYPES[indexType].BYTES_PER_ELEMENT; | ||
const to = from + length; | ||
store[eid] = store[$storeSubarrays][type].subarray(from, to); | ||
store[eid][$queryShadow] = store[$storeSubarrays][type][$queryShadow].subarray(from, to); | ||
store[eid][$serializeShadow] = store[$storeSubarrays][type][$serializeShadow].subarray(from, to); | ||
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; | ||
@@ -185,21 +186,20 @@ store[eid][$indexType] = TYPES_NAMES[indexType]; | ||
const $store = Symbol('store'); | ||
if (!schema) return {}; | ||
schema = JSON.parse(JSON.stringify(schema)); | ||
const arrayCounts = {}; | ||
if (schema.constructor.name === 'Map') { | ||
schema[$storeSize] = size; | ||
return schema; | ||
} | ||
const collectArrayCounts = s => { | ||
const keys = Object.keys(s); | ||
schema = JSON.parse(JSON.stringify(schema)); | ||
const collectArrayCount = (count, key) => { | ||
if (isArrayType(schema[key])) { | ||
count++; | ||
} else if (schema[key] instanceof Object) { | ||
count += Object.keys(schema[key]).reduce(collectArrayCount, 0); | ||
for (const k of keys) { | ||
if (isArrayType(s[k])) { | ||
if (!arrayCounts[s[k][0]]) arrayCounts[s[k][0]] = 0; | ||
arrayCounts[s[k][0]]++; | ||
} else if (s[k] instanceof Object) { | ||
collectArrayCounts(s[k]); | ||
} | ||
} | ||
return count; | ||
}; | ||
const arrayCount = Object.keys(schema).reduce(collectArrayCount, 0); | ||
collectArrayCounts(schema); | ||
const metadata = { | ||
@@ -214,3 +214,3 @@ [$storeSize]: size, | ||
}), {}), | ||
[$storeArrayCount]: arrayCount, | ||
[$storeArrayCounts]: arrayCounts, | ||
[$storeFlattened]: [] | ||
@@ -229,4 +229,3 @@ }; | ||
} else if (isArrayType(a[k])) { | ||
const type = a[k][0]; | ||
const length = a[k][1]; | ||
const [type, length] = a[k]; | ||
a[k] = createArrayStore(metadata, type, length); | ||
@@ -250,5 +249,10 @@ | ||
return stores[$store]; | ||
} | ||
} // tag component | ||
return {}; | ||
stores[$store] = metadata; | ||
stores[$store][$storeBase] = () => stores[$store]; | ||
return stores[$store]; | ||
}; | ||
@@ -549,3 +553,3 @@ | ||
const $queryComponents = Symbol('queryComponents'); | ||
const NONE$1 = 2 ** 32; // TODO: add ability to have multiple hooks by wrapping last hook in a function that calls this hook and last hook(s) | ||
const NONE$1 = 2 ** 32; // TODO: linked list of functions | ||
@@ -775,3 +779,3 @@ const enterQuery = (world, query, fn) => { | ||
}; | ||
const addComponent = (world, component, eid) => { | ||
const addComponent = (world, component, eid, reset = false) => { | ||
if (!world[$componentMap].has(component)) registerComponent(world, component); | ||
@@ -784,6 +788,4 @@ if (hasComponent(world, component, eid)) return; // Add bitflag to entity bitmask | ||
} = world[$componentMap].get(component); | ||
world[$entityMasks][generationId][eid] |= bitflag; // Zero out each property value | ||
world[$entityMasks][generationId][eid] |= bitflag; // todo: archetype graph | ||
resetStoreFor(component, eid); // todo: archetype graph | ||
world[$queries].forEach(query => { | ||
@@ -793,5 +795,7 @@ if (!queryCheckComponent(world, query, component)) return; | ||
if (match) queryAddEntity(world, query, eid); | ||
}); | ||
}); // Zero out each property value | ||
if (reset) resetStoreFor(component, eid); | ||
}; | ||
const removeComponent = (world, component, eid) => { | ||
const removeComponent = (world, component, eid, reset = false) => { | ||
const { | ||
@@ -809,3 +813,5 @@ generationId, | ||
world[$entityMasks][generationId][eid] &= ~bitflag; | ||
world[$entityMasks][generationId][eid] &= ~bitflag; // Zero out each property value | ||
if (reset) resetStoreFor(component, eid); | ||
}; | ||
@@ -854,2 +860,4 @@ | ||
} | ||
return tmp; | ||
}; | ||
@@ -856,0 +864,0 @@ const Types = TYPES_ENUM; |
@@ -67,2 +67,3 @@ declare module 'bitecs' { | ||
export function defineDeserializer (target: IWorld | IComponent | IComponentProp | QueryModifier): (world: IWorld, packet: ArrayBuffer) => void | ||
export function pipe(...fns: ((...args: any[]) => any)[]): (input: any) => any | ||
} |
{ | ||
"name": "bitecs", | ||
"version": "0.2.29", | ||
"description": "Tiny, data-driven, high performance ECS library written in Javascript", | ||
"version": "0.2.30", | ||
"description": "Functional, minimal, data-driven, ultra-high performance ECS library written in Javascript", | ||
"license": "MPL-2.0", | ||
@@ -28,3 +28,3 @@ "type": "module", | ||
"build": "rollup -c", | ||
"test": "mocha ./test", | ||
"test": "c8 mocha test --recursive", | ||
"docs": "node scripts/docs.js", | ||
@@ -38,2 +38,3 @@ "dist": "npm run test && npm run build && npm run docs" | ||
"babel": "^6.23.0", | ||
"c8": "^7.7.2", | ||
"dmd-readable": "SupremeTechnopriest/dmd-readable", | ||
@@ -40,0 +41,0 @@ "globby": "^11.0.1", |
@@ -107,9 +107,6 @@ # ๐พ bitECS ๐พ | ||
const Velocity = defineComponent(Vector3) | ||
const List = defineComponent({ values: [f32, 3] }) // [type, length] | ||
const Tag = defineComponent() | ||
``` | ||
Array types are defined as such: | ||
```js | ||
const List = defineComponent({ values: [f32, 6] }) // [type, length] | ||
``` | ||
Add components to an entity in a world: | ||
@@ -119,5 +116,7 @@ ```js | ||
addComponent(world, Velocity, eid) | ||
addComponent(world, List, eid) | ||
addComponent(world, Tag, eid) | ||
``` | ||
Component data accessed directly via `eid`, there are no getters or setters: | ||
Component data is accessed directly via `eid`, there are no getters or setters: | ||
* This is how high performance iteration is achieved | ||
@@ -127,2 +126,4 @@ ```js | ||
Velocity.y[eid] = 1 | ||
List.values[eid].set([1,2,3]) | ||
``` | ||
@@ -231,6 +232,6 @@ | ||
Serialization can take a whole world as a config and will serialize all component stores in that world: | ||
Serialization can take a world as a config and will serialize all component stores registered in that world: | ||
```js | ||
const serialize = createSerializer(world) | ||
const deserialize = createDeserializer(world) | ||
const serialize = defineSerializer(world) | ||
const deserialize = defineDeserializer(world) | ||
``` | ||
@@ -258,4 +259,4 @@ | ||
```js | ||
const serializeMovement = createSerializer([Position, Velocity.x, Velocity.y]) | ||
const deserializeMovement = createDeserializer([Position, Velocity.x, Velocity.y]) | ||
const serializeMovement = defineSerializer([Position, Velocity.x, Velocity.y]) | ||
const deserializeMovement = defineDeserializer([Position, Velocity.x, Velocity.y]) | ||
``` | ||
@@ -273,3 +274,3 @@ | ||
```js | ||
const serializeOnlyChangedPositions = createSerializer([Changed(Position)]) | ||
const serializeOnlyChangedPositions = defineSerializer([Changed(Position)]) | ||
@@ -276,0 +277,0 @@ const serializeChangedMovementQuery = pipe(movementQuery, serializeOnlyChangedPositions) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
17
1826
280
231124
11