北大侠客行MUD论坛

 找回密码
 注册
搜索
热搜: 新手 wiki 升级
查看: 6395|回复: 2

[转帖]Robot development framework in Mush Client (Lua)

[复制链接]
发表于 2010-10-6 09:41:00 | 显示全部楼层 |阅读模式
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:
[/img]


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 ==
nil) then
-- 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 ==
nil) then
-- 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 ~=
0) then
-- 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 ~=
0) then
for k, v in
pairs (matchs) do
            v:OnEvent(...)
        
end
end
end
function EventDispatcher:SendTimeout(timerName)
   
local matchs = self:Match(timerName)
   
if (#matchs ~=
0) then
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, 1, 1) ==
"#") 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 ==
nil) then
        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 ~=
0) then
            cmd
= cmdSender.cmds[1] -- pick cmd in queue

table.remove (cmdSender.cmds, 1) -- remove cmd in queue

end
if (cmd ~=
nil) then
            cmdSender.isRunning
=
true
if (string.sub(cmd, 1, 1) ==
"@") then
local interval =
tonumber(string.sub(cmd, 2))
               
if (interval >
0) then
                    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()
http://archive.cnblogs.com/a/1241569/

[ 本帖最后由 wzice 于 2010-10-6 09:47 AM 编辑 ]
北大侠客行Mud(pkuxkx.com),最好的中文Mud游戏!
发表于 2010-10-6 21:31:40 | 显示全部楼层
艹,什么鸟东东
北大侠客行Mud(pkuxkx.com),最好的中文Mud游戏!
发表于 2010-10-7 00:29:09 | 显示全部楼层
发帖的是谁的小号?
北大侠客行Mud(pkuxkx.com),最好的中文Mud游戏!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|北大侠客行MUD ( 京ICP备16065414号-1 )

GMT+8, 2024-11-28 10:43 PM , Processed in 0.013290 second(s), 14 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表