北大侠客行MUD论坛

 找回密码
 注册
搜索
热搜: 新手 wiki 升级
查看: 158|回复: 2

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

[复制链接]
发表于 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),可以在每次发起时提供一个数据空间,让相应函数之间有交互的基础。

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

  8.             task.AddCatcher("line", function (catcher, event) {
  9.                 return event.Context.Get("core.room.onobject")
  10.             })
复制代码


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

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


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

具体看代码

事件的代码
  1.         Propose(callback){
  2.             return this.#propose(this.#proposals,callback)
  3.         }
  4.         ProposeEarly(callback){
  5.             return this.#propose(this.#proposalsEarly,callback)
  6.         }
  7.         ProposeEarlier(callback){
  8.             return this.#propose(this.#proposalsEarlier,callback)
  9.         }
  10.         ProposeLate(callback){
  11.             return this.#propose(this.#proposalsLate,callback)
  12.         }
  13.         ProposeLater(callback){
  14.             return this.#propose(this.#proposalsLater,callback)
  15.         }
  16.         Execute(){
  17.             [this.#proposalsEarlier,this.#proposalsEarly,this.#proposals,this.#proposalsLate,this.#proposalsLater].forEach(proposals => {
  18.                 proposals.forEach(hook=>{
  19.                     hook()
  20.                 })
  21.             });
  22.         }
  23.         #propose(proposals,callback) {
  24.             proposals.push(callback)
  25.             return true
  26.         }
复制代码


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

典型的应用场景是

  1.     App.BindEvent("core.roomentry", function (event) {
  2.         event.Context.ProposeLater(function () {
  3.             App.Map.OnWalking()
  4.         })
  5.     })
复制代码


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

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

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

  1.         OnEvent(event) {
  2.             this.#pendingEvents.push(event)
  3.             if (this.#pendingEvents.length > 1){
  4.                 return
  5.             }
  6.             while (this.#pendingEvents.length > 0) {
  7.                 let current=this.#pendingEvents[0]
  8.                 this.EventBus.RaiseEvent(current)
  9.                 this.#eventHandlers.forEach(handler => {
  10.                     handler.Handler(current)
  11.                 })
  12.                 current.Context.Execute()
  13.                 this.#pendingEvents.shift()
  14.             }
  15.         }
复制代码


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

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

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

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



北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2024-10-15 14:24:22 | 显示全部楼层
然后是事件的发起方式,纯粹的代码发起不算。

我写了两个函数,

App.LineEvent和App.FilterLineEvent

  1.         LineEvent(eventname) {
  2. let self = this;
  3. return function (name, output, wildcards) {
  4. let event = new self.#eventmodule.Event(eventname, {
  5. Name: name,
  6. Output: output,
  7. Wildcards: wildcards,
  8. }).WithType("line")
  9. self.OnEvent(event)
  10. }
  11. }
  12. FilterLineEvent(filtername, eventname) {
  13. let filter = this.#filters[filtername]
  14. if (filter) {
  15. let self = this;
  16. return function (name, output, wildcards) {
  17. let event = new self.#eventmodule.Event(eventname, {
  18. Name: name,
  19. Output: output,
  20. Wildcards: wildcards,
  21. }).WithType("line")
  22. filter(event)
  23. }
  24. }
  25. }
复制代码



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

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

举个例子,大概是这样

  1.     App.Engine.SetFilter("core.normalroomname", function (event) {
  2. let words = App.History.CurrentOutput.Words
  3. if (words.length == 1 && words[0].Color == "Cyan" && words[0].Bold == true) {
  4. App.RaiseEvent(event)
  5. }
  6. })
复制代码


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

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

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

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




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2024-10-15 14:25:43 | 显示全部楼层
论坛编辑器的格式没救了……

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

下次正常应该更新新的行走模块。
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|北大侠客行MUD ( 京ICP备16065414号-1 )

GMT+8, 2024-11-1 09:24 AM , Processed in 0.012985 second(s), 15 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表