mirror of
https://github.com/Refactorio/RedMew.git
synced 2024-12-12 10:04:40 +02:00
120 lines
4.6 KiB
Lua
120 lines
4.6 KiB
Lua
--- This module provides a classical mealy/moore state machine.
|
|
-- Each machine in constructed by calling new()
|
|
-- States and Transitions are lazily added to the machine as transition handlers and state tick handlers are registered.
|
|
-- However the state machine must be fully defined after init is done. Dynamic machine changes are currently unsupported
|
|
-- An example usage can be found here: map_gen\combined\tetris\control.lua
|
|
|
|
local Module = {}
|
|
|
|
local Debug = require 'utils.debug'
|
|
|
|
local in_state_callbacks = {}
|
|
local transaction_callbacks = {}
|
|
local max_stack_depth = 20
|
|
local machine_count = 0
|
|
local control_stage = _STAGE.control
|
|
|
|
--- Transitions the supplied machine into a given state and executes all transaction_callbacks
|
|
-- @param self StateMachine
|
|
-- @param new_state number/string The new state to transition to
|
|
function Module.transition(self, new_state)
|
|
Debug.print(string.format('Transitioning from state %d to state %d.', self.state, new_state))
|
|
local old_state = self.state
|
|
|
|
local stack_depth = self.stack_depth
|
|
self.stack_depth = stack_depth + 1
|
|
if stack_depth > max_stack_depth then
|
|
if _DEBUG then
|
|
error('[WARNING] Stack overflow at:' .. debug.traceback())
|
|
else
|
|
log('[WARNING] Stack overflow at:' .. debug.traceback())
|
|
end
|
|
end
|
|
|
|
local exit_callbacks = transaction_callbacks[self.id][old_state]
|
|
if exit_callbacks then
|
|
local entry_callbacks = exit_callbacks[new_state]
|
|
if entry_callbacks then
|
|
for i = 1, #entry_callbacks do
|
|
local callback = entry_callbacks[i]
|
|
if callback then
|
|
callback()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
self.state = new_state
|
|
end
|
|
|
|
--- Is this machine in this state?
|
|
-- @param self StateMachine
|
|
-- @param state number/string
|
|
-- @return boolean
|
|
function Module.in_state(self, state)
|
|
return self.state == state
|
|
end
|
|
|
|
--- Invoke a machine tick. Will execute all in_state_callbacks of the given machine
|
|
-- @param self StateMachine the machine, whose handlers will be invoked
|
|
function Module.machine_tick(self)
|
|
local callbacks = in_state_callbacks[self.id][self.state]
|
|
if callbacks then
|
|
for i=1, #callbacks do
|
|
local callback = callbacks[i]
|
|
if callback then
|
|
callback()
|
|
end
|
|
end
|
|
end
|
|
self.stack_depth = 0
|
|
end
|
|
|
|
--- Register a handler that will be invoked by StateMachine.machine_tick
|
|
-- You may register multiple handlers for the same transition
|
|
-- NOTICE: This function will invoke an error if called after init. Dynamic machine changes are currently unsupported
|
|
-- @param self StateMachine the machine
|
|
-- @param state number/string The state, that the machine will be in, when callback is invoked
|
|
-- @param callback function
|
|
function Module.register_state_tick_callback(self, state, callback)
|
|
if _LIFECYCLE ~= control_stage then
|
|
error('Calling StateMachine.register_state_tick_callback after the control stage is unsupported due to desyncs.', 2)
|
|
end
|
|
in_state_callbacks[self.id][state] = in_state_callbacks[self.id][state] or {}
|
|
table.insert(in_state_callbacks[self.id][state], callback)
|
|
end
|
|
|
|
--- Register a handler that will be invoked by StateMachine.transition
|
|
-- You may register multiple handlers for the same transition
|
|
-- NOTICE: This function will invoke an error if called after init. Dynamic machine changes are currently unsupported
|
|
-- @param self StateMachine the machine
|
|
-- @param state number/string exiting state
|
|
-- @param state number/string entering state
|
|
-- @param callback function
|
|
function Module.register_transition_callback(self, old, new, callback)
|
|
if _LIFECYCLE ~= control_stage then
|
|
error('Calling StateMachine.register_transition_callback after the control stage is unsupported due to desyncs.', 2)
|
|
end
|
|
transaction_callbacks[self.id][old] = transaction_callbacks[self.id][old] or {}
|
|
transaction_callbacks[self.id][old][new] = transaction_callbacks[self.id][old][new] or {}
|
|
table.insert(transaction_callbacks[self.id][old][new], callback)
|
|
end
|
|
|
|
--- Constructs a new state machine
|
|
-- @param init_state number/string The starting state of the machine
|
|
-- @return StateMachine The constructed state machine object
|
|
function Module.new(init_state)
|
|
if _LIFECYCLE ~= control_stage then
|
|
error('Calling StateMachine.new after the control stage is unsupported due to desyncs.', 2)
|
|
end
|
|
machine_count = machine_count + 1
|
|
in_state_callbacks[machine_count] = {}
|
|
transaction_callbacks[machine_count] = {}
|
|
return {
|
|
state = init_state,
|
|
stack_depth = 0,
|
|
id = machine_count,
|
|
}
|
|
end
|
|
|
|
return Module
|