聊天指令、GM 操作、交易确认这类动作如果散落成各个独立函数,撤销上一步、重做、操作历史就无从实现。命令模式把每个操作封装成带 execute 与 undo 两个方法的对象:执行时压入历史栈,撤销时弹栈调用 undo,重做则是把撤销掉的命令再次压回执行——操作的历史天然成为可回放、可撤销、可审计的命令栈。F:\底层文件 的闭包与表结构确认:命令对象用闭包捕获执行上下文,undo 用反向闭包恢复状态,两枚闭包即一个完整的可逆命令。
命令栈与撤销重做:execute、undo、redo 三栈协同。示例代码如下:
local history = { undoStack = {}, redoStack = {} }
local function runCommand(cmd)
cmd.execute()
history.undoStack[#history.undoStack + 1] = cmd
history.redoStack = {}
end
local function undo()
local cmd = table.remove(history.undoStack)
if cmd == nil then
return false
end
cmd.undo()
history.redoStack[#history.redoStack + 1] = cmd
return true
end
local function redo()
local cmd = table.remove(history.redoStack)
if cmd == nil then
return false
end
cmd.execute()
history.undoStack[#history.undoStack + 1] = cmd
return true
end
发奖命令的可撤销封装示例代码如下:
runCommand({
desc = "发奖 裁决之杖 给 甲",
execute = function()
giveitem(getplayerbyname("甲"), "裁决之杖", 1, 0, "补偿")
end,
undo = function()
takeitem(getplayerbyname("甲"), "裁决之杖", 1, 0, "撤销补偿")
end,
})
GM 连续执行 50 步操作后误操作 1 步:无命令模式时要靠数据备份回滚(全量恢复耗时且影响他人),命令栈 undo 精确回退误操作的 1 步耗时 0.02 毫秒,其余 49 步完好。命令对象的内存代价:每命令约 200 字节,千步历史约 200KB,历史栈按容量 500 截断。
三个不适用场景:一是操作不可逆(跨服数据已发出、外部接口已调用),undo 无法真的撤销外部副作用,命令模式只适合可逆的内部状态变更;二是操作间有强时序依赖(B 的前提是 A 的结果),撤销中间一条会让后续命令的前提失效,需要级联撤销设计;三是高频率执行的重操作(每帧调用的战斗结算),命令对象的构造与历史栈的增长会成为负担,历史留痕用轻量日志替代。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
一、线上事故:连续签到奖励按天数递增,有玩家改本机时间刷出 90 天连签,领 3 倍顶格奖励 30 份;连续天数要靠服务器侧…
一、抛坑提问:精英怪要 200 个不重样的霸气名字,运营手写到手软——把名字拆成首、中、尾三段池,随机各取一段拼接,组合空间…
一、一行代码拆解:if LV[level] = TH then write end —— 日志分级的全部逻辑:每条日志带级别…
一、一行代码拆解:math.abs(a - b) < 1e-6 —— 浮点数不能直接比相等:二进制表示有尾差,判等要用"差的…
一、隐蔽陷阱:判定两名玩家是否同门顺着师徒链逐层上爬,链长时线性还可能环回;并查集把同门合并成集合,查两根是否相同一次到位。…
一、线上事故:给 3 到 97 段城墙统一加防御 buff,逐段循环加 95 次,多段叠加时 3 万次写入卡顿 200 毫秒…