• Robot development framework in Mush Client (Lua)


    Play mud games? Using Mush Client to write robot? Let's see what I show you~

    This is a framework I wrote for writing robot with Lua Script in Mush Client.
    The framework enables you dealing with every action/command base on the message/event type, and do it in sequence, which means you needn't pay attention to enable or disable a trigger, and just focuing on the logical of your robot.
    The idea of the framework is liking a Windows Message/Event Driven mode. A serial of events you can define, and handle them in a callback function. The key point is an event can be defined as triggered how many times within a period or infinite time.
    Moreover, a command sender I created helps you send commands for repetition, delaying, or you can extend it to process some special commands by adding codes in Command:Send function. The command sender will ensure all of your commands sent in sequence according to the order you invoke the Command:Add function.

    In order to use this framework to develop your robot, you can follow this way:
    1. define an event you wanna handle.
    2. create a trigger in mush to fire this event.
    3. write a call back function for this event.
    4. in the callback function, you are able to do some response by sending commands using command sender.

    for example:
    you can create a trigger like:



    you can handle events like:

    -- handle "event1"
    Listener.new():Register(Event.new("event1"), EventHandler1.new())
    EventHander1 
    = class(Callback)
    function EventHander1:Do(event, )
        
    -- do something
        cmdSender:Add("cmd1;#3 cmd2;@2;cmd3"-- #3 means repeat 3 times, @2 means delay 2 seconds
    end

    -- handle "event2" five times
    Listener.new():Register(Event.new("event2, 0, 5"), EventHandler2.new())
    EventHander2 
    = class(Callback)
    function EventHander2:Do(event, )
        
    -- do something
        cmdSender:Add({"cmd4""@3""#2 cmd5"}) -- accept commands in a table
    end

    -- handle "event3" twice within 10 seconds. if it is not triggered twice within 10 seconds, a timeout event is sent.
    Listener.new():Register(Event.new("event3, 10, 2"), EventHandler3.new())
    EventHander3 
    = class(Callback)
    function EventHander3:Do(event, )
        
    -- do something
        if (event.isTimeout) then
            cmdSender:Add(
    "cmd6")
        
    else
            cmdSender:Add({
    "cmd7""cmd8"})
        
    end
    end


    Here is the codes of this framework, copy it to your lua script file, then you can use it.

    ---------------------------------------------------------
    --
     OO, implement class module
    --
    -------------------------------------------------------

    local _class = {}

    function class(super)
        
    local class_type = {}
        class_type.ctor 
    = false
        class_type.super 
    = super
        class_type.new 
    =
            
    function()
                
    local obj = {}
                
    do
                    
    local create
                    create 
    =
                        
    function(c, )
                            
    if c.super then
                                create(c.super, )
                            
    end
                            
    if c.ctor then
                                c.ctor(obj, )
                            
    end
                        
    end
                    create(class_type, )
                
    end
                
    setmetatable(obj, { __index = _class[class_type] })
                
    return obj
            
    end
        
    local vtbl = {}
        
    _class[class_type] = vtbl

        
    setmetatable(class_type, { __newindex =
            
    function(t, k, v)
                vtbl[k] 
    = v
            
    end
        })

        
    if super then
            
    setmetatable(vtbl, { __index =
                
    function(t,k)
                    
    local ret = _class[super][k]
                    vtbl[k] 
    = ret
                    
    return ret
                
    end
            })
        
    end

        
    return class_type
    end


    ---------------------------------------------------------
    --
     event
    --
    - type: type of an event
    --
    - timeout: in a particular time(in seconds) didn't receive the event will fire a timeout event
    --
    - times: the event will be triggered how many times, then will be self removed
    --
    -------------------------------------------------------

    Event 
    = class()

    function Event:ctor(type, timeout, times)
        self.
    type = type
        
    if (timeout == nil and times == nilthen
            
    -- if both timeout and times are not set, then can be triggered any times (set times to zero)
            self.timeout = 0
            self.times 
    = 0
        
    elseif (timeout ~= nil and times == nilthen
            
    -- if timeout is set, times is not set, then can be trigger only once
            self.timeout = timeout
            self.times 
    = 1
        
    else
            
    -- if both timeout and times are set, then can be trigger any times within timeout
            self.timeout = timeout
            self.times 
    = times
        
    end
        self.isTimeout 
    = false
        self.triggered 
    = 0
    end

    function Event:Reset()
        self.isTimeout 
    = false
        self.triggered 
    = 0
    end

    ---------------------------------------------------------
    --
     callback: callback function when receved an event
    --
    -------------------------------------------------------

    Callback 
    = class()

    function Callback:ctor(insideFunc)
        self.func 
    = insideFunc
    end

    function Callback:Invoke(event, )
        
    -- logging
        helper:Print("Event:", event.type" Timeout:", event.isTimeout, " Triggered:", event.triggered)
        
    -- call handler
        self:Do(event, )
    end

    function Callback:Do(event, )
        helper:Print(
    "Do Noting")
    end

    ---------------------------------------------------------
    --
     listener
    --
    -------------------------------------------------------

    Listener 
    = class()

    function Listener:ctor()
        self.id 
    = CreateGUID()
    end

    function Listener:Register(event, callback)
        
    assert(event.type ~= nil"event type is nil")
        self.event 
    = event
        self.callback 
    = callback
        
    -- create timer if has timeout
        if (event.timeout ~= 0then -- create timer using type as timer name
            helper:AddTimer(self.event.type, self.event.timeout)
        
    end
        
    -- add self in listener list
        dispatcher:AddListener(self)
    end

    function Listener:Remove()
        
    assert(self.event ~= nil"have to register event then remove it")
        
    -- if has timer and the timer is not timeout, delete it
        if (self.event.timeout ~= 0 and not self.event.isTimeout) then
            helper:RemoveTimer(self.event.
    type)
        
    end
        
    -- remove self in listener list
        dispatcher:RemoveListener(self)
    end

    function Listener:OnEvent()
        
    -- add triggered times
        self.event.triggered = self.event.triggered + 1
        
    -- check if reach triggered times
        if (self.event.times ~= 0 and self.event.triggered == self.event.times) then
            self:Remove()
        
    end
        
    -- call back
        self.callback:Invoke(self.event, )
    end

    function Listener:OnTimeout()
        
    -- set isTimeout and call back
        self.event.isTimeout = true
        
    -- delete listener
        self:Remove()
        
    -- call back
        self.callback:Invoke(self.event)
    end

    ---------------------------------------------------------
    --
     event dispatcher
    --
    -------------------------------------------------------

    EventDispatcher 
    = class()

    function EventDispatcher:ctor()
        self.listeners 
    = {}
    end

    function EventDispatcher:AddListener(listener)
        self.listeners[listener.id] 
    = listener
    end

    function EventDispatcher:RemoveListener(listener)
        self.listeners[listener.id] 
    = nil
    end

    function EventDispatcher:IsListening(listener)
        
    return (self.listeners[listener.id] ~= nil)
    end

    function EventDispatcher:Match(eventType)
        
    local matchs = {}
        
    for k, v in pairs (self.listeners) do
            
    if (v.event.type == eventType) then
                
    table.insert(matchs, v)
            
    end
        
    end
        
    return matchs
    end

    function EventDispatcher:SendEvent(eventType, )
        
    local matchs = self:Match(eventType)
        
    if (#matchs ~= 0then
            
    for k, v in pairs (matchs) do
                v:OnEvent()
            
    end
        
    end
    end

    function EventDispatcher:SendTimeout(timerName)
        
    local matchs = self:Match(timerName)
        
    if (#matchs ~= 0then
            
    for k, v in pairs (matchs) do
                v:OnTimeout()
            
    end
        
    end
    end

    -- only one instance
    dispatcher = EventDispatcher.new()

    ---------------------------------------------------------
    --
     Helper
    --
    -------------------------------------------------------

    Helper 
    = class()

    function Helper:ctor()
        self.isPrint 
    = false
        self.cmds 
    = {}
    end

    function Helper:Print()
        
    if self.isPrint then
            Note()
        
    end
    end

    function Helper:AddTimer(name, interval)
        
    local hours = math.floor(interval / 3600)
        interval 
    = interval - (hours * 3600)
        
    local minutes = math.floor(interval / 60)
        
    local seconds = interval - (minutes * 60)
        
    local status = AddTimer (name, hours, minutes, seconds, "dispatcher:SendTimeout(\"" .. name .. "\")", timer_flag.OneShot + timer_flag.Temporary + timer_flag.Replace, "")
        
    assert(status == error_code.eOK, "fail to create timer:" .. name)
        SetTimerOption(name, 
    "send_to"12)
        EnableTimer(name, 
    true)
        ResetTimer(name)
    end

    function Helper:ResetTimer(name, interval)
        
    assert(IsTimer(name), "timer doesn't exist")
        EnableTimer(name, 
    false)
        
    local hours = math.floor(interval / 3600)
        interval 
    = interval - (hours * 3600)
        
    local minutes = math.floor(interval / 60)
        
    local seconds = interval - (minutes * 60)
        SetTimerOption(name, 
    "hour", hours)
        SetTimerOption(name, 
    "minute", minutes)
        SetTimerOption(name, 
    "second", seconds)
        EnableTimer(name, 
    true)
        ResetTimer(name)
    end

    function Helper:RemoveTimer(name)
        EnableTimer(name, 
    false)
        DeleteTimer(name)
    end

    -- only one instance
    helper = Helper.new()

    ---------------------------------------------------------
    --
     Command
    --
    - Repeat: #4 xx (repeat 4 times for command xx)
    --
    - Delay: @3 (delay 3 seconds)
    --
    -------------------------------------------------------

    Command 
    = class()

    function Command:ctor()
        self.cmds 
    = {}
        self.isRunning 
    = false
        self.thread 
    = nil
    end

    function Command:ToTable(cmds)
        
    assert(type(cmds) == "string""commands must be string type")
        
    local retVal = {}
        
    for k, v in pairs(utils.split(cmds, ";")) do
            
    if (string.sub(v, 11== "#"then -- convert repeat command
                local sb, se = string.find(v, "%s+")
                
    assert(sb ~= nil and se ~= nil"wrong repeat command format")
                
    local times = tonumber(string.sub(v, 2, sb - 1))
                
    local cmd = string.sub(v, se + 1)
                
    for i = 1, times, 1 do
                    retVal[
    #retVal + 1= cmd
                
    end
            
    else
                retVal[
    #retVal + 1= v
            
    end
        
    end
        
    return retVal
    end

    function Command:Add(cmds)
        
    if (type(cmds) == "string"then
            cmds 
    = self:ToTable(cmds)
        
    end
        
    assert(type(cmds) == "table""commands must be string or table type")
        
    -- add cmds
        for k, v in pairs (cmds) do
            self.cmds[
    #self.cmds + 1= v
        
    end
        
    -- wakeup to process
        self:Wakeup()
    end

    function Command:Clear()
        self.cmds 
    = {}
    end

    function Command:Wakeup()
        
    if (self.thread == nilthen
            cmdSender.thread 
    = coroutine.create(cmdSender.Do)
        
    end
        
    if (not self.isRunning) then
            
    coroutine.resume(self.thread)
        
    end
    end

    function Command:Do()
        
    while true do
            
    local cmd = nil
            
    if (#cmdSender.cmds ~= 0then
                cmd 
    = cmdSender.cmds[1-- pick cmd in queue
                table.remove (cmdSender.cmds, 1-- remove cmd in queue
            end
            
    if (cmd ~= nilthen
                cmdSender.isRunning 
    = true
                
    if (string.sub(cmd, 11== "@"then
                    
    local interval = tonumber(string.sub(cmd, 2))
                    
    if (interval > 0then
                        helper:Print(
    "delay:", interval)
                        DoAfterSpecial(interval, 
    "coroutine.resume(cmdSender.thread)"12)
                        
    coroutine.yield()
                    
    end
                
    else
                    cmdSender:Send(cmd)
                
    end
            
    else
                cmdSender.isRunning 
    = false
                
    coroutine.yield()
            
    end
        
    end
    end

    function Command:Send(cmd)
        helper:Print(
    "cmd:", cmd)
        
    if (IsAlias(cmd) == error_code.eOK) then
            SendImmediate(GetAliasInfo(cmd, 
    2))
        
    else
            SendImmediate(cmd)
        
    end
    end

    cmdSender 
    = Command.new()
  • 相关阅读:
    sqlserver FOR XML查询参数RAW的实例
    Dinky使用——mysql2clickhouse
    7、Canal实现MySQL到ES实时同步2
    加一
    极大极小游戏
    Dinky 0.6.5安装部署
    多数元素
    Dinky的使用——hbase2mysql
    packetbeat配置
    大数据分析与展示
  • 原文地址:https://www.cnblogs.com/wj/p/1241569.html
Copyright © 2020-2023  润新知