lua版本的一个状态机

这个状态机http://www.cnblogs.com/flytrace/p/5587033.html的lua版本

-- LUA 有实现枚举值的好办法么
local sc_enum =
{
-- event flow be broke up
none = "none",
-- event flow pass througth next state
forward = "forward",
-- deffer a speciafic event
--deffer = 4 -- select by default(the first position in the list)
shallow = "shallow",
-- the state will remain actived state that last time was
deep = "deep", -- 不实现。该模式定义不良好。多个嵌套的子并行状态相对复杂,流程不清晰,数据结构也不直接。
-- 还没找到比较良好的并行状态语义。
-- only one substate can run at one time by default
singleman = "singleman",
-- the state will be enabled always, substate in parallel region can't transit to none-parallel region
parallel = "parallel", } return sc_enum
-- Date 2016-7-13
-- yanfei --[[
一个sc_fsm也是一个sc_state,状态机本身也可以作为一个状态,但目前子状态机只能视为状态,未能作为独立的机制运行
sc_fsm
{
type2states -- save all states accord to their name in a dictionary
}
--]]
sc_helper = require("fsm.sc_helper")
require("utils.sc_list")
local sc_queue = require("utils.sc_queue")
local sc_enum = require("fsm.sc_enum")
local sc_state = require("fsm.sc_state")
local classprototype = require("utils.middleclass")
-- a fsm is a state
local fsm = classprototype("sc_fsm", sc_state) -- 建立类型-状态表
local function buildstates(fsm, astate)
fsm.type2states[astate.statename] = astate
if astate.substates then
for statename_, state_ in ipairs(astate.substates) do
assert(fsm.type2states[state_.statename] == nil, string.format("[%s]: has a state named [%s] already!", fsm.statename, state_.statename))
buildstates(fsm, state_)
end
end
end -- 重载initialize
-- @statename_ 状态名字
-- @basestate_ 父状态
-- @statemode_ 运行模式
-- @operation_ 激活操作
function fsm:initialize(statename_, basestate_, statemode_, operation_)
sc_state.initialize(self, statename_, basestate_, statemode_, operation_)
end function fsm:init(initstatename)
assert(type(self) == 'table', "Make sure that you are using 'fsm:init' instead of 'fsm.init'") self.type2states = {}
buildstates(self, self) local initstate = self.type2states[initstatename]
assert(initstate and initstate:isInstanceOf(sc_state), string.format("[%s] hasn't state named [%s]", self.statename, initstatename)) -- 建立主激活列表
local astate = initstate
while astate do
astate.activesubstate = astate.substates and astate.substates[] or nil
self.ineststate = astate.activesubstate and astate.activesubstate or astate
astate = astate.activesubstate
end astate = initstate
while astate ~= self do
astate.basestate.activesubstate = astate
astate = astate.basestate
end astate = self
while astate.activesubstate ~= nil do
if astate.activesubstate.enter then astate.activesubstate:enter(self) end
astate = astate.activesubstate
end if not self.eventqueue then self.eventqueue = sc_queue:newqueue() end
self.suspend = false
self.__lockqueue = false
self.__transiting = false return true
end -- 跳转到目标状态
function fsm:transit( targetname_ )
assert(type(self) == 'table', "Make sure that you are using 'fsm:transit' instead of 'fsm.transit'") if self.__transiting then
self.__transiting = false
return sc_enum.none
end self.__transiting = true; local targetstate = self.type2states[targetname_]
assert(targetstate and targetstate:isInstanceOf(sc_state), string.format("[%s] hasn't state named [%s]", self.statename, targetname_)) -- detect the whole active state list
local astate = self.ineststate
--
local bstate = targetstate
local dis = astate.depth - bstate.depth
if dis < then
bstate, astate = astate, targetstate
end -- bstate is topper dis = math.abs(dis)
for i = , dis do astate = astate.basestate end --now they are both same depth if astate == bstate then -- is family
self.__transiting = false
return sc_enum.none
end -- find nearest ancestor both
while astate ~= bstate do
astate, bstate = astate.basestate, bstate.basestate
end -- first we will call exit chain
while self.ineststate ~= astate do
if self.ineststate.exit then self.ineststate:exit(self) end
self.ineststate = self.ineststate.basestate
end -- now we will decide the enter chain -- build active chain down side
local astate = targetstate
while astate do
astate.activesubstate = (astate.substates and astate.statemode == sc_enum.shallow)
and astate.substates[] or astate.activesubstate
self.ineststate = astate.activesubstate and astate.activesubstate or astate
astate = astate.activesubstate
end -- build active chain up side
astate = targetstate
while astate ~= self do
astate.basestate.activesubstate, astate = astate, astate.basestate
end -- call enter chain
while bstate.activesubstate ~= nil do
if bstate.activesubstate.enter then bstate.activesubstate:enter(self) end
bstate = bstate.activesubstate
end return sc_enum.none
end function fsm:process_event( eventname_, ...)
assert(type(self) == 'table', "Make sure that you are using 'fsm:process_event' instead of 'fsm.process_event'")
if self.suspend then return nil end
if self.__lockqueue then return nil end local pevent = {...}
pevent.eventname = eventname_
if not self.eventqueue then self.eventqueue = sc_queue:newqueue() end
local queue = self.eventqueue
queue:push_back(pevent) self.__lockqueue = true
--local eventcount = queue:count()
while true do
--eventcount = eventcount - 1
local astate = self
local processflag_ = sc_enum.forward
pevent = queue:pop_front()
if not pevent then break end
while astate.activesubstate ~= nil do
processflag_ = astate.activesubstate:_internal_process(self, pevent)
if processflag_ == sc_enum.none then
return processflag_
end
astate = astate.activesubstate
end
end self.__lockqueue = false return processflag_
end --
function fsm:post_event( eventname_, ... )
local pevent = {...}
pevent.eventname = eventname_
self.eventqueue:push_back(pevent)
end function fsm:context( statename_ )
local targetstate = self.type2states[targetname_]
--assert(targetstate and targetstate:isInstanceOf(sc_state), string.format("[%s] hasn't state named [%s]", self.statename, targetname_))
return targetstate
end return fsm
-- Date 2016-7-13
-- yanfei --[[
-- sc_state
-- {
statename, -- 状态名
basestate, -- 父状态
statemode, -- 历史模式
operation, -- 操作,完成并行,筛选。目前保留字段 substates, -- 子状态列表
activestate, -- 当前激活状态 events,, -- event-callback表
-- }
-- 一个状态是一个节点,basestate指向父状态,substates指向子状态列表,所有状态形成一棵树
--]] require("utils.sc_list")
local sc_enum = require("fsm.sc_enum")
--require("fsm.sc_event")
local classprototype = require("utils.middleclass")
local sc_state = classprototype("sc_state") local rawget = rawget
local rawset = rawset
local string = string -- 重载initialize
-- @statename_ 状态名字
-- @basestate_ 父状态
-- @statemode_ 运行模式
-- @operation_ 激活操作
function sc_state:initialize(statename_, basestate_, statemode_, operation_)
assert(type(statename_) == "string","statename must be a string")
self.statename = statename_
self.statemode = statemode_ or sc_enum.shallow
--self.operation = operation_ or sc_enum.singleman -- 处理父子关系
assert(basestate_ and basestate_:isInstanceOf(sc_state) or basestate_ == nil, string.format("[%s]: base state must be a sc_state", statename_))
self.basestate = basestate_
if basestate_ then basestate_:addsubstate(self) end -- 状态深度 top state is 1
self.depth = self.basestate and self.basestate.depth + or end -- 创建该状态的子状态
function sc_state:createstate(statename_, statemode_, operation_)
return sc_state:new(statename_, self, statemode_, operation_)
end -- 添加子状态
function sc_state:addsubstate(...)
-- 填充子状态列表
self.substates = self.substates or {}
for i, state_ in ipairs{...} do
assert(state_:isInstanceOf(sc_state), "substate must be a sc_state")
assert(self.substates[state_.statename] == nil, string.format("%s has a substate named [%s] already!", self.statename, state_.statename))
table.insert(self.substates, state_)
end
end -- 绑定事件
-- @eventname_ 事件名
-- @callbackname_ 事件处理函数名
function sc_state:bind_event(eventname_, callbackname_)
assert(type(self) == 'table', "Make sure that you are using 'sc_state:process_event' instead of 'sc_state.process_event'")
assert(type(eventname_) == 'string', "Make sure that callbakname is a string")
assert(type(callbackname_) == 'string', "Make sure that callbakname is a string")
--local callback_ = self[callbackname_]
local callback_ = rawget(self, callbackname_)
assert(callback_, string.format("[%s] bind_event event[%s]: hasn't a function named [%s]", self.statename, eventname_, callbackname_))
assert(type(callback_) == "function", string.format("[%s]:bind_event event[%s]: callback must be a function", self.statename, eventname_)) self.events = self.events or {}
self.events[eventname_] = function(fsm, ...) return callback_(self, fsm, ...) end
end function sc_state:_internal_process(fsm, pevent)
if not self.events then return sc_enum.forward end
local callback_ = self.events[pevent.eventname]
if not callback_ then return sc_enum.forward end
return callback_(fsm, unpack(pevent))
end return sc_state
--region *.lua
--Date 2016.7.17
-- yanfei local queue = {} function queue:newqueue()
local o = {first = , last = -}
setmetatable(o, self)
self.__index = self
return o
end function queue:push_front(v)
local first = self.first -
self.first = first
self[first] = v
end function queue:pop_front()
local first = self.first
if first > self.last then return nil end
local v = self[first]
self[first] = nil
self.first = first +
return v
end function queue:push_back(v)
local last = self.last +
self.last = last
self[last] = v
end function queue:pop_back()
local last = self.last
if last < self.first then return nil end
local v = self[last]
self[last] = nil
self.last = last -
return v
end function queue:count()
return self.last - self.first +
end return queue
--endregion
--region *.lua

module ("sc_list", package.seeall)

--- An iterator over the elements of a list.
-- @param l list to iterate over
-- @return iterator function which returns successive elements of the list
-- @return the list <code>l</code> as above
-- @return <code>true</code>
function elems (l)
local n =
return function (l)
n = n +
if n <= #l then
return l[n]
end
end,
l, true
end --- An iterator over the elements of a list, in reverse.
-- @param l list to iterate over
-- @return iterator function which returns precessive elements of the list
-- @return the list <code>l</code> as above
-- @return <code>true</code>
function relems (l)
local n = #l +
return function (l)
n = n -
if n > then
return l[n]
end
end,
l, true
end --- Fold a binary function into an iterator.
-- @param f function
-- @param d initial first argument
-- @param i iterator
-- @return result
function fold_x (f, d, i, t)
local r = d
for e in i (t) do
r = f (r, e)
end
return r
end function fold (f, d, t)
local r = d
for e in elems (t) do
r = f (r, e)
end
return r
end ----- Fold a binary function through a list right associatively.
---- @param f function
---- @param e element to place in right-most position
---- @param l list
---- @return result
--function foldr (f, e, l)
-- return _G.fold (function (x, y) return f (y, x) end,
-- e, relems, l)
--end function find(f, t)
for e in elems (t) do
if f(e) then return e end
end
return nil
end function findall(f, t)
local result = {}
for e in elems(t) do
if f(e) then result[#result+] = (e) end
end
return result
end function join(a, b)
return {unpack(a), unpack(b)}
end function cons(a, t)
return {a, unpack(t)}
end --endregion

使用

-- 只是一个例子

local sc_enum = require("fsm.sc_enum")
local sc_fsm_pt = require("fsm.sc_fsm") local gamemain_fsm = sc_fsm_pt:new("gamemain_fsm") local print = print --[[
-- gamemain_state
--]]
local gamemain_state = gamemain_fsm:createstate("gamemain_state")
function gamemain_state:enter(fsm)
print("enter "..self.statename)
end function gamemain_state:exit(fsm)
print("exit "..self.statename)
end function gamemain_state:update(fsm, delta)
print("update "..self.statename)
print("timedelta: "..tostring(delta))
end gamemain_state:bind_event("game_update", "update") --[[
-- gamemain_init_state
--]]
local gamemain_init_state = gamemain_state:createstate("gamemain_init_state") function gamemain_init_state:enter(fsm)
print("enter "..self.statename)
end function gamemain_init_state:exit(fsm)
print("exit "..self.statename)
end --[[
-- gamemain_init_waiting_state
--]]
local gamemain_init_waiting_state = gamemain_init_state:createstate("gamemain_init_waiting_state") function gamemain_init_waiting_state:enter(fsm)
print("enter "..self.statename)
end function gamemain_init_waiting_state:exit(fsm)
print("exit "..self.statename)
end --[[
-- gamemain_idle_state
--]]
local gamemain_idle_state = gamemain_state:createstate("gamemain_idle_state") function gamemain_idle_state:enter(fsm)
print("enter "..self.statename)
end function gamemain_idle_state:exit(fsm)
print("exit "..self.statename)
end function gamemain_idle_state:update(fsm, delta)
print("update "..self.statename)
print("timedelta: "..tostring(delta))
--fsm:process_event("game_upadte", 0.0001)
end function gamemain_idle_state:tolobby(fsm, msg1, msg2)
print(msg1..' '..msg2)
print("ready to taransit to lobby")
return fsm:transit("gamemain_lobby_state")
end gamemain_idle_state:bind_event("game_update", "update")
gamemain_idle_state:bind_event("event_tolobby", "tolobby")
--[[
-- gamemain_net_state
--]]
local gamemain_net_state = gamemain_state:createstate("gamemain_net_state")
function gamemain_net_state:enter(fsm)
print("enter "..self.statename)
end function gamemain_net_state:exit(fsm)
print("exit "..self.statename)
end --[[
-- gamemain_net_send_state
--]]
local gamemain_net_send_state = gamemain_net_state:createstate("gamemain_net_send_state")
function gamemain_net_send_state:enter(fsm)
print("enter "..self.statename)
end function gamemain_net_send_state:exit(fsm)
print("exit "..self.statename)
end --[[
-- gamemain_net_receive_state
--]]
local gamemain_net_receive_state = gamemain_net_state:createstate("gamemain_net_receive_state")
function gamemain_net_receive_state:enter(fsm)
print("enter "..self.statename)
end function gamemain_net_receive_state:exit(fsm)
print("exit "..self.statename)
end --[[
-- gamemain_lobby_state
--]]
local gamemain_lobby_state = gamemain_state:createstate("gamemain_lobby_state")
function gamemain_lobby_state:enter(fsm)
print("enter "..self.statename)
end function gamemain_lobby_state:exit(fsm)
print("exit "..self.statename)
end --[[
-- gamemain_lobby_shop_state
--]]
local gamemain_lobby_shop_state = gamemain_lobby_state:createstate("gamemain_lobby_shop_state")
function gamemain_lobby_shop_state:enter(fsm)
print("enter "..self.statename)
fsm:post_event("event_opendoor", "opendoor")
end function gamemain_lobby_shop_state:exit(fsm)
print("exit "..self.statename)
end function gamemain_lobby_shop_state:update(fsm, delta)
print("update "..self.statename)
print("timedelta: "..tostring(delta))
end function gamemain_lobby_shop_state:opendoor(fsm)
print("open the door")
end gamemain_lobby_shop_state:bind_event("game_update", "update")
gamemain_lobby_shop_state:bind_event("event_opendoor", "opendoor") --[[
-- gamemain_lobby_dungeon_state
--]]
local gamemain_lobby_dungeon_state = gamemain_lobby_state:createstate("gamemain_lobby_dungeon_state")
function gamemain_lobby_dungeon_state:enter(fsm)
print("enter "..self.statename)
end function gamemain_lobby_dungeon_state:exit(fsm)
print("exit "..self.statename)
end return gamemain_fsm
--region *.lua
--Date sc_helper = require("fsm.sc_helper")
local sc_event = require("fsm.sc_event")
local queue = require("utils.sc_queue") -- 获取定义好的gamemain状态机
local gamemain_fsm = require("game.gamemain_fsm")
-- 打印状态树
sc_helper:printstates(gamemain_fsm) -- 初始化状态机,初始状态为gamemain_init_state,进入gamemain_init_state时将自动进入其子状态gamemain_init_waiting_state
-- 进入某个状态后,如果有子状态,会尝试进入其子状态
gamemain_fsm:init("gamemain_init_state") -- 自顶向下打印当前激活的状态链
sc_helper:printactivestates(gamemain_fsm) -- 状态迁移到gamemain_idle_state
gamemain_fsm:transit("gamemain_idle_state") sc_helper:printactivestates(gamemain_fsm) -- 发送事件game_update, 在gamemain_idle_state中注册了对该事件的响应
gamemain_fsm:process_event("game_update", 0.0333333)
-- 发送事件event_tolobby,在gamemain_idle_state中注册了对该事件的响应。在响应中将调用gamemain_fsm:transit迁移到gamemain_lobby_state
gamemain_fsm:process_event("event_tolobby", "hello", "world")
sc_helper:printactivestates(gamemain_fsm)
gamemain_fsm:process_event("game_update", 0.0333333) print("over")
--endregion
上一篇:java项目中读取src目录下的文件


下一篇:kafka设计原理介绍