UNPKG

yoni-mcscripts-lib

Version:

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

187 lines (186 loc) 6.04 kB
import { LegacyEventSignal as EventSignal, LegacyEventTriggerBuilder as EventTriggerBuilder } from "../../legacy_event.js"; import { EntityEvent } from "./EntityEvent.js"; import { EntityUtils as EntityBase, YoniScheduler, Schedule, world } from "../../index.js"; import { logger } from "../logger.js"; export class EntityMovementEventSignal extends EventSignal { static warnEventAbuse = (function () { let hasBeenWarned = false; return function warnEventAbuse() { if (hasBeenWarned) return; hasBeenWarned = true; logger.warning("[EntityMovementEventSignal] 不加筛选的监听所有实体的移动可能带来严重的性能问题"); }; })(); subscribe(callback, options) { super.subscribe(callback, options); if (options == null) { EntityMovementEventSignal.warnEventAbuse(); } filters.set(callback, options ?? null); return callback; } unsubscribe(callback) { super.unsubscribe(callback); filters.delete(callback); return callback; } } //这个事件非常卡,我相信你们不会想要使用它的 export class EntityMovementEvent extends EntityEvent { #cancelled = false; get cancel() { return this.#cancelled; } /** * 如果取消跨维度移动事件的话,可能会导致游戏崩溃 */ set cancel(bool) { if (this.#cancelled) return; if (bool) { this.#cancelled = true; this.entity.teleport(this.from); } } get from() { return this.#from.clone(); } get to() { return this.#to.clone(); } get movementKeys() { return this.#movementKeys.slice(0); } constructor(entity, from, to, movementKeys) { super(entity); this.#from = from; this.#to = to; this.#movementKeys = movementKeys; } #movementKeys; #to; #from; } let entityLocationRecords = new WeakMap(); const filters = new Map(); function getFilters() { return new Set(filters.values()); } function getTargetEntities() { const filters = getFilters(); if (filters.size === 0 || filters.has(null)) { return world.getLoadedEntities(); } const targets = []; const typedEntities = new Map(); for (const filter of filters) { if (!filter) continue; // impossible unless you are cheating if (filter.entities) { targets.push(...Array.from(filter.entities) .map(function toYoniEntity(entity) { return EntityBase.from(filter.entities); }) .filter(v => v != null)); } if (filter.entityTypes) { for (const entityType of Array.from(filter.entityTypes)) { if (typedEntities.has(entityType)) continue; typedEntities.set(entityType, Array.from(world.selectEntities({ type: entityType }))); } } } return new Set(targets.concat(Array.from(typedEntities.values()).flat())); //as unknown as Iterable<YoniEntity>; } const schedule = new Schedule({ async: false, type: Schedule.cycleTickSchedule, period: 1, delay: 0 }, function compareTargetsLocation() { for (const entity of getTargetEntities()) { // not location record, save and continue next let oldLoc = entityLocationRecords.get(entity); if (oldLoc === undefined) { entityLocationRecords.set(entity, entity.location); continue; } let newLoc = entity.location; // generate movement status keys let movementKeys = []; if (newLoc.dimension !== oldLoc.dimension) { movementKeys.push("x", "y", "z", "rx", "ry", "dimension", "location", "rotation"); } else { if (newLoc.x !== oldLoc.x) { movementKeys.push("x", "location"); } if (newLoc.y !== oldLoc.y) { movementKeys.push("y", "location"); } if (newLoc.z !== oldLoc.z) { movementKeys.push("z", "location"); } if (newLoc.rx !== oldLoc.rx) { movementKeys.push("rx", "rotation"); } if (newLoc.ry !== oldLoc.ry) { movementKeys.push("ry", "rotation"); } } // no difference, continue next if (movementKeys.length === 0) { continue; } // save new location entityLocationRecords.set(entity, newLoc); // remove duplication movementKeys = Array.from(new Set(movementKeys)); // trigger event trigger.triggerEvent(entity, oldLoc, newLoc, movementKeys); } }); function resolveFilter(values, filterValues) { const [entity, from, to, movementKeys] = values; if (filterValues.movementKeys != null) { for (const key of filterValues.movementKeys) { if (!movementKeys.includes(key)) { return false; } } } if (filterValues.entities != null) { let found = false; for (let filterEntity of filterValues.entities) { if (EntityBase.isSameEntity(filterEntity, entity)) { found = true; break; } } if (!found) { return false; } } if (filterValues.entityTypes != null) { return filterValues.entityTypes.includes(entity.typeId); } return true; } const trigger = new EventTriggerBuilder() .id("yoni:entityMovement") .eventSignalClass(EntityMovementEventSignal) .eventClass(EntityMovementEvent) .filterResolver(resolveFilter) .whenFirstSubscribe(() => { YoniScheduler.addSchedule(schedule); }) .whenLastUnsubscribe(() => { YoniScheduler.removeSchedule(schedule); }) .build() .registerEvent();