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