UNPKG

yoni-mcscripts-lib

Version:

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

281 lines (237 loc) 9.86 kB
import { VanillaWorld } from "../basis.js"; import { EntityUtils } from "../EntityUtils.js"; import { Logger } from "../util/Logger.js"; const logger = new Logger("ChatCommand"); const defaultCmdMap = new Map(); const nonPrefixCmdMap = new Map(); const prefixMap = new Map(); let defaultPrefix = "!"; export class ChatCommand { static get defaultPrefix(){ return defaultPrefix; } static set defaultPrefix(i){ if (typeof i === "string" && i.length === 1) defaultPrefix = i; } /*static executeCommand(sender, rawCommand){ const parameters = this.getParameters(rawCommand); const label = parameters[0]; if (label.startsWith(defaultPrefix)) label = label.slice(defaultPrefix.length); this.#invokeCommand(sender, rawCommand, label, parameters.splice(1)); } }*/ static receiveBeforeChatEvent(event){ let isCmd = false; let executor = null; let sender = event.sender; let message = event.message; let rawCommand = message; let prefix; let parameters = this.getParameters(message); let label = parameters[0]; for (let p of prefixMap.keys()){ if (!message.startsWith(p)) continue; let cmdMap = prefixMap.get(p); let newLabel = label.slice(p.length); if (!cmdMap.has(newLabel)) continue; isCmd = true; label = label.slice(p.length); rawCommand = rawCommand.slice(p.length); executor = cmdMap.get(newLabel); break; } if (!isCmd && nonPrefixCmdMap.has(label)){ isCmd = true; executor = nonPrefixCmdMap.get(label); } if (!isCmd && message.startsWith(defaultPrefix)){ isCmd = true; label = label.slice(defaultPrefix.length); rawCommand = rawCommand.slice(defaultPrefix.length); if (defaultCmdMap.has(label)){ executor = defaultCmdMap.get(label); } } if (isCmd){ event.cancel = true; if (executor !== null){ this.#invokeCommand({ sender: event.sender, rawCommand: rawCommand, args: parameters.slice(1), label: label, executor: executor }); } else { logger.debug("无法找到命令 {}", label); EntityUtils.from(sender).sendMessage("[ChatCommand]: 无法找到命令" + label); } } } /** * @param {string} prefix * @param {string} command * @param {(sender: import("../types").YoniPlayer, rawCommand:string, label:string, args:string[])=>void} executor */ static registerPrefixCommand(prefix, command, executor){ this.registerCustomPrefixCommand.apply(this, arguments); } /** * * @param {string} command * @param {(sender: import("../types").YoniPlayer, rawCommand:string, label:string, args:string[])=>void} executor */ static registerCommand(command, executor){ if (typeof command !== "string" || command.length === 0) throw new TypeError("command not valid"); let cmdMap = defaultCmdMap; if (typeof executor.onCommand !== "function" && typeof executor !== "function") throw new TypeError("cannot execute it"); cmdMap.set(command, executor); logger.trace("注册命令 command: {}", command); } static unregisterCommand(command){ defaultCmdMap.delete(command); logger.trace("移除命令 command: {}", command); } /** * @param {string} command * @param {(sender: import("../types").YoniPlayer, rawCommand:string, label:string, args:string[])=>void} executor */ static registerNonPrefixCommand(command, executor){ if (typeof command !== "string" || command.length === 0) throw new TypeError("command not valid"); let cmdMap = nonPrefixCmdMap; if (typeof executor.onCommand !== "function" && typeof executor !== "function") throw new TypeError("cannot execute it"); cmdMap.set(command, executor); logger.trace("注册无前缀命令 command: {}", command); } static unregisterNonPrefixCommand(command){ nonPrefixCmdMap.delete(command); logger.trace("移除无前缀命令 command: {}", command); } /** * @param {string} prefix * @param {string} command * @param {(sender: import("../types").YoniPlayer, rawCommand:string, label:string, args:string[])=>void} executor */ static registerCustomPrefixCommand(prefix, command, executor){ if (typeof prefix !== "string" || prefix.length === 0) throw new TypeError("prefix not valid"); if (typeof command !== "string" || command.length === 0) throw new TypeError("command not valid"); let cmdMap = null; if (!prefixMap.has(prefix)) prefixMap.set(prefix, new Map()); cmdMap = prefixMap.get(prefix); if (typeof executor.onCommand !== "function" && typeof executor !== "function") throw new TypeError("cannot execute it"); cmdMap.set(command, executor); logger.trace("注册命令 prefix: {}, command: {}", prefix, command); } static unregisterCustomPrefixCommand(prefix, command){ let cmdMap = prefixMap.get(prefix); if (cmdMap === undefined) return; cmdMap.delete(command); if (cmdMap.size === 0) prefixMap.delete(prefix); logger.trace("移除命令 prefix: {}, command: {}", prefix, command); } static #invokeCommand(options){ let { sender, rawCommand, label, args, executor } = options; sender = EntityUtils.from(sender); try { logger.debug("正在执行命令{}", label); if (typeof executor.onCommand === "function"){ executor.onCommand(sender, rawCommand, label, args); } else { executor(sender, rawCommand, label, args); } } catch(err) { logger.error("运行命令{}时发生错误 {}", label, err); sender.sendMessage(`[ChatCommand]: 运行命令${label}时发生错误,请查看控制台或寻求管理员的帮助`); } } /* 写的很懒,大体是根据空格分割参数 如果有反斜杠就忽略特殊符号 如果有引号就将里边的内容当成一段文字 参考了bash对单引号(') 双引号(") 反斜杠(\) 空格( )这四个符号的处理 */ static getParameters(commandContent){ let args = []; let command = ""; let escapeFlag = false; let escapeCharacter = ""; let quoteFlag = false; let singleQuoteFlag = false; let quoteContent = ""; let argIndex = 0; for (let i = 0; i < commandContent.length ; i++){ if (args[argIndex] == null) args[argIndex] = ""; let str = commandContent.substr(i, 1); //console.log("str: "+str); if (str == "\\" && !escapeFlag && !singleQuoteFlag && ( (quoteFlag && commandContent.substr(i, 2) == "\\\"") || !quoteFlag ) ) { escapeFlag = true; i += 1; str = commandContent.substr(i, 1); //console.log("escape str: "+str); } if (str == "\"" && !escapeFlag && !singleQuoteFlag){ //console.log("switching quote, current: "+quoteFlag); args[argIndex] += quoteContent; quoteContent = ""; quoteFlag = !quoteFlag; i += 1; str = commandContent.substr(i, 1); } else if (str == "'" && !quoteFlag && (singleQuoteFlag || !escapeFlag)) { //console.log("switching single quote, current: "+singleQuoteFlag); args[argIndex] += quoteContent; quoteContent = ""; singleQuoteFlag = !singleQuoteFlag; i += 1; str = commandContent.substr(i, 1); } //console.log("is quote: "+ quoteFlag); //console.log("is escape: "+ escapeFlag); if (singleQuoteFlag || (quoteFlag && ( (escapeFlag && str == "\"") || !escapeFlag) ) ){ //console.log("detect quote"); quoteContent += str; } else if (str == " " && !escapeFlag){ //console.log("next arg"); argIndex ++; } else { args[argIndex] += str; } escapeFlag = false; } //if (quoteFlag || singleQuoteFlag) console.log("警告:引用未结束"); let parameters = []; for (let arg of args){ if (arg != "") parameters.push(arg); } return parameters; } } VanillaWorld.beforeEvents.chatSend.subscribe(function (event){ if (event.cancel) return; ChatCommand.receiveBeforeChatEvent(event); });