UNPKG

yoni-mcscripts-lib

Version:

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

248 lines (247 loc) 8.49 kB
import { MinecraftSystem, VanillaWorld } from "../basis.js"; import { debug } from "../config.js"; import { Logger } from "../util/Logger.js"; const logger = new Logger("Event"); /* 与事件有关的一些函数 */ export function getIdentifierInfo(identifier) { if (typeof identifier !== "string") { throw new TypeError("Not a string identifier"); } let namespace = null; let name = null; let info = identifier.match(/^(?:(\S+):)?(\S+)$/m); if (info === null) { throw new Error("Invalid identifier: " + identifier); } else if (info[2] === undefined) { throw new Error("Could not find a name in identifier: " + identifier); } if (info[1] !== undefined) { namespace = info[1]; } name = info[2]; return { id: identifier, name, namespace }; } /* 事件注册部分 */ //定义存储已注册事件的地图 const registeredEventTypes = new Map(); //用于记录 已延迟的 事件的监听 的注册 的地图 const waitingEventRegisterMap = new Map(); /** * @deprecated 废弃,等待重写 */ class EventRegisterListener { static add(eventTypeIdentifier, callback) { let idInfo = getIdentifierInfo(eventTypeIdentifier); if (!waitingEventRegisterMap.has(idInfo.id)) { waitingEventRegisterMap.set(idInfo.id, []); } let list = waitingEventRegisterMap.get(idInfo.id); list.push(callback); } static async register(eventType) { if (Types.has(eventType) && waitingEventRegisterMap.has(eventType)) { let list = waitingEventRegisterMap.get(eventType); waitingEventRegisterMap.delete(eventType); list.forEach((callback) => { try { callback(); } catch (e) { logger.error(e); } }); } else { logger.error("unknown eventType" + eventType); } } } function getNamespaceEventTypesMap(namespace) { if (namespace === null) { let rtmap = new Map(); //获取key的反序,这样,先注册的事件优先级就会变低 for (let map of Array.from(registeredEventTypes.values()).reverse()) { for (let key of Array.from(map.keys()).reverse()) { if (rtmap.has(key)) { continue; } rtmap.set(key, map.get(key)); } } return rtmap; } if (!registeredEventTypes.has(namespace)) { registeredEventTypes.set(namespace, new Map()); } return registeredEventTypes.get(namespace); } /** * @deprecated 废弃,不再使用,请使用新的 {@link import("./v2/EventRegistry").EventRegistry}。另外,如果你使用了此LegacyEvent中的自定义事件,webpack打包或者类似的操作将无法完成。 */ class Types { static register(identifier, eventType) { let idInfo = getIdentifierInfo(identifier); if (idInfo.namespace === null) { idInfo.namespace = "custom"; } let namespaceMap = getNamespaceEventTypesMap(idInfo.namespace); if (namespaceMap.has(idInfo.name)) { throw new Error("EventType " + idInfo.id + " already registered"); } if ("subscribe" in eventType) { if (!("unsubscribe" in eventType)) { logger.warn("正在注册的{}事件没有一个unsubscribe属性,这可能会导致一些错误", idInfo.id); } try { namespaceMap.set(idInfo.name, eventType); } catch (err) { throw new Error("未能注册事件,可能指定的命名空间不可修改事件\n以下是错误信息: " + String(err)); } //logger.trace("注册了事件 {}", idInfo.id); } else { throw new Error("在eventType上必须有一个subscribe属性才可以注册事件"); } if (waitingEventRegisterMap.has(idInfo.id)) { EventRegisterListener.register(idInfo.id); } } static registerNamespace(namespace, namespaceEventTypes) { if (typeof namespace === "string" || namespace.match(/[\s:]/) === null) { throw new Error("命名空间需要是一个字符串,且不能包含空格或冒号"); } if (registeredEventTypes.has(namespace)) { throw new Error("指定的命令空间已被注册"); } if ("get" in namespaceEventTypes) { registeredEventTypes.set(namespace, namespaceEventTypes); //logger.trace("注册了事件命名空间 {}", namespace); } } /** * @param {String} 事件的identifer * @returns {Boolean} 事件是否存在且已移除 */ static unregister(identifier) { let idInfo = getIdentifierInfo(identifier); if (idInfo.namespace === null) { idInfo.namespace = "custom"; } let namespaceMap = getNamespaceEventTypesMap(idInfo.namespace); if (namespaceMap.has(idInfo.name)) { try { namespaceMap.delete(idInfo.name); logger.debug("已注销事件 {},但它仍存在于已注册的监听器中", idInfo.id); return true; } catch (err) { throw new Error("未能注册事件,可能指定的命名空间不可修改事件\n以下是错误信息: " + String(err)); } } return false; } static hasNamespace(namespace) { return registeredEventTypes.has(namespace); } static has(identifier) { let idInfo = getIdentifierInfo(identifier); let namespaceMap = getNamespaceEventTypesMap(idInfo.namespace); return namespaceMap.has(idInfo.name); } /** * @param {String} 事件的identifer * @returns 事件的示例,如不存在返回null */ static get(identifier) { let idInfo = getIdentifierInfo(identifier); let namespaceMap = getNamespaceEventTypesMap(idInfo.namespace); if (namespaceMap.has(idInfo.name)) { return namespaceMap.get(idInfo.name); } else { return null; } } static getEventTypes() { let rtmap = new Map(); for (let mkey of registeredEventTypes.keys()) { let map = registeredEventTypes.get(mkey); for (let key of map.keys()) { let k = mkey + ":" + key; rtmap.set(k, map.get(key)); } } return rtmap; } static *getAll() { for (let namespaceMap of registeredEventTypes.values()) { yield* namespaceMap.values(); } } } function registerBeforeAndAfterEvents(namespace, object) { let map = new Map(); for (let s in object.beforeEvents) { map.set("beforeEvents." + s, object.beforeEvents[s]); } for (let s in object.afterEvents) { map.set("afterEvents." + s, object.afterEvents[s]); } Object.freeze(map); registeredEventTypes.set(namespace, map); } registerBeforeAndAfterEvents("minecraft", VanillaWorld); registerBeforeAndAfterEvents("system", MinecraftSystem); /** * @deprecated 废弃,等待重写 */ export const events = new Proxy({}, { get(t, k) { if (k === Symbol.iterator) return () => Types.getAll(); if (Types.has(k)) return Types.get(k); }, set(t, k, v) { try { Types.register(k, v); } catch { return false; } return true; }, has(t, k) { if (k in t) { return true; } return Types.has(k); }, deleteProperty(t, k) { try { Types.unregister(k); } catch { return false; } return true; }, ownKeys() { let typeKeys = []; let defaultKeys = []; for (let k of Types.getEventTypes().keys()) { let idInfo = getIdentifierInfo(k); if (!defaultKeys.includes(idInfo.id)) defaultKeys.push(idInfo.id); if (idInfo.namespace === "custom") continue; typeKeys.push(k); } return Array.from(new Set(defaultKeys.concat(typeKeys))); } }); export { Types }; export { Types as EventTypes }; export { EventRegisterListener };