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:
Robot development framework in Mush Client (Lua)


you can handle events like:

-- handle "event1"
Listener.new():Register(Event.new("event1"), EventHandler1.new())
EventHander1 
= class(Callback)
function EventHander1:Do(event, Robot development framework in Mush Client (Lua))
    
-- 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, Robot development framework in Mush Client (Lua))
    
-- 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, Robot development framework in Mush Client (Lua))
    
-- 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(Robot development framework in Mush Client (Lua))
            
local obj = {}
            
do
                
local create
                create 
=
                    
function(c, Robot development framework in Mush Client (Lua))
                        
if c.super then
                            create(c.super, Robot development framework in Mush Client (Lua))
                        
end
                        
if c.ctor then
                            c.ctor(obj, Robot development framework in Mush Client (Lua))
                        
end
                    
end
                create(class_type, Robot development framework in Mush Client (Lua))
            
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, Robot development framework in Mush Client (Lua))
    
-- logging
    helper:Print("Event:", event.type" Timeout:", event.isTimeout, " Triggered:", event.triggered)
    
-- call handler
    self:Do(event, Robot development framework in Mush Client (Lua))
end

function Callback:Do(event, Robot development framework in Mush Client (Lua))
    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(Robot development framework in Mush Client (Lua))
    
-- 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, Robot development framework in Mush Client (Lua))
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, Robot development framework in Mush Client (Lua))
    
local matchs = self:Match(eventType)
    
if (#matchs ~= 0then
        
for k, v in pairs (matchs) do
            v:OnEvent(Robot development framework in Mush Client (Lua))
        
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(Robot development framework in Mush Client (Lua))
    
if self.isPrint then
        Note(Robot development framework in Mush Client (Lua))
    
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()

相关文章: