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