yoni-mcscripts-lib
Version:
为 Minecraft Script API 中的部分接口创建了 wrapper,并提供简单的事件管理器和任务管理器,另附有一些便于代码编写的一些小工具。
288 lines (287 loc) • 10.6 kB
JavaScript
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,
};
}
}
}