UNPKG

yoni-mcscripts-lib

Version:

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

300 lines (239 loc) 8.92 kB
import { VanillaWorld } from "../basis.js"; import { Command, AsyncCommandSender } from "../command.js"; import { visualizeValue } from "../lib/console.js"; import { dealWithCmd as JSONModifier_dealWithCmd } from "../commandutils.js"; import { config } from "../config.js"; import { setDebugFunction } from '../debug.js'; import { ConsoleLogger } from "../lib/ConsoleLogger.js"; async function sendMessageTo(receiver: AsyncCommandSender, message: string){ let rawtext = JSON.stringify({rawtext:[{text: message}]}, JSONModifier_dealWithCmd); await Command.addExecute(Command.PRIORITY_HIGH, receiver, `tellraw @s ${rawtext}`); } function formatAnyValue(v: any): string { return typeof v === "string" ? v : visualizeValue(v, 1, { showEnumerableOnly: true, maxItemPerObject: 4, recursiveQuery: false, // recursiveQuery: true, //不开,因为会出奇怪的问题。 // recursiveQueryDepth: 1, //未实现 }); } function showLoggerUsageOnce(){ if ((showLoggerUsageOnce as any).hasExecuted) return; (showLoggerUsageOnce as any).hasExecuted = true; } function sendToConsole( method: (...data: any[]) => void, msg: string){ method(msg); } function getTimeString(now: Date = new Date()): string { let H = String(now.getHours()); let M = String(now.getMinutes()); let S = String(now.getSeconds()); let MS = String(now.getMilliseconds()); let sH = "00" + H; sH = sH.slice(sH.length - 2); let sM = "00" + M; sM = sM.slice(sM.length - 2); let sS = "00" + S; sS = sS.slice(sS.length - 2); let sMS = MS + "000"; sMS = sMS.slice(0,3); let str = sH + ":" + sM + ":" + sS + "." + sMS; return str; } enum LoggingLevel { FATAL = 0, ERROR = 1, WARN = 2, INFO = 3, DEBUG = 4, TRACE = 5, VERBOSE = 6, } function getLoggingLevelName(code: LoggingLevel){ for (const key of Object.keys(LoggingLevel)) //@ts-ignore if (LoggingLevel[key] === code) return key; return "LOG"; } export const LoggingNameMappings = { "0": LoggingLevel.FATAL, fatal: LoggingLevel.FATAL, f: LoggingLevel.FATAL, "1": LoggingLevel.ERROR, error: LoggingLevel.ERROR, e: LoggingLevel.ERROR, err: LoggingLevel.ERROR, fail: LoggingLevel.ERROR, ex: LoggingLevel.ERROR, severe: LoggingLevel.ERROR, "2": LoggingLevel.WARN, warn: LoggingLevel.WARN, w: LoggingLevel.WARN, warning: LoggingLevel.WARN, notice: LoggingLevel.WARN, "3": LoggingLevel.INFO, info: LoggingLevel.INFO, i: LoggingLevel.INFO, log: LoggingLevel.INFO, "4": LoggingLevel.DEBUG, debug: LoggingLevel.DEBUG, d: LoggingLevel.DEBUG, "5": LoggingLevel.TRACE, trace: LoggingLevel.TRACE, t: LoggingLevel.TRACE, "6": LoggingLevel.VERBOSE, verbose: LoggingLevel.VERBOSE, } type RegisteredLevelNames = keyof typeof LoggingNameMappings; type LoggerWithDefinedLoggingName = { [P in RegisteredLevelNames]: (...data: any[]) => void; } function transferHolder(msg: string, replacer: any[]){ msg = msg.replace(/\{\}/g, (match, offset, string) => { if (replacer.length > 0) match = formatAnyValue(replacer.shift()); return match; }); if (replacer.length > 0){ msg += " " + replacer.map(formatAnyValue).join(" "); } return msg; } function sendLogText(level: string, msg: string, rps: any[], time: Date, ignoreLevel = false){ const shouldSendToConsole = ignoreLevel || config.getBoolean("logging.outputToConsole"); const players = Array.from( //我们对旧版有良好的兼容性!(翻译:就是想用,你打我啊) VanillaWorld.getPlayers({ tags: [ config.getString("logging.playerConsoleSpecificTag", "yoni:console") ] }) ); if (players.length === 0 && !shouldSendToConsole){ showLoggerUsageOnce(); return; } const timeMsgHeader = `[${getTimeString(time)} ${level}]`; const msgHeader = `[${level}]`; msg = transferHolder(msg, rps); if (config.getBoolean("logging.uniqueFontSize")) msg = "§中" + msg; if (config.getBoolean("logging.showTimeStringOnConsoleOutput") || config.getBoolean("logging.showTimeString")) if (shouldSendToConsole){ const printer = Logger.getConsolePrinter(level, ConsoleLogger.warn); sendToConsole(printer, (config.getBoolean("logging.showTimeStringOnConsoleOutput") ? timeMsgHeader : msgHeader ) + msg); } if (players.length > 0){ const msgWithHead = (config.getBoolean("logging.showTimeString") ? timeMsgHeader : msgHeader) + msg; players.forEach(player => sendMessageTo(player, msgWithHead)); } } class Logger { static setConsolePrinter(level: string, printer: (msg: string) => void): void { Logger.#consoleLevelPrinters.set(String(level), printer); } static getConsolePrinter(level: string): ((msg: string) => void) | undefined; static getConsolePrinter(level: string, defaultPrinter: (msg: string) => void): (msg: string) => void; static getConsolePrinter(level: string, defaultPrinter?: (msg: string) => void): ((msg: string) => void) | undefined { return Logger.#consoleLevelPrinters.get(String(level)) ?? defaultPrinter; } static removeConsolePrinter(level: string): boolean { if (Logger.#consoleLevelPrinters.has(String(level))){ Logger.#consoleLevelPrinters.delete(String(level)); return true; } return false; } static #consoleLevelPrinters = new Map<string, (msg: string) => void>(); /** * 向控制台输出日志。 */ static log(...data: any[]): void { sendLogText("LOG", "", data, new Date(), true); } name: string; constructor(name: string){ if (typeof name !== "string" || name.trim() === "") throw new TypeError("Logger's name doesn't valid"); this.name = name; for (const lv_c of Object.entries(LoggingNameMappings)){ this.addLevelLogFunc(lv_c[0], lv_c[1] as LoggingLevel); } // return new Proxy(this, new LoggerProxy()); } async printOnLevel(lv: string, time: Date, msg: any = "", ...rps: any[]){ if (typeof msg !== "string"){ rps.unshift(msg); msg = ""; } msg = `[${this.name}]: ${msg}`; //@ts-ignore sendLogText(getLoggingLevelName(LoggingNameMappings[lv]), msg, rps, time); } addLevelLogFunc(level: string, levelCode: LoggingLevel){ //@ts-ignore this[level] = (...data: any[]): void => { const time = new Date(); if (levelCode <= config.getInt("logging.logLevel", 3)){ this.printOnLevel(level, time, ...data); } } //@ts-ignore return this[level]; } } type LoggerNames = { [levelName in keyof typeof LoggingNameMappings]: (...args: any[]) => void }; interface Logger extends LoggerNames { } export { Logger }; export const console = new Logger("LOG"); export const log = console.log; export const print = console.log; //修改原本的console if (config.getBoolean("logging.overrideDefaultConsole")){ Object.assign(globalThis.console, console, { log: Logger.log }); globalThis.print = print; } setDebugFunction(async function (ChatCommandModule: any){ const { ChatCommand } = await import("../command/ChatCommand.js"); ChatCommand.registerPrefixCommand("$", "log", onCommandExecute); //@ts-ignore async function onCommandExecute(sender, rawCommand, label, args){ const specificTag = config.getString("logging.playerConsoleSpecificTag", "yoni:console"); let action; if (args.length === 0){ if (sender.hasTag(specificTag)){ action = "off"; } else { action = "on"; } } else if (args[0] === "level"){ const newLevelCode = parseInt(args[1]); if (isFinite(newLevelCode)){ config.setInt("logging.logLevel", newLevelCode); } sender.sendMessage("日志输出等级 §b"+newLevelCode); return; } else { if (args[0] === "off" || args[0] === "on") action = args[0]; } if (action === "off"){ sender.removeTag(specificTag); sender.sendMessage("日志输出 §c关闭"); } else if (action === "on"){ sender.addTag(specificTag); sender.sendMessage("日志输出 §a开启"); } } });