跳到主要内容

MxtEvents:事件

所有事件都注册在服务器事件组。订阅方式:

MxtEvents.abilityUse(event => {
if (event.isPre() && event.getAbility() === 'example:forbidden') {
event.cancel()
}
})

event.cancel() 会立即停止当前 KubeJS 监听链。只有底层是可取消 NeoForge 事件时才会取消 MXT 事务;非可取消阶段调用仅停止脚本监听,不会撤销已经发生的游戏行为。

专用事件包装​

事件阶段可用方法可修改内容
abilityUsePre、PostgetEntity()、getAbility()、isPre()、getPaidCosts()Pre 可取消。getPaidCosts() 在 Pre 返回空映射。
curseApplyPre、PostgetCurse()、isPre()、getStacks()、setStacks(n)、getSource()、setSource(text)仅 Pre 可取消和修改层数/来源;层数必须大于 0。对 Post 调用 setter 会抛异常。
resourceConsumePre、PostisPre()、getAmounts()、setAmount(resource, amount)仅 Pre 可取消和修改单项资源量;金额必须有限且大于 0。
auraZoneenter、leave、tick、overridegetKind()、getSource()、getConcentration()、isCultivationSuppressed()、getOverrideZone()仅 override 可取消。getOverrideZone() 非 override 时返回空字符串。
friendRelation无(判断型事件)getJudgeId()、getJudge()、hasJudge()、getCandidate()、getResult()、isAnswered()、setFriend(friend)、abstain()脚本给自己判断:setFriend(true/false) 表态,abstain() 交回玩家好友名单。getResult() 返回 "true" / "false" / "default"。判断者以 UUID 给出(getJudgeId() 永远有值),getJudge() 在对方离线时返回 null,先用 hasJudge() 判断。不可取消,也不该返回布尔值——表态只能通过 setter。没有脚本监听时该事件根本不会派发。

资源映射的键已转换为字符串 ID。例如:

MxtEvents.resourceConsume(event => {
if (!event.isPre()) return
const amounts = event.getAmounts()
if (amounts['mxt:spirit_power'] > 0) {
event.setAmount('mxt:spirit_power', 5)
}
})

friendRelation 是判断型事件:脚本不改游戏状态,只回答问题,所以没有"阶段"也没有取消。

MxtEvents.friendRelation(event => {
// 只认这一位是自己人;其余情况什么都不做,交回玩家好友名单。
// 需要用判断者实体时先看 hasJudge():对方可能离线。
if (event.getCandidate().getName().getString() === 'Alice') {
event.setFriend(true)
}
})

不表态(或调用 abstain())等于"按名单来",不等于否定。所有事件都在服务端派发。

通用生命周期事件​

其余事件使用通用包装,方法为:

方法返回/作用
getType()KubeJS 事件名,例如 cultivationBreak。
getPhase()实际 Java 阶段类名,例如 Pre、StrikePre、StartPost。
isCancellable()当前阶段是否可取消。
getEvent()原生 MXT 事件实例,可调用下表列出的 Java accessor。
cancel()KubeJS 标准取消方法;仅在 isCancellable() 为 true 时会取消底层事务。

以下表中 native 代表 const native = event.getEvent()。Identifier、Holder、Attachment 等返回值均为 Java 对象;需要文本 ID 时使用 String(value)。

