虽然很多客户端和教程介绍机器时,都是用触发来驱动机器。
但是,要做一个稳定,功能强大的机器,被动的靠出发来驱动明显不可行,必须要依靠全面详尽的数据来运行。
在Mud中,有很多指令会对应获取更新数据。常见比如"hp","score","i","skills"等。
我们一般都会根据这些指令的结果来更新数据。
但是,Mud又是动态的,这些数据往往又容易失效。
做任何动作之前猛刷一通指令明显不科学,浪费服务器资源,还干扰用户使用。在这个情况下,我们肯定是要面对有时效性的数据来进行编码。
也就是说,我们在收集数据时,不光光面对是一个采集的问题。还要把数据是做缓存,处理一个缓存失效的问题。
那以缓存的角度来看看到数据的话,实际是要实现以下几个地方
数据的有效期(ttl)
数据回源(发现过期后主动更新数据)
数据的强制更新(更新数据后更新数据和有效期)
数据强制失效
实现的方法其实有很多,我这里说说我的实现方法。
以我的代码为例
在处理数据时,我使用了这么一个结构
let DefaultExecute = function (check) {
return check.Command
}
class Check {
constructor(id) {
this.#id = id
}
#id = ""
Interval = 0
#last = 0
Execute = DefaultExecute
Command = null
WithExecute(fn) {
this.Execute = fn ? fn : DefaultExecute
}
ID() {
return this.#id
}
WithCommand(cmd) {
if (typeof (cmd) != "function") {
this.Command = function () { App.Send(cmd) }
} else {
this.Command = cmd
}
return this
}
WithInterval(interval) {
interval = interval - 0
if (isNaN(interval)) {
interval = 0
}
this.Interval = interval
return this
}
InCooldown() {
return (new Date()).getTime() - this.Interval < this.#last
}
Reset() {
this.#last = (new Date()).getTime()
}
Force() {
this.#last = 0
}
}
这个机构很明显。
#last 最后一次更新时间
Interval 重新获取数据的间隔(ttl)
Execute,Command 更新数据的函数和指令
#id 缓存的 id,可以制定id进行更新
在使用时,我会以这样的形式来注册一个数据检查(更新)器
let checkerSkills = App.Checker.Register("skills", "skills", 300000)
这是注册一个 id为skills,指令为skills,数据有效期为300秒的检查器。
在匹配skill最后的代码最后调用一下Reset
function (result) {
checkerSkills.Reset()
})
就能按一定的有效期来保存数据了。
然后由于这个是获取技能数据的,所以我绑定了技能提升的触发事件,在技能重置时强制这个缓存数据失效
//技能升级时重置相关的checker
App.BindEvent("core.skillimproved", function () {
checkerHPM.Force()
checkerSkills.Force()
checkerJifa.Force()
})
就能比较有效的处理缓存数据了。
在可能需要处理状态的地方,我加入了检查所有预设检查器的代码
//检查的函数
App.Check = () => {
App.RaiseEvent(eventBeforeCheck)//触发检查发送指令
let checks = App.Checker.Check()//获取需要执行的检查
if (checks.length == 0) {//无需检查
App.Next()
return
}
App.PushCommands(//一次性执行所有检查
App.Commands.NewFunctionCommand(function () {
checks.forEach(check => {
check()
});
App.Next()
}),
App.NewSyncCommand(),
)
App.Next()
}
//准备函数,第一个函数时准备id,第二个时准备的上下文(环境变量)
App.Prepare = function (id, context) {
App.RaiseEvent(eventBeforeCheck)
let checks = App.Checker.Check()
if (checks.length == 0) {
AfterCheck(id, context)
return
}
App.PushCommands(
App.Commands.NewFunctionCommand(function () {
checks.forEach(check => {
check()
});
App.Next()
}),
App.NewSyncCommand(),
App.Commands.NewFunctionCommand(function () {
AfterCheck(id, context)
}),
)
App.Next()
}
这样就能确保每次在需要数据进行决策(准备)时,使用的是最新的有效的数据。