@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
272 lines (213 loc) • 6.72 kB
JavaScript
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;
}
}