jarlyyn 发表于 2024-10-15 14:16:05

杰哥瞎扯蛋之 事件系统的优化

本帖最后由 jarlyyn 于 2024-10-15 02:24 PM 编辑

新机器人的新任务模块做好了,跑稳定性测试,正好再水一帖。

之前我提过 mud事件系统,见

https://www.pkuxkx.net/forum/thread-49068-1-1.html

这次做框架时做了小优化。

我之前说过,事件系统最大的优点时解耦,最大的缺点时执行顺序不定。无法避免重复触发。

这个我优化不掉,优点既缺点。

但我在新的框架里提供3个新的机制,使得代码里能适当的避开这些缺点。

1.为每个发起的事件提供了上下文(Context),可以在每次发起时提供一个数据空间,让相应函数之间有交互的基础。

参考代码:
            task.AddTrigger(matcherOnHeal, function (trigger, result, event) {
                let item = new objectModule.Object(result, "", App.History.CurrentOutput).
                  WithParam("动作", "result")
                App.Map.Room.Data.Objects.Append(item)
                event.Context.Set("core.room.onobject", true)
                return true
            })

            task.AddCatcher("line", function (catcher, event) {
                return event.Context.Get("core.room.onobject")
            })

在触发器里,匹配过的行会设置"core.room.onobject"属性。

这样,当在事件捕捉期里,由于处理的是同一个事件,就能避免重复处理以及冲突了。


2.为事件自带了默认的生命周期,使得虽然事件的绑定程序顺序不定,但可以将处理程序绑在事件上,按不同的优先级处理。

具体看代码

事件的代码
      Propose(callback){
            return this.#propose(this.#proposals,callback)
      }
      ProposeEarly(callback){
            return this.#propose(this.#proposalsEarly,callback)
      }
      ProposeEarlier(callback){
            return this.#propose(this.#proposalsEarlier,callback)
      }
      ProposeLate(callback){
            return this.#propose(this.#proposalsLate,callback)
      }
      ProposeLater(callback){
            return this.#propose(this.#proposalsLater,callback)
      }
      Execute(){
            .forEach(proposals => {
                proposals.forEach(hook=>{
                  hook()
                })
            });
      }
      #propose(proposals,callback) {
            proposals.push(callback)
            return true
      }

很明显,我给事件的上下文了挂了proposal这个东西,同时有Earlier,Early,普通,Late,Later5个阶段,同时配合立刻执行,等于6了六个执行优先级,一般也够用了。

典型的应用场景是

    App.BindEvent("core.roomentry", function (event) {
      event.Context.ProposeLater(function () {
            App.Map.OnWalking()
      })
    })

在进入房间后,先确保其他事件都执行了(典型就是确定当前房间的id),最后再调用移动模块,确认移动完成。

3.确保事件是异步的,避免不必要的期待。

我的主事件调用函数是这样的

      OnEvent(event) {
            this.#pendingEvents.push(event)
            if (this.#pendingEvents.length > 1){
                return
            }
            while (this.#pendingEvents.length > 0) {
                let current=this.#pendingEvents
                this.EventBus.RaiseEvent(current)
                this.#eventHandlers.forEach(handler => {
                  handler.Handler(current)
                })
                current.Context.Execute()
                this.#pendingEvents.shift()
            }
      }


新Raise的事件会压入pending,等当前事件处理完毕后

目的很明确避免,事件中调用事件造成事件执行数序的不可控。

当然,这也有缺点,必须直到事件调用是异步的,不能用传统的 初始化数据,发起构建事件,获取构建后的数据的流程(比如行走前更新更走的tag)。

有的必有失,没有银弹,只有tradeoff,没有办法。



jarlyyn 发表于 2024-10-15 14:24:22

然后是事件的发起方式,纯粹的代码发起不算。

我写了两个函数,

App.LineEvent和App.FilterLineEvent

      LineEvent(eventname) {
let self = this;
return function (name, output, wildcards) {
let event = new self.#eventmodule.Event(eventname, {
Name: name,
Output: output,
Wildcards: wildcards,
}).WithType("line")
self.OnEvent(event)
}
}
FilterLineEvent(filtername, eventname) {
let filter = this.#filters
if (filter) {
let self = this;
return function (name, output, wildcards) {
let event = new self.#eventmodule.Event(eventname, {
Name: name,
Output: output,
Wildcards: wildcards,
}).WithType("line")
filter(event)
}
}
}


LineEvent是生成一个客户端回调,直接抛出一个指定的触发事件

FilterLineEvent是调用一个判断函数,可以再处理后抛出一个触发事件。

举个例子,大概是这样

    App.Engine.SetFilter("core.normalroomname", function (event) {
let words = App.History.CurrentOutput.Words
if (words.length == 1 && words.Color == "Cyan" && words.Bold == true) {
App.RaiseEvent(event)
}
})

匹配文字,然后判断是否都是指定颜色,是指定颜色再抛出core.roomname事件。

这样可以很直接的弱化客户端的触发器功能。

基本我的触发器是这个画风

响应的代码都不要再写,维护起来十分轻松。




jarlyyn 发表于 2024-10-15 14:25:43

论坛编辑器的格式没救了……

这次就更新刀这里,看机器人稳定性去了。

下次正常应该更新新的行走模块。
页: [1]
查看完整版本: 杰哥瞎扯蛋之 事件系统的优化