diff --git a/game/fsm/fsm.go b/game/fsm/fsm.go index 61e1589..2bc462e 100644 --- a/game/fsm/fsm.go +++ b/game/fsm/fsm.go @@ -2,54 +2,116 @@ package fsm import ( "fmt" + "github.com/kercylan98/minotaur/utils/hash" ) // NewFSM 创建一个新的状态机 -func NewFSM[S comparable, Data any](data Data) *FSM[S, Data] { - return &FSM[S, Data]{ - states: map[S]*State[S, Data]{}, +func NewFSM[State comparable, Data any](data Data) *FSM[State, Data] { + return &FSM[State, Data]{ + states: map[State]struct{}{}, data: data, } } // FSM 状态机 -type FSM[S comparable, Data any] struct { - current S +type FSM[State comparable, Data any] struct { + prev *State + current *State data Data - states map[S]*State[S, Data] + states map[State]struct{} + + enterBeforeEventHandles map[State][]func(state *FSM[State, Data]) + enterAfterEventHandles map[State][]func(state *FSM[State, Data]) + updateEventHandles map[State][]func(state *FSM[State, Data]) + exitBeforeEventHandles map[State][]func(state *FSM[State, Data]) + exitAfterEventHandles map[State][]func(state *FSM[State, Data]) } // Update 触发当前状态 -func (slf *FSM[S, Data]) Update() { - state := slf.states[slf.current] - state.Update(slf.data) +func (slf *FSM[State, Data]) Update() { + if slf.current == nil { + return + } + for _, event := range slf.updateEventHandles[*slf.current] { + event(slf) + } } // Register 注册状态 -func (slf *FSM[S, Data]) Register(state *State[S, Data]) { - slf.states[state.GetState()] = state +func (slf *FSM[State, Data]) Register(state State, options ...Option[State, Data]) { + slf.states[state] = struct{}{} + for _, option := range options { + option(slf, state) + } } // Unregister 反注册状态 -func (slf *FSM[S, Data]) Unregister(state S) { +func (slf *FSM[State, Data]) Unregister(state State) { + if !slf.HasState(state) { + return + } delete(slf.states, state) + delete(slf.enterBeforeEventHandles, state) + delete(slf.enterAfterEventHandles, state) + delete(slf.updateEventHandles, state) + delete(slf.exitBeforeEventHandles, state) + delete(slf.exitAfterEventHandles, state) } // HasState 检查状态机是否存在特定状态 -func (slf *FSM[S, Data]) HasState(state S) bool { - _, has := slf.states[state] - return has +func (slf *FSM[State, Data]) HasState(state State) bool { + return hash.Exist(slf.states, state) } // Change 改变状态机状态到新的状态 -func (slf *FSM[S, Data]) Change(state S) { - current := slf.states[slf.current] - current.Exit(slf.data) +func (slf *FSM[State, Data]) Change(state State) { + if !slf.IsZero() { + for _, event := range slf.exitBeforeEventHandles[*slf.current] { + event(slf) + } + } - next := slf.states[state] - if next == nil { + slf.prev = slf.current + slf.current = &state + + if !slf.PrevIsZero() { + for _, event := range slf.exitAfterEventHandles[*slf.prev] { + event(slf) + } + } + + if !slf.HasState(state) { panic(fmt.Errorf("FSM object is attempting to switch to an invalid / undefined state: %v", state)) } - next.Enter(slf.data) + for _, event := range slf.enterBeforeEventHandles[*slf.current] { + event(slf) + } + + for _, event := range slf.enterAfterEventHandles[*slf.current] { + event(slf) + } +} + +// Current 获取当前状态 +func (slf *FSM[State, Data]) Current() (state State) { + if slf.current == nil { + return + } + return *slf.current +} + +// GetData 获取状态机数据 +func (slf *FSM[State, Data]) GetData() Data { + return slf.data +} + +// IsZero 检查状态机是否无状态 +func (slf *FSM[State, Data]) IsZero() bool { + return slf.current == nil +} + +// PrevIsZero 检查状态机上一个状态是否无状态 +func (slf *FSM[State, Data]) PrevIsZero() bool { + return slf.prev == nil } diff --git a/game/fsm/fsm_state.go b/game/fsm/fsm_state.go deleted file mode 100644 index 4d73fd8..0000000 --- a/game/fsm/fsm_state.go +++ /dev/null @@ -1,38 +0,0 @@ -package fsm - -type ( - StateEnterHandle[Data any] func(data Data) - StateUpdateHandle[Data any] func(data Data) - StateExitHandle[Data any] func(data Data) -) - -func NewFSMState[S comparable, Data any](state S, enter StateEnterHandle[Data], update StateUpdateHandle[Data], exit StateExitHandle[Data]) *State[S, Data] { - return &State[S, Data]{ - enter: enter, - update: update, - exit: exit, - } -} - -type State[S comparable, Data any] struct { - state S - enter StateEnterHandle[Data] - update StateUpdateHandle[Data] - exit StateExitHandle[Data] -} - -func (slf *State[S, Data]) GetState() S { - return slf.state -} - -func (slf *State[S, Data]) Enter(data Data) { - slf.enter(data) -} - -func (slf *State[S, Data]) Update(data Data) { - slf.update(data) -} - -func (slf *State[S, Data]) Exit(data Data) { - slf.exit(data) -} diff --git a/game/fsm/options.go b/game/fsm/options.go new file mode 100644 index 0000000..259453b --- /dev/null +++ b/game/fsm/options.go @@ -0,0 +1,56 @@ +package fsm + +type Option[State comparable, Data any] func(fsm *FSM[State, Data], state State) + +// WithEnterBeforeEvent 设置状态进入前的回调 +// - 在首次设置状态时,状态机本身的当前状态为零值状态 +func WithEnterBeforeEvent[State comparable, Data any](fn func(state *FSM[State, Data])) Option[State, Data] { + return func(fsm *FSM[State, Data], state State) { + if fsm.enterBeforeEventHandles == nil { + fsm.enterBeforeEventHandles = map[State][]func(state *FSM[State, Data]){} + } + fsm.enterBeforeEventHandles[state] = append(fsm.enterBeforeEventHandles[state], fn) + } +} + +// WithEnterAfterEvent 设置状态进入后的回调 +func WithEnterAfterEvent[State comparable, Data any](fn func(state *FSM[State, Data])) Option[State, Data] { + return func(fsm *FSM[State, Data], state State) { + if fsm.enterAfterEventHandles == nil { + fsm.enterAfterEventHandles = map[State][]func(state *FSM[State, Data]){} + } + fsm.enterAfterEventHandles[state] = append(fsm.enterAfterEventHandles[state], fn) + } +} + +// WithUpdateEvent 设置状态内刷新的回调 +func WithUpdateEvent[State comparable, Data any](fn func(state *FSM[State, Data])) Option[State, Data] { + return func(fsm *FSM[State, Data], state State) { + if fsm.updateEventHandles == nil { + fsm.updateEventHandles = map[State][]func(state *FSM[State, Data]){} + } + fsm.updateEventHandles[state] = append(fsm.updateEventHandles[state], fn) + } +} + +// WithExitBeforeEvent 设置状态退出前的回调 +// - 该阶段状态机的状态为退出前的状态,而非新的状态 +func WithExitBeforeEvent[State comparable, Data any](fn func(state *FSM[State, Data])) Option[State, Data] { + return func(fsm *FSM[State, Data], state State) { + if fsm.exitBeforeEventHandles == nil { + fsm.exitBeforeEventHandles = map[State][]func(state *FSM[State, Data]){} + } + fsm.exitBeforeEventHandles[state] = append(fsm.exitBeforeEventHandles[state], fn) + } +} + +// WithExitAfterEvent 设置状态退出后的回调 +// - 该阶段状态机的状态为新的状态,而非退出前的状态 +func WithExitAfterEvent[State comparable, Data any](fn func(state *FSM[State, Data])) Option[State, Data] { + return func(fsm *FSM[State, Data], state State) { + if fsm.exitAfterEventHandles == nil { + fsm.exitAfterEventHandles = map[State][]func(state *FSM[State, Data]){} + } + fsm.exitAfterEventHandles[state] = append(fsm.exitAfterEventHandles[state], fn) + } +} diff --git a/game/room/events.go b/game/room/events.go index dd4c134..8a27232 100644 --- a/game/room/events.go +++ b/game/room/events.go @@ -21,8 +21,8 @@ type ( PlayerSeatSetEventHandle[PID comparable, P game.Player[PID], R Room] func(room R, player P, seat int) // PlayerSeatCancelEventHandle 玩家座位取消事件处理函数 PlayerSeatCancelEventHandle[PID comparable, P game.Player[PID], R Room] func(room R, player P, seat int) - // RoomCreateEventHandle 房间创建事件处理函数 - RoomCreateEventHandle[PID comparable, P game.Player[PID], R Room] func(room R, helper *Helper[PID, P, R]) + // CreateEventHandle 房间创建事件处理函数 + CreateEventHandle[PID comparable, P game.Player[PID], R Room] func(room R, helper *Helper[PID, P, R]) ) func newEvent[PID comparable, P game.Player[PID], R Room]() *event[PID, P, R] { @@ -57,7 +57,7 @@ type event[PID comparable, P game.Player[PID], R Room] struct { playerSeatSetEventRoomHandles map[int64][]PlayerSeatSetEventHandle[PID, P, R] playerSeatCancelEventHandles []PlayerSeatCancelEventHandle[PID, P, R] playerSeatCancelEventRoomHandles map[int64][]PlayerSeatCancelEventHandle[PID, P, R] - roomCreateEventHandles []RoomCreateEventHandle[PID, P, R] + roomCreateEventHandles []CreateEventHandle[PID, P, R] } func (slf *event[PID, P, R]) unReg(guid int64) { @@ -254,7 +254,7 @@ func (slf *event[PID, P, R]) OnPlayerSeatCancelEvent(room R, player P, seat int) } // RegRoomCreateEvent 房间创建时将立即执行被注册的事件处理函数 -func (slf *event[PID, P, R]) RegRoomCreateEvent(handle RoomCreateEventHandle[PID, P, R]) { +func (slf *event[PID, P, R]) RegRoomCreateEvent(handle CreateEventHandle[PID, P, R]) { slf.roomCreateEventHandles = append(slf.roomCreateEventHandles, handle) }