UNPKG

yoni-mcscripts-lib

Version:

为 Minecraft Script API 中的部分接口创建了 wrapper,并提供简单的事件管理器和任务管理器,另附有一些便于代码编写的一些小工具。

288 lines (287 loc) 10.6 kB
import { StatusCode, overworld } from "./basis.js"; import { config } from "./config.js"; import { CommandPriority } from "./command/CommandPriority.js"; import { CommandExecutor } from "./command/CommandExecutor.js"; import { CommandQueue } from "./command/CommandQueue.js"; import { AsyncCommandQueue } from "./command/AsyncCommandQueue.js"; import { AsyncCommandExecutor } from "./command/AsyncCommandExecutor.js"; export { CommandPriority, CommandExecutor, AsyncCommandExecutor, }; export class Command { static #asyncCommandExecutor = new AsyncCommandExecutor(true); static #syncCommandExecutor = new CommandExecutor(true); static config = (function getCommandConfig() { let commandConfig = config.getConfig("command"); if (!commandConfig) commandConfig = config.createConfig("command"); return commandConfig; })(); static PRIORITY_HIGHEST = CommandPriority.highest; static PRIORITY_HIGH = CommandPriority.high; static PRIORITY_NORMAL = CommandPriority.medium; static PRIORITY_LOW = CommandPriority.low; static PRIORITY_LOWEST = CommandPriority.lowest; /** * 返回队列中未执行的命令的数量 * @returns {number} */ static countQueues() { return Command.#asyncCommandExecutor.commandList.count(); } /** * Execute a command asynchronously * @param {string} command */ static fetch(command) { return Command.addExecute(Command.PRIORITY_NORMAL, overworld, command); } /** * execute a command asynchronously with params * @param {...string} params - Command params * @returns {Promise<CommandResult>} */ static fetchParams(command, ...params) { return Command.addExecute(Command.PRIORITY_NORMAL, overworld, Command.getCommand(command, params)); } /** * execute a command with params by specific sender * @param {AsyncCommandSender} sender - Command's sender * @param {...string} params - command params * @returns {Promise<CommandResult>} */ static fetchExecuteParams(sender, command, ...params) { return Command.addExecute(Command.PRIORITY_NORMAL, sender, Command.getCommand(command, params)); } /** * execute a command by specific sender * @param {AsyncCommandSender} sender - Command's sender * @returns {Promise<CommandResult>} */ static fetchExecute(sender, command) { return Command.addExecute(Command.PRIORITY_NORMAL, sender, command); } /** * add a command to specific priority to execute * @param {CommandPriority} priority * @param {string} command * @returns {Promise<CommandResult>} */ static add(priority, command) { return Command.addExecute(priority, overworld, command); } /** * add a command with params to specific priority to execute * @param {CommandPriority} priority * @param {...string} params * @returns {Promise<CommandResult>} */ static addParams(priority, command, ...params) { return Command.addExecute(priority, overworld, Command.getCommand(command, params)); } /** * add a command with params to specific priority to execute by sender * @param {CommandPriority} priority * @param {AsyncCommandSender} sender * @param {...string} params * @returns {Promise<CommandResult>} */ static addExecuteParams(priority, sender, command, ...params) { return Command.addExecute(priority, sender, Command.getCommand(command, params)); } //某些命令需要以尽可能快的速度执行,故添加此函数,可设置命令权重 //然后我就把所有命令都用这个来执行了 /** * 在对象上调用 `runCommandAsync` 执行命令。 * @param {CommandPriority} priority * @param {AsyncCommandSender} sender * @param {string} command * @returns {Promise<CommandResult>} */ static addExecute(priority, sender, command) { if (Command.config.getBoolean("useSyncExecutorOnAsyncExecute")) { // @ts-ignore 特性,出问题别怪我 return Command.addSyncExecute(priority, sender, command); } let resolve, reject; let promise = new Promise((re, rj) => { resolve = re; reject = rj; }); const queue = new AsyncCommandQueue(sender, command, (result) => { const commandResult = Command.generateCommandResult(true, result); resolve(commandResult); }, (result) => { const commandResult = Command.generateCommandResult(false, result); reject(commandResult); }); Command.#asyncCommandExecutor.commandList.add(priority, queue); return promise; //throw new Error("Unknown command priority " + String(priority)); } static addSyncExecute(priority, sender, command) { let resolve, reject; let promise = new Promise((re, rj) => { resolve = re; reject = rj; }); const queue = new CommandQueue(sender, command); Command.#syncCommandExecutor.commandList.add(priority, queue); queue.onresolve((result) => { const commandResult = Command.generateCommandResult(true, result); resolve(commandResult); }); queue.onreject((result) => { const commandResult = Command.generateCommandResult(false, result); reject(commandResult); }); return promise; } static getCommand(cmd, ...args) { const specificCharGlobalRegex = /(["'\\])/g; const specificCharRegex = /(["'\\])/; const spaceCharRegex = /(\s)/g; if (args?.length === 1 && Array.isArray(args[0])) { args = args[0]; } const params = [cmd]; //遍历每个参数,对满足某一条件的参数进行处理 for (let arg of args.map(String)) { let shouldQuote = false; //空参数 if (arg.trim().length === 0) { shouldQuote = true; } //空格 if (spaceCharRegex.test(arg)) { shouldQuote = true; } //转义特殊符号 if (specificCharGlobalRegex.test(arg)) { arg = arg.replaceAll(specificCharGlobalRegex, "\\$1"); } //如果需要引号,则添加引号 if (shouldQuote) { arg = `"${arg}"`; } params.push(arg); } return params.join(" "); } static getCommandMoreStrict(cmd, ...args) { const specificCharGlobalRegex = /(["'\\])/g; const specificCharRegex = /(["'\\])/; const spaceCharRegex = /(\s)/g; const startsWithNumberRegex = /^[0-9]/; if (args?.length === 1 && Array.isArray(args[0])) { args = args[0]; } const params = [cmd]; //遍历每个参数,对满足某一条件的参数进行处理 for (let arg of args.map(String)) { let shouldQuote = false; //空参数 if (arg.trim().length === 0) { shouldQuote = true; } //空格 if (spaceCharRegex.test(arg)) { shouldQuote = true; } //以数字开头的参数 if (startsWithNumberRegex.test(arg)) { shouldQuote = true; } //转义特殊符号 if (specificCharGlobalRegex.test(arg)) { arg = arg.replaceAll(specificCharGlobalRegex, "\\$1"); } //如果需要引号,则添加引号 if (shouldQuote) { arg = `"${arg}"`; } params.push(arg); } return params.join(" "); } /** * execute a set of commands by sender * @param {AsyncCommandSender} sender * @param {string[]} commands - command * @returns {Promise<CommandResult[]>} */ static async postExecute(sender, commands) { commands = Array.from(commands); let promises = commands.map((cmd) => Command.fetchExecute(sender, cmd)); let results = []; for (let pro of promises) { results.push(await pro); } return Promise.all(results); } static run(command) { if (!overworld.runCommand) { throw new Error("current version doesn't support 'Command.run' method, try 'Command.fetch' instead"); } return Command.execute(overworld, command); } static execute(sender, command) { if (!sender.runCommand) { throw new Error("not a command sender or current version doesn't support 'Command.execute' method, try 'Command.fetchExecute' instead"); } let originalCommandResult; let isSuccess = false; try { originalCommandResult = sender.runCommand(command); isSuccess = true; } catch (cmderr) { originalCommandResult = cmderr; } return Command.generateCommandResult(isSuccess, originalCommandResult); } static generateCommandResult(isSuccess, value) { // 反正最后返回的是 CommandResult let statusCode = isSuccess ? StatusCode.success : StatusCode.fail; let successCount = isSuccess ? 1 : 0; let statusMessage = "command error"; if (typeof value === "string") { try { value = JSON.parse(value); } catch { } } if (value?.statusCode !== undefined) { statusCode = value.statusCode; } if (typeof value?.statusMessage === "string") { statusMessage = value.statusMessage; } if (typeof value?.successCount === "number") { successCount = value.successCount; } if (value instanceof Error) { statusCode = StatusCode.fail; statusMessage = value.message; } else if (typeof value === "string") { statusMessage = value; } else if (typeof value === "number") { statusCode = value; } try { return Object.assign({}, value, { successCount, statusCode, statusMessage, }); } catch { return { successCount, statusCode, statusMessage, }; } } }