@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
206 lines (150 loc) • 5.94 kB
JavaScript
import { assert } from "../../../assert.js";
import { abstractJSONDeserializer } from "../../../json/abstractJSONDeserializer.js";
import { NodeInstance } from "../node/NodeInstance.js";
import { NodeRegistry } from "../node/NodeRegistry.js";
/**
*
* @param {NodeInstance} node
* @param {*} json
* @param {Map<string, function>} deserializers
*/
function nodeParametersFromJSON(node, json, deserializers) {
for (const key in json) {
const jParameter = json[key];
const value = abstractJSONDeserializer(jParameter, deserializers);
node.setParameterValue(key, value);
}
}
/**
*
* @param {[]} j_descriptions
* @param {Map<string,function>} deserializers
* @param {NodeRegistry} node_registry
* @param {boolean} allow_node_id_collisions
* @returns {Promise<NodeDescription[]>}
*/
async function deserializeNodeDescriptors(j_descriptions, deserializers, node_registry, allow_node_id_collisions) {
/**
*
* @type {NodeDescription[]}
*/
const result = [];
/**
*
* @type {{index:number, promise:Promise<NodeDescription>}[]}
*/
const deferred = [];
/**
*
* @type {NodeDescription[]}
*/
const deserialized = [];
const node_description_count = j_descriptions.length;
// parse node result
for (let i = 0; i < node_description_count; i++) {
const jDescription = j_descriptions[i];
const result = abstractJSONDeserializer(jDescription, deserializers);
if (result instanceof Promise) {
deferred.push({
index: i,
promise: result
});
} else {
deserialized[i] = result;
}
}
if (deferred.length > 0) {
const resolved = await Promise.all(deferred.map(m => m.promise));
for (let i = 0; i < deferred.length; i++) {
const node_description = resolved[i];
const spec = deferred[i];
deserialized[spec.index] = node_description;
}
}
for (let i = 0; i < node_description_count; i++) {
const loaded_node = deserialized[i];
if (
typeof loaded_node !== "object"
|| loaded_node.isNodeDescription !== true
) {
const jDescription = j_descriptions[i];
throw new Error(`Deserialized object was expected to be a NodeDescription, instead got something else. Source JSON[${i}]: ${jDescription}`);
}
const existing_node = node_registry.getNode(loaded_node.id);
if (existing_node === undefined) {
result[i] = loaded_node;
// make sure to register the node
node_registry.addNode(loaded_node);
} else {
if (!existing_node.equals(loaded_node)) {
// collision exists
if (allow_node_id_collisions) {
result[i] = loaded_node;
} else {
// illegal collision
throw new Error(`Loaded node does not match the one in the registry. Loaded: ${loaded_node}, Existing: ${existing_node}`);
}
} else {
// re-use node from registry
result[i] = existing_node;
}
}
}
return result;
}
/**
*
* @param {NodeGraph} graph
* @param {{nodes?:[], connections?:[], descriptions?:[]}} json
* @param {NodeRegistry} [node_registry]
* @param {Map<string, function>} [deserializers]
* @param {boolean} [allow_node_id_collisions]
*/
export async function deserializeNodeGraphFromJSON({
graph,
json,
node_registry = new NodeRegistry(),
deserializers = new Map(),
allow_node_id_collisions = false
}) {
const j_nodes = json.nodes ?? [];
const j_connections = json.connections ?? [];
const j_descriptions = json.descriptions ?? [];
assert.isArray(j_nodes, 'json.nodes');
assert.isArray(j_connections, 'json.connections');
assert.isArray(j_descriptions, 'json.descriptions');
graph.reset();
const descriptions = await deserializeNodeDescriptors(
j_descriptions,
deserializers,
node_registry,
allow_node_id_collisions
);
// parse nodes
const node_count = j_nodes.length;
for (let i = 0; i < node_count; i++) {
const jNode = j_nodes[i];
const node_id = jNode.id;
assert.isNonNegativeInteger(node_id, 'node.id');
const description_index = jNode.description;
const jParameters = jNode.parameters;
// get node description
const node_description = descriptions[description_index];
if (node_description === undefined) {
throw new Error(`Index ${description_index} not found in .description`);
}
const nodeInstance = new NodeInstance();
nodeInstance.id = node_id;
nodeInstance.setDescription(node_description);
nodeParametersFromJSON(nodeInstance, jParameters, deserializers);
graph.addNode(nodeInstance);
}
// parse connections
const connection_count = j_connections.length;
for (let i = 0; i < connection_count; i++) {
const jConnection = j_connections[i];
const jSource = jConnection.source;
const jTarget = jConnection.target;
graph.createConnection(jSource.instance, jSource.port, jTarget.instance, jTarget.port);
}
}