跳转至内容
  • 欢迎
  • 版块
  • 最新
  • 标签
  • 热门
  • 用户
  • 群组
皮肤
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • 默认(不使用皮肤)
  • 不使用皮肤
折叠
品牌标识

Hellclient 社区

  1. 主页
  2. Script脚本
  3. 深入浅出制作全自动Mud机器人-同步

深入浅出制作全自动Mud机器人-同步

已定时 已固定 已锁定 已移动 Script脚本
全自动机器人代码范例
1 帖子 1 发布者 16 浏览
  • 从旧到新
  • 从新到旧
  • 最多赞同
回复
  • 在新帖中回复
登录后回复
此主题已被删除。只有拥有主题管理权限的用户可以查看。
  • jarlyynJ 离线
    jarlyynJ 离线
    jarlyyn
    编写于 最后由 编辑
    #1

    同步是和异步相对的概念,一般来说,编码里的同步和异步的区别是在于是否阻塞。

    • 同步的代码会阻塞并等待任务的返回结果。
    • 异步的代码一般只发起任务,不关心任务是否完成,可以通过回调/通道之类的方法获取处理结果。

    在我们的机器人中,一般会用异步的方式抛出一系列的指令,然后需要一个同步的指令,阻塞任务队列,保证不会被其他的信息污染,来进行更详细的指令判断。

    比如,我newhelljjs中有一个炼丹模块,代码如下

            $.PushCommands(
                $.To("1387"),
                $.Do("give cao yao to xiao tong"),
                $.To("1389"),
                $.Do("i"),
                $.Sync(),
                $.Plan(PlanLiandan),
                $.Sync(),
                $.To("1388"),
                $.Ask("yao chun", "炼丹"),
               ...
               )
    

    就很明显,用了两个$.Sync的同步指令,确保在新指令执行时,之前的指令已经处理完毕。
    比如第一个$.Sync(),就是确保i指令已经从Mud服务端获取到了完整的回复,更新了角色的当前物品清单信息。

    选择合适的指令

    首先,我们的代码会需要选择一个合适的同步指令。这个指令要满足以下的要求:

    1. 什么时候都能使用
    2. 返回固定,没有消耗
    3. 对服务器压力低
    4. 平时不会使用,避免误触发

    那么,对于没有提供对应低消耗指令的mud,我们一般会找这样的指令:

    1.已经废弃的指令,使用时只有废弃提示。
    2.有特殊格式要求的,不带参数会报错的。

    注意,一个正经的机器人我们还是要注意服务器消耗的。不光光是公德心的问题,毕竟写的太丑的机器人也丢自己脸是不?

    另外,除了同步指令,一般建议再找一个判忙的指令,要求和同步的指令类似,区别是在忙和不忙得情况下,回复会有不同。

    毕竟忙(busy)也是很多mud的重要状态之一。而且很多时候,判忙也能代替同步指令的作用(更复杂的同步)。

    合理的使用

    使用同步/忙指令主要有以下特殊点:

    1. 避免连续重复使用,容易导致信号失真,特别是有可能进入状态重置的情况。
      2.在无法避免信号失真的情况下,要保证机器能在短暂的失效后能重回正轨,有一定的容错性。
      3.对于复杂场景,要有个统一的失效机制。也就是不要在等到同步信号之前就开始执行指令,在使用了同步机制后,要统一的在同步完成后再开始小一步。

    举个例子,由于我的机器采用的是预期管理的模型驱动的,我特地建立一个Response的作用范围,然后效果如下:

        let PlanTimes = new App.Plan(
            App.Positions["Response"],
            (task) => {
                task.AddTrigger(matcherTimes, (tri, result) => {
                    if (result[1] == San.Data.WeaponName) {
                        task.Data = result[2]
                    }
                })
                App.Send(`l ${San.Data.Weapon}`)
                App.Sync()
            },
            (result) => {
                if (result.Task.Data) {
                    San.Data.Times = App.CNumber.ParseNumber(result.Task.Data)
                    App.HUD.Update()
                }
                App.Next()
            }
        )
    

    利用我的期望失效的机制,强制在App.Sync()后,接收到同步信号时,通过Task.Data来判断结果和下一步的动作,避免会在同步信号前再发送一个同步指令,造成混乱。

    避免滥用同步指令

    同步指令一般只用在发送多个指令后进入不可预期的状态时的确认。

    很多有明确预期的指令,比如移动,遍历,不应该以来同步指令,避免对服务器的不必要的压力和效率降低。

    最典型的就是判断当前房间移动结束,或者房间的物品列表。

    尽量使用 格式的变化(比如第一个出现的非房间物品)来判断。

    比如

        App.BindEvent("core.onexit", App.Core.Room.OnExit)
        let matcherOnHeal = /^    (\S{2,8})正坐在地下(.+)。$/
        let matcherOnYanlian = /^    (\S{2,8})正在演练招式。$/
        let matcherOnObj = /^    ((\S+) )?(\S*[「\(].+[\)」])?(\S+)\(([^\(\)]+)\)( \[.+\])?(( <.+>)*)$/
        //处理得到出口之后的信息(npc和道具列表)的计划
        var PlanOnExit = new App.Plan(App.Positions.Connect,
            function (task) {
                task.AddTrigger(matcherOnObj, function (trigger, result, event) {
                    let item = new objectModule.Object(result[4], result[5], App.History.CurrentOutput).
                        WithParam("身份", result[2]).
                        WithParam("外号", result[3]).
                        WithParam("描述", result[6] || "").
                        WithParam("状态", result[7] || "").
                        WithParam("动作", "")
                    App.Map.Room.Data.Objects.Append(item)
                    event.Context.Set("core.room.onobject", true)
                    return true
                })
                task.AddTrigger(matcherOnHeal, function (trigger, result, event) {
                    let item = new objectModule.Object(result[1], "", App.History.CurrentOutput).
                        WithParam("动作", result[2])
                    App.Map.Room.Data.Objects.Append(item)
                    event.Context.Set("core.room.onobject", true)
                    return true
                })
                task.AddTrigger(matcherOnYanlian, function (trigger, result, event) {
                    let item = new objectModule.Object(result[1], "", App.History.CurrentOutput).
                        WithParam("动作", "演练招式")
                    App.Map.Room.Data.Objects.Append(item)
                    event.Context.Set("core.room.onobject", true)
                    return true
                })
    
                task.AddCatcher("line", function (catcher, event) {
                    let output = event.Data.Output
                    if (output.length > 4 && output.startsWith("    ") && output[4] != " ") {
                        return true
                    }
                    //未匹配过的行代表npc和道具结束
                    return event.Context.Get("core.room.onobject")
                })
            }, function (result) {
                if (result.Type != "cancel") {
                    if (App.Map.Room.Name && !App.Map.Room.ID) {
                        let idlist = App.Map.Data.RoomsByName[App.Map.Room.Name]
                        if (idlist && idlist.length == 1) {
                            App.Map.Room.ID = idlist[0]
                        }
                    }
                    App.RaiseEvent(new App.Event("core.roomentry"))
                }
            })
    

    是通过出现的行不是以4个空格开头(固定格式)来判断的。

    1 条回复 最后回复
    • jarlyynJ jarlyyn 被引用 于这个主题
    回复
    • 在新帖中回复
    登录后回复
    • 从旧到新
    • 从新到旧
    • 最多赞同


    • 登录

    • 没有帐号? 注册

    • 登录或注册以进行搜索。
    Powered by Herbrhythm.
    • 第一个帖子
      最后一个帖子
    0
    • 欢迎
    • 版块
    • 最新
    • 标签
    • 热门
    • 用户
    • 群组