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