UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

193 lines (141 loc) • 6.3 kB
import { assert } from "../../../core/assert.js"; import { current_time_in_seconds } from "../../../core/time/current_time_in_seconds.js"; import { logger } from "../../logging/GlobalLogger.js"; import { SerializationFlags, SerializationMetadata } from "../components/SerializationMetadata.js"; import { BinaryCollectionSerializer } from "./binary/collection/BinaryCollectionSerializer.js"; import { BinaryObjectSerializationAdapter } from "./binary/object/BinaryObjectSerializationAdapter.js"; import { COMPONENT_SERIALIZATION_TRANSIENT_FIELD } from "./COMPONENT_SERIALIZATION_TRANSIENT_FIELD.js"; /** * @template T * @param {Class<T>|constructor|function|{serializable?:boolean}} componentClass * @returns {boolean} */ function isComponentClassSerializable(componentClass) { return componentClass.serializable !== false; } /** * Whether or not an entity should be serialized at all * @param {number} entity * @param {EntityComponentDataset} dataset * @param {number} smComponentIndex * @param {Object} componentInstance * @returns {boolean} */ function isEntityTransient(entity, dataset, smComponentIndex, componentInstance) { if (smComponentIndex === -1) { return false; } //check component instance flag if (componentInstance[COMPONENT_SERIALIZATION_TRANSIENT_FIELD] === true) { return true; } /** * * @type {SerializationMetadata} */ const serializationMetadata = dataset.getComponentByIndex(entity, smComponentIndex); if (serializationMetadata === undefined) { return false; } const isTransient = serializationMetadata.getFlag(SerializationFlags.Transient); return isTransient; } class BinaryBufferSerializer { /** * * @type {BinarySerializationRegistry} */ registry = null; /** * * @type {Engine|null} */ engine = null; /** * * @param {EntityComponentDataset} dataset * @param {BinaryBuffer} buffer */ process(buffer, dataset) { console.groupCollapsed('Serialization'); console.time('serializing'); const smComponentIndex = dataset.computeComponentTypeIndex(SerializationMetadata); // get a list of serializable component classes const serializableComponentTypes = dataset.getComponentTypeMap().filter(isComponentClassSerializable); const numSerializableTypes = serializableComponentTypes.length; const typeCountAddress = buffer.position; buffer.writeUint32(numSerializableTypes); const collectionSerializer = new BinaryCollectionSerializer(); const registry = this.registry; collectionSerializer.setRegistry(registry); collectionSerializer.setBuffer(buffer); const objectAdapter = new BinaryObjectSerializationAdapter(); objectAdapter.initialize(registry); let numTypesWritten = 0; let i; const engine = this.engine; for (i = 0; i < numSerializableTypes; i++) { const componentType = serializableComponentTypes[i]; const positionComponentsStart = buffer.position; const className = componentType.typeName; if (className === undefined) { throw new Error(`Component type has no .typeName property`); } collectionSerializer.setClass(className); const problemLog = []; const initialized = collectionSerializer.initialize({ /** * * @param {string} className * @param {Class} klass * @param {BinaryClassSerializationAdapter} adapter */ adapterOptionsSupplier(className, klass, adapter) { return [engine, objectAdapter]; }, problemConsumer(message) { problemLog.push(message); } }); if (!initialized) { logger.error(`Failed to initialize serializer for class '${className}': ${problemLog.join('; ')}`) continue; } const __start_time = current_time_in_seconds(); let lastEntity = 0; dataset.traverseComponents(componentType, function (componentInstance, entity) { if (isEntityTransient(entity, dataset, smComponentIndex, componentInstance)) { //skip return; } assert.ok(entity >= lastEntity, `entity[=${entity}] < lastEntity[=${lastEntity}]`); //write only the entity step const step = entity - lastEntity; lastEntity = entity; collectionSerializer.write(step, componentInstance); }); const numComponentsWritten = collectionSerializer.getElementCount(); const __end_time = current_time_in_seconds(); collectionSerializer.finalize(); if (numComponentsWritten > 0) { numTypesWritten++; const currentPosition = buffer.position; //print size of the component set const componentsByteSize = currentPosition - positionComponentsStart; console.log(`Component ${componentType.typeName} written. Count: ${numComponentsWritten}, Bytes: ${componentsByteSize}, Bytes/component: ${Math.ceil(componentsByteSize / numComponentsWritten)}. Time taken: ${((__end_time - __start_time) * 1000).toFixed(2)}ms`); } else { //no elements written, lets re-wind to skip the type buffer.position = positionComponentsStart; } } //go back and patch actual number of written classes const p = buffer.position; buffer.position = typeCountAddress; buffer.writeUint32(numTypesWritten); //restore position buffer.position = p; console.timeEnd('serializing'); console.groupEnd(); } } export default BinaryBufferSerializer;