UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

250 lines (193 loc) • 6.1 kB
import { assert } from "../../../core/assert.js"; import { BinaryBuffer } from "../../../core/binary/BinaryBuffer.js"; import { noop } from "../../../core/function/noop.js"; import DataType from "../../../core/parser/simple/DataType.js"; import { BinaryObjectSerializationAdapter } from "../../ecs/storage/binary/object/BinaryObjectSerializationAdapter.js"; import { Blackboard } from "./Blackboard.js"; export class BlackboardDynamicStorageAdapter { constructor() { /** * * @type {Blackboard} * @private */ this.__blackboard = null; /** * * @type {Storage} * @private */ this.__storage = null; /** * * @type {boolean} * @private */ this.__is_linked = false; /** * * @type {string} * @private */ this.__storage_key = ""; /** * * @type {BinaryObjectSerializationAdapter} * @private */ this.__serialization_adapter = new BinaryObjectSerializationAdapter(); this.__serialization_in_progress = false; this.__need_write = false; /** * "setTimeout" Handle * @type {number} * @private */ this.__write_timeout_handle = -1; /** * In milliseconds * @type {number} * @private */ this.__write_timeout_delay = 200; /** * Last time write happened * @type {number} * @private */ this.__last_write_timestamp = 0; } setStorageKey(value) { this.__storage_key = value; } /** * @param {BinarySerializationRegistry} s */ setSerialization(s) { assert.defined(s, 's'); this.__serialization_adapter.initialize(s); } /** * @param {Blackboard} v */ setBlackboard(v) { this.__blackboard = v; } /** * @param {Storage} v */ setStorage(v) { this.__storage = v; } /** * Read data from storage and populate blackboard accordingly */ async read() { const data = await this.__storage.promiseLoadBinary(this.__storage_key); this.__blackboard.reset(); if (data === undefined) { // no data return; } const buffer = BinaryBuffer.fromArrayBuffer(data); const object = this.__serialization_adapter.deserialize(buffer); if (!(object instanceof Blackboard)) { throw new Error('Expected blackboard, got something else'); } this.__blackboard.copy(object); } __handle_serialization_finalized() { this.__serialization_in_progress = false; if (this.__need_write) { // write is required this.write(); } } /** * Write data to storage, this is not guaranteed to result in immediate writing */ write() { if (this.__serialization_in_progress) { this.__need_write = true; return; } if (this.__write_timeout_handle !== -1) { // there is a pending write already return; } const current_time = performance.now(); const time_since_last_write = current_time - this.__last_write_timestamp; const time_until_next_write = this.__write_timeout_delay - time_since_last_write; if (time_until_next_write <= 0) { // enough time has passed since last write to do an immediate write this.__last_write_timestamp = current_time; this.__write_immediate(); return; } // not enough time has passed since last write, let's schedule another write this.__write_timeout_handle = setTimeout(this.__handle_write_timeout.bind(this), time_until_next_write); } __handle_write_timeout() { // clear timeout handle this.__write_timeout_handle = -1; this.__write_immediate(); } /** * Perform write right now * @private */ __write_immediate() { this.__serialization_in_progress = true; this.__need_write = false; const buffer = new BinaryBuffer(); this.__serialization_adapter.serialize(buffer, this.__blackboard, Blackboard.typeName); buffer.trim(); const success = () => { this.__handle_serialization_finalized(); } const reject = () => { this.__handle_serialization_finalized(); } this.__storage.storeBinary(this.__storage_key, buffer.data, success, reject, noop); } /** * * @param {string} name * @private */ __watch_property(name) { const value = this.__blackboard.acquire(name, DataType.Any); value.onChanged.add(this.write, this); this.write(); } /** * * @param {string} name * @private */ __unwatch_property(name) { const value = this.__blackboard.acquire(name, DataType.Any); value.onChanged.remove(this.write, this); } async link() { if (this.__is_linked) { return; } this.__is_linked = true; await this.read(); this.__blackboard.traverse((name, value, type) => { this.__watch_property(name); }); this.__blackboard.on.added.add(this.__watch_property, this); } async unlink() { if (!this.__is_linked) { return; } this.__is_linked = false; this.__blackboard.traverse((name, value, type) => { this.__unwatch_property(name); }); this.__blackboard.on.added.remove(this.__watch_property, this); } }