import { Utils } from '../common/Utils';
import { BaseComponent } from './BaseComponent';
const __ = {
private: Symbol('private'),
}
/**
* @class BlueprintComponent
* 蓝图组件。
* @memberof THING
* @extends THING.BaseComponent
* @public
*/
class BlueprintComponent extends BaseComponent {
static mustCopyWithInstance = true;
/**
* 加载蓝图资源并运行。
*/
constructor() {
super();
this[__.private] = {};
let _private = this[__.private];
_private.pendingPromise = null;
_private.resources = [];
// _private.childBlueprints = [];
_private.debugger = null;
_private.disposed = false;
}
onRemove() {
let _private = this[__.private];
_private.resources.forEach(resource => {
resource.dispose();
});
_private.resources.length = 0;
if (_private.debugger) {
_private.debugger.dispose();
_private.debugger = null;
}
_private.pendingPromise = null;
_private.disposed = true;
super.onRemove();
}
// #region Private
// #endregion
/**
* @typedef {object} BlueprintComponentLoadArgs
* @property {string|Array<string>} url The resource URL(s).
* @property {object|Array<object>} data The json data(s).
*/
/**
* 从URL或数据加载。
* @param {BlueprintComponentLoadArgs} options 选项。
* @returns {THING.BlueprintComponent}
* @public
* @example
* let object = new THING.Object3D();
* let blueprints = await object.blueprint.load({ url: './blueprints/myBP.json' });
* // @expect(blueprints.length == 1)
*/
load(options = {}) {
let _private = this[__.private];
let url = options['url'];
let data = options['data'] || [];
let path = options['path'] || null;
let workPath = options['workPath'] || '';
let resourceManager = this.app.resourceManager;
if (_private.disposed) {
console.warn('BlueprintComponent is disposing or disposed, cannot load blueprint.');
return null;
}
_private.pendingPromise = new Promise((resolve, reject) => {
let blueprints = [];
// Get data through url when has url(can use cache in async mode).
if (url) {
if (Utils.isString(url)) {
url = [url];
}
url.forEachAsync((_url) => {
return resourceManager.loadBlueprintAsync(_url).then(datas => {
datas.forEach(data => {
let blueprint = this._loadBlueprintData(data, _url._getPath(), workPath);
blueprint && blueprints.push(blueprint);
});
}, (err) => {
reject(err);
})
}).then(() => {
resolve(blueprints);
});
}
// Get data through url(in sync mode).
else if (data) {
if (data.info) {
let bodyData = resourceManager.getBlueprintBodyData(data);
// Load blueprint resource(s)
bodyData.forEach(body => {
let blueprint = this._loadBlueprintData(body, path, workPath);
blueprint && blueprints.push(blueprint);
});
}
else {
let blueprint = this._loadBlueprintData(data, path, workPath);
blueprint && blueprints.push(blueprint);
}
resolve(blueprints);
}
});
return _private.pendingPromise;
}
_loadBlueprintData(data, path, workPath) {
let _private = this[__.private];
if (_private.disposed) {
return null;
}
let resources = _private.resources;
let blueprint = Utils.createObject('Blueprint', { app: this.app, object: this.object, path, workPath });
resources.push(blueprint);
blueprint.load(data);
return blueprint;
}
/**
* 运行所有蓝图
* @param options
* @public
*/
run(options = {}) {
let _private = this[__.private];
if (_private.pendingPromise) {
_private.pendingPromise.then(() => {
_private.resources.forEach(resource => {
resource.getRunner().start(null, options);
});
}, (err) => {
Utils.error(err)
});
}
}
/**
* 清除所有蓝图。
* @public
*/
clear() {
let _private = this[__.private];
_private.resources.forEach(resource => {
resource.dispose();
});
_private.resources = []
}
/**
* 结束所有蓝图。
* @public
*/
stop() {
let _private = this[__.private];
_private.resources.forEach(resource => {
resource.getRunner().stop();
});
}
/**
* 暂停所有蓝图。
* @public
*/
pause() {
let _private = this[__.private];
if (_private.pendingPromise) {
_private.pendingPromise.then(() => {
_private.resources.forEach(resource => {
resource.getRunner().pause();
});
});
}
}
/**
* 恢复所有蓝图。
* @public
*/
resume() {
let _private = this[__.private];
if (_private.pendingPromise) {
_private.pendingPromise.then(() => {
_private.resources.forEach(resource => {
resource.getRunner().resume();
});
});
}
}
/**
* 根据名称设置变量。
* @param {string} name 变量名称。
* @param {*} value 变量值。
* @public
*/
setVar(name, value) {
let _private = this[__.private];
if (_private.pendingPromise) {
_private.pendingPromise.then(() => {
_private.resources.forEach(resource => {
resource.setVar(name, value);
});
});
}
}
/**
* 根据名称获取变量。变量存在则返回对应的变量,不存在则返回null。(如果有多蓝图,则返回第一个相等的属性)
* @param {string} name 变量名称。
* @returns {Promise}
* @public
*/
async getVar(name) {
let _private = this[__.private];
if (_private.pendingPromise) {
await _private.pendingPromise;
for (var i = 0; i < _private.resources.length; i++) {
if (_private.resources[i].hasVar(name)) {
var value = _private.resources[i].getVar(name);
return value;
}
}
return null;
}
else {
return null;
}
}
/**
* 设置变量。
* @param {object} value 变量值。
* @public
*/
setVars(value) {
let _private = this[__.private];
if (_private.pendingPromise) {
_private.pendingPromise.then(() => {
_private.resources.forEach(resource => {
resource.setVars(value);
});
});
}
}
/**
* 获取蓝图所有变量。
* @returns {Promise<object>}
* @public
*/
async getVars() {
let _private = this[__.private];
if (_private.pendingPromise) {
await _private.pendingPromise;
if (_private.resources && _private.resources.length) {
var value = _private.resources[0].getVars();
return value;
}
else {
return {};
}
}
else {
return {};
}
}
/**
* 注册事件
* @param {string} type 事件类型。
* @param {Function} callback 回调函数。
* @public
*/
addEventListener(type, callback) {
let _private = this[__.private];
if (_private.pendingPromise) {
_private.pendingPromise.then(() => {
_private.resources.forEach(resource => {
resource.addEventListener(type, callback);
});
});
}
}
/**
* 取消注册事件。
* @param {string} type 事件类型。
* @param {Function} callback 回调函数。
* @public
*/
removeEventListener(type, callback) {
let _private = this[__.private];
if (_private.pendingPromise) {
_private.pendingPromise.then(() => {
_private.resources.forEach(resource => {
resource.removeEventListener(type, callback);
});
});
}
}
/**
* 触发事件
* @param {string} type 事件类型。
* @param {object} event 事件信息。
* @public
*/
triggerEvent(type, event) {
let _private = this[__.private];
if (_private.pendingPromise) {
_private.pendingPromise.then(() => {
_private.resources.forEach(resource => {
resource.triggerEvent(type, event);
});
});
}
}
getDebugger() {
let _private = this[__.private];
_private.debugger = _private.debugger || Utils.createObject('BlueprintDebugger', { app: this.app });
_private.resources.forEach(resource => {
resource.setDebugger(_private.debugger);
});
return _private.debugger;
}
// #region ChildBlueprint
/**
* 运行子图
* @param {string} url 子图路径
* @param {object} params
* @returns {Promise}
* @public
*/
async runChildBlueprintFile(url, params = {}) {
let _private = this[__.private];
if (_private.disposed) {
console.warn('BlueprintComponent is disposing or disposed, cannot load blueprint.');
return null;
}
let resourceManager = this.app.resourceManager;
let datas = await resourceManager.loadBlueprintAsync(url);
return await this.runChildBlueprintData(datas[0].blueprints[0], params);
}
async runChildBlueprintData(data, params = {}) {
let _private = this[__.private];
if (_private.disposed) {
console.warn('BlueprintComponent is disposing or disposed, cannot load blueprint.');
return null;
}
if (data) {
let blueprint = Utils.createObject('Blueprint', { app: this.app, object: this.object });
if (data) {
blueprint.load(data);
let result = await this.runChildBlueprint(blueprint, params);
return {
result, // 执行结果
blueprint // 蓝图实例
};
}
}
}
mapInputParam(node, param) {
let inputParamMap = node.getData().paramKeys;
let realParam = {};
for (let key in inputParamMap) {
let value = param[key];
realParam[inputParamMap[key]] = value;
}
return realParam;
}
mapOutputParam(node, param) {
let outputParamMap = node.getData().paramKeys;
let realParam = {};
for (let key in outputParamMap) {
let value = outputParamMap[key];
realParam[key] = param[value];
}
return realParam;
}
/**
* 运行子蓝图
* @param {object} name 子蓝图
* @param blueprint
* @param {object} param 子蓝图的参数
* @private
*/
async runChildBlueprint(blueprint, param) {
let bp = blueprint;
let startNode = bp.getNode("BPChildBlueprintStart");
let endNode = bp.getNode("BPChildBlueprintEnd");
if (!startNode || !endNode) {
console.error('startNode or endNode is not found');
return null;
}
let resolve = null;
let promise = new Promise((res, rej) => {
resolve = res;
})
const eventID = 'OnExecuteBPChildBlueprintEnd'
function callback(event) {
resolve && resolve(event.outputs);
resolve = null;
bp.removeEventListener(eventID, callback);
// 销毁的权利留给外边,因为销毁的话,有的蓝图节点会一并销毁自己创建的东西
// bp.dispose();
}
bp.addEventListener(eventID, callback);
// bp.getRunner().start(this.mapInputParam(startNode, param), { node: startNode, execName: "exec" });
// TODO:调用了_run私有函数, 因为如果使用start方法,因为startNode没有连接输入,会忽略掉这里传入的参数
let rootRunner = bp.getRunner();
rootRunner._run(startNode, this.mapInputParam(startNode, param), {}, "exec");
let outputs = await promise;
let result = this.mapOutputParam(endNode, outputs);
return result;
}
// #endregion
}
export { BlueprintComponent }