UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

321 lines (247 loc) • 7.03 kB
import { assert } from "../../../assert.js"; import { BitSet } from "../../../binary/BitSet.js"; import { DataType } from "../type/DataType.js"; import { NodeDescription } from "./NodeDescription.js"; import { Port } from "./Port.js"; /** * * @param {Port} port * @return {DataType} */ function getPortType(port) { return port.dataType; } export class NodeRegistry { /** * ID -> Node mapping * @type {Map<number, NodeDescription>} */ nodes = new Map(); /** * ID -> DataType mapping * @type {Map<number, DataType>} */ types = new Map(); /** * @returns {NodeDescription[]} */ getNodesAsArray() { return Array.from(this.nodes.values()); } /** * * @param {NodeDescription} node * @returns {boolean} true if node was added, false if node already exists */ addNode(node) { assert.defined(node, "node"); assert.notNull(node, "node"); assert.equal(node.isNodeDescription, true, 'node.isNodeDescription !== true'); const existing_node = this.nodes.get(node.id); if (existing_node !== undefined) { if (existing_node !== node && !existing_node.equals(node)) { // node with the same ID exists and is not the same node throw new Error(`A different node with the same ID ${node.id} is already registered. IDs of all nodes in the registry must be unique`); } // console.warn(`Node with id ${node.id} already exists`); return false; } //extract types node.getPorts().map(getPortType).forEach(this.addType, this); this.nodes.set(node.id, node); return true; } /** * * @param {NodeDescription} node * @returns {boolean} */ hasNode(node) { assert.defined(node, "node"); assert.notNull(node, "node"); assert.equal(node.isNodeDescription, true, 'node.isNodeDescription !== true'); const existing = this.nodes.get(node.id); if (existing === undefined) { return false; } if (existing !== node) { return false; } return true; } /** * * @param {NodeDescription} node * @returns {boolean} */ removeNode(node) { assert.defined(node, "node"); assert.notNull(node, "node"); assert.equal(node.isNodeDescription, true, 'node.isNodeDescription !== true'); if (!this.hasNode(node)) { // not found return false; } this.nodes.delete(node.id); return true; } /** * Given a class inheriting from NodeDescription, get all registered instances * Matching is strict, instances of subclasses are not matched * @param {Type<NodeDescription>} Klass * @returns {NodeDescription[]} */ getNodesByClass(Klass) { const result = []; for (const [id, node] of this.nodes) { if (node.constructor === Klass) { result.push(node); } } return result; } /** * * @param {DataType} type */ addType(type) { if (this.hasType(type)) { return false; } this.types.set(type.id, type); return true; } /** * * @param {DataType} type * @return {boolean} */ hasType(type) { assert.defined(type, 'type'); assert.notNull(type, 'type'); return this.types.has(type.id); } /** * @param {number} id * @returns {DataType|undefined} */ getTypeById(id) { return this.types.get(id); } /** * * @returns {number} */ generateTypeId() { const ids = new BitSet(); this.types.forEach((v, id) => ids.set(id, true)); const id = ids.nextClearBit(0); if (id === -1) { return 0; } else { return id; } } /** * * @param {String} name * @returns {DataType} */ createType(name) { const dataType = new DataType(); dataType.name = name; dataType.id = this.generateTypeId(); this.addType(dataType); return dataType; } /** * * @param {number} id * @returns {NodeDescription|undefined} */ getNode(id) { return this.nodes.get(id); } /** * * @param json * @returns {Port} */ parsePort(json) { const port = new Port(); port.dataType = this.types.get(json.dataType); port.direction = json.direction; port.name = json.name; return port; } /** * NOTE: this only works with un-extended node descriptions * @param json * @returns {NodeDescription} */ parseNode(json) { const node = new NodeDescription(); node.name = json.name; const jPorts = json.ports; for (const p in jPorts) { const jPort = jPorts[p]; const portId = parseInt(p); const port = this.parsePort(jPort); port.id = portId; node.ports.push(port); } return node; } fromJSON(json) { const jNodes = json.nodes; const jTypes = json.types; this.types.clear(); this.nodes.clear(); //parse types for (const p in jTypes) { const id = parseInt(p); const dataType = new DataType(); dataType.id = id; dataType.name = jTypes[p]; this.types.set(id, dataType); } for (const p in jNodes) { const id = parseInt(p); const jNode = jNodes[p]; const node = this.parseNode(jNode); node.id = id; this.nodes.set(id, node); } } toJSON() { //write types const types = {}; for (const [id, type] of this.types) { types[id] = type.name; } const nodes = {}; for (const [id, node] of this.nodes) { const ports = {}; node.getPorts().forEach(port => { ports[port.id] = { dataType: port.dataType.id, direction: port.direction, name: port.name }; }); nodes[id] = { name: node.name, ports }; } return { types, nodes }; } } /** * @readonly * @type {boolean} */ NodeRegistry.prototype.isNodeRegistry = true;