KubeJS 事件阶段类名native 的主要 accessor / 语义
abilityTriggeredPre、PostgetEntity()、getAbility()、getSignalType()、getContext();Pre 可取消触发的技能。原生事件的 getAbility() 返回 Holder<Ability>,需要 ID 时使用 HolderHelper.id(...) 或 KubeJS 包装器的 getAbility()。
curseRemovePre、PostgetCurse()(Holder<Curse>)、getState()、getReason()、getGameTime()、getHolder()、getSources();Pre 可取消移除。reason 为 EXPLICIT、EXPIRED、CLEANSED、REPLACED(replace 叠层模式覆盖旧实例时补发的 Post 用它,且不可取消)。
cultivationBreakPre、PostgetTarget()(Holder<RealmStage>)、getThreshold()、getContext()、getSpirit()、getResource();Pre 另有 getOriginalCosts()、getCosts()、setCost(resource, amount),可取消;Post 有 getPaidCosts()。
techniqueLearnPre、PostgetTechnique()(Holder<Technique>)、getSpirit();Pre 可取消。
alchemyCraftPre、Post两端都有 getRecipe()(RecipeHolder<AlchemyRecipe>,自动解析后冻进批次的身份,不是玩家选择)、getPos()、getOperator()(UUID)。Pre.getPlayer() 一定有开炉的人,Pre.getInputs() 是不可变 InputCopy 列表,每项是 role()(MAIN / AUXILIARY / CATALYST)与 stack() 副本;改副本或改这份列表不改账,取消不扣料,改真实库存则拒绝开炉(原因是 CANCELLED)且不还原库存。Post.getPlayer() 在原操作者离线时为空,Post.isSuccess()、Post.getReason()(成功时为空)、Post.getOutputs() 是产物快照。结算先清待输出与会话,再在重入守卫内跑方块动作与原操作者的玩家动作、判完成,退出守卫后才发 Post,所以回调可以在仍有效的丹炉上启动下一批;Post 不改写出货格。
artifactRefinePre、PostgetStack()、getEntity();Pre 可取消。
forgingStart、Started、StrikePre、StrikePost、CompletePre、CompletePost、Cancel每个阶段都可读 getPlayer()(ServerPlayer)与 getPos()(BlockPos,台子位置)。分阶段:Start.getBlueprint();Started/StrikePost/Cancel.getSession();StrikePre.getMethod()(Holder<ForgingMethod>)、getResources()、getContext()、getCosts()、setCosts(costs);CompletePre.getBlueprint()、getSession();CompletePost.getBlueprint()、getSession()、getResult()、getQuality()(Holder<ItemQuality>,这一炉定下的档——getResult() 只给锻造记录,监听者在成品堆写入组件之前跑,所以要档位就读它)。Start、StrikePre、CompletePre、Cancel 可取消。
formationActivate、Deactivate、Tick、TickEffects、UpkeepFailedgetLevel()、getController()、getInstance()(阵法 ID 取 getInstance().formation());Activate、TickEffects、UpkeepFailed 可取消,Deactivate 与 Tick 不可取消。Tick 是"本周期已付费"的观察点,TickEffects 只挡这一周期的效果且不退费,UpkeepFailed 取消表示让阵法撑过付不出钱的这一周期;UpkeepFailed 另有 getPayer()(Optional<Entity>,无人付款时为空)与 getFailedResource()(Optional<Identifier>,没有付款者时为空),脚本据此知道谁欠费、欠的是哪种资源。
secretRealmCreate、Destroy、EnterPre、EnterPost、ExitgetDefinition()(Holder<SecretRealm>)、getDimension()(ResourceKey<Level>,实例维度键)、getIndex()(第几份,从 0 数)、getOwner()(Optional<UUID>,主人)、getServer();成员事件另有 getMember()(UUID)。只有 EnterPre 可取消。Create 在一份新实例维度建好后、第一位成员落下之前发布;Destroy 在实例结束时发布——无论它是销毁地形还是(被认领的实例)只卸载休眠。
soulTransferPre、TransferPost、ReclaimPre、ReclaimPostgetEntity()、getSoul();所有 *Pre 可取消。
spiritContractPre、PostgetContract()、getContractType()、getRequester()、getAction();getContractType() 是 Optional<Holder<ContractType>>,getAction() 为 BIND、RELEASE、DEATH、RECALL、RECALL_COMPLETED(召回闩被消费,不可取消);Pre 可取消。
tribulationStartPre、StartPost、EntryPre、EntryPost、CompletegetTribulation()(Holder<Tribulation>)、getData();两种节拍事件另有 getIndex()(第几拍,从 0 数)与 getEntry()。getData() 就是附件本身:peek()/remaining() 读队首与还剩几拍,state() 读当前节拍写下的现场,windup() 读启动前摇还剩多少 tick(0 表示已经在走时间线、或这场天劫没有前摇)。StartPre 可取消(拒绝这次启动),EntryPre 可取消(跳过该节拍)。

forging 的两条额外约定​

会话是只读的。 getSession() 返回 ForgingSessionView,可读 value()、steps()、optimalSteps()、 history()(不可变列表)、canComplete(),不能改写会话——原生的 ForgingSession 是可变对象, 已不再交给监听器,所以 event.getEvent().getSession().strike(...) 这类写法不存在。台子本身也不交给监听器, 只给位置 getPos();要读槽位就用 player.level().getBlockEntity(pos)。

监听器不要抛异常,抛了也不会搞坏操作。 四个决定型事件(Start、StrikePre、CompletePre、 Cancel)在事务中间派发,所以监听器抛出时会被服务端就地转成一次拒绝:日志记为 LISTENER_ERROR (与脚本主动 cancel() 的 CANCELLED 区分),操作不发生、材料不消耗、会话保持原样。 三个通知型事件(Started、StrikePost、CompletePost)派发时操作已经生效,抛出只记录并忽略。

例如调整突破消耗:

MxtEvents.cultivationBreak(event => {
if (event.getPhase() !== 'Pre') return
const native = event.getEvent()
native.setCost('mxt:spirit_power', 20)
})

相关​

寿元耗尽与转世使用 Reincarnation 提供的 ReincarnationEvents。