UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

272 lines (213 loc) • 6.72 kB
import List from "../../core/collection/list/List.js"; import { Reference } from "../reference/v2/Reference.js"; import Signal from "../../core/events/signal/Signal.js"; import { ProcessState } from "../../core/process/ProcessState.js"; import { assert } from "../../core/assert.js"; const TRANSIENT_STATES = [ ProcessState.Finalizing, ProcessState.Initializing, ProcessState.Starting, ProcessState.Stopping ]; /** * * @param {EnginePlugin} plugin * @param {Engine} engine * @returns {Promise} */ function navigate_to_initialized(plugin, engine) { const s = plugin.getState().getValue(); if (s === ProcessState.Initialized || s === ProcessState.Stopped) { return Promise.resolve(); } if (s === ProcessState.New || s === ProcessState.Finalized) { plugin.initialize(engine); return Promise.resolve(); } else if (s === ProcessState.Running) { return plugin.shutdown(); } else { return Promise.reject(`Unsupported initial state '${s}'`); } } /** * * @param {EnginePlugin} plugin * @param {Engine} engine * @returns {Promise} */ function navigate_to_finalized(plugin, engine) { const s = plugin.getState().getValue(); if (s === ProcessState.New || s === ProcessState.Finalized) { return Promise.resolve(); } if (s === ProcessState.Running) { return navigate_to_initialized(plugin, engine).then(() => { return plugin.finalize(); }); } else if (s === ProcessState.Stopped || s === ProcessState.Initialized) { plugin.finalize(); return Promise.resolve(); } else { return Promise.reject(`Unsupported initial state '${s}'`); } } /** * * @param {EnginePlugin} plugin * @param {Engine} engine * @returns {Promise} */ function navigate_to_running(plugin, engine) { const s = plugin.getState().getValue(); if (s === ProcessState.Running) { return Promise.resolve(); } if (s === ProcessState.Initialized || s === ProcessState.Stopped) { return plugin.startup(); } else if (s === ProcessState.New || s === ProcessState.Finalized) { return navigate_to_initialized(plugin, engine).then(() => { return plugin.startup(); }); } else { return Promise.reject(`Unsupported initial state '${s}'`); } } export class PluginReferenceContext { /** * * @param {EnginePlugin} instance * @param {Class<T>} klass */ constructor(instance, klass) { /** * * @type {Class<T>} * @private */ this.__klass = klass; /** * * @type {EnginePlugin} * @private */ this.__instance = instance; /** * * @type {List<Reference> } * @private */ this.__references = new List(); /** * * @type {List<Reference<EnginePlugin>>} */ this.dependency_references = new List(); this.on = { lastReleased: new Signal() }; /** * * @type {Promise<void>} * @private */ this.__transition = Promise.resolve(); } /** * Returns promise to the latest transition, useful to synchronization * @example await ctx.synchronize(); * @returns {Promise<void>} */ synchronize() { return this.__transition; } /** * * @param {ProcessState} target_state * @param {Engine} engine * @returns {Promise<unknown>} */ transition(target_state, engine) { assert.defined(engine, 'engine'); assert.notNull(engine, 'engine'); assert.enum(target_state, ProcessState, 'target_state'); if (TRANSIENT_STATES.includes(target_state)) { return Promise.reject(`Requested state '${target_state}' is transient. Only steady states are allowed`); } const plugin = this.__instance; const promise = this.__transition.then(() => { return new Promise((resolve, reject) => { const state = plugin.getState().getValue(); if (state === target_state) { return resolve(); } if (target_state === ProcessState.Initialized) { return navigate_to_initialized(plugin, engine) .then(resolve, reject); } else if (target_state === ProcessState.Running) { return navigate_to_running(plugin, engine) .then(resolve, reject); } else if (target_state === ProcessState.Finalized) { return navigate_to_finalized(plugin, engine) .then(resolve, reject); } else { reject(`Unsupported target state '${target_state}'`); } }); }); this.__transition = promise; return promise; } async dispose() { // shutdown the plugin const plugin = this.__instance; const state = plugin.getState(); if (state.getValue() === ProcessState.Running) { await plugin.shutdown(); } if (state.getValue() === ProcessState.Stopped || state.getValue() === ProcessState.Initialized) { await plugin.finalize(); } // release references of dependencies this.dependency_references.forEach(r => r.release()); } /** * * @param {Reference} ref * @private */ __handle_reference_released(ref) { const removed = this.__references.removeOneOf(ref); if (removed && this.__references.isEmpty()) { // last reference removed this.on.lastReleased.send2(this, ref); } } getKlass() { return this.__klass; } /** * * @returns {Reference} */ getReference() { const r = new Reference(); r.onReleased.add(this.__handle_reference_released, this); r.bind(this.__instance); this.__references.add(r); return r; } /** * * @returns {number} */ get referenceCount() { return this.__references.length; } /** * * @returns {EnginePlugin} */ get instance() { return this.__instance; } }