Files
spacetris/src/core/state/StateManager.cpp
2025-12-25 09:38:06 +01:00

237 lines
8.1 KiB
C++

#include "StateManager.h"
#include "../../graphics/renderers/RenderManager.h"
#include <SDL3/SDL.h>
StateManager::StateManager(AppState initial)
: m_currentState(initial)
, m_previousState(initial)
{
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "StateManager initialized with state: %s", getStateName(initial));
}
void StateManager::registerEventHandler(AppState state, EventHandler handler) {
if (!isValidState(state)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for event handler registration");
return;
}
m_eventHandlers[stateToInt(state)].push_back(std::move(handler));
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Event handler registered for state: %s", getStateName(state));
}
void StateManager::registerUpdateHandler(AppState state, UpdateHandler handler) {
if (!isValidState(state)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for update handler registration");
return;
}
m_updateHandlers[stateToInt(state)].push_back(std::move(handler));
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Update handler registered for state: %s", getStateName(state));
}
void StateManager::registerRenderHandler(AppState state, RenderHandler handler) {
if (!isValidState(state)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for render handler registration");
return;
}
m_renderHandlers[stateToInt(state)].push_back(std::move(handler));
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Render handler registered for state: %s", getStateName(state));
}
void StateManager::registerOnEnter(AppState state, Hook hook) {
if (!isValidState(state)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for enter hook registration");
return;
}
m_onEnterHooks[stateToInt(state)].push_back(std::move(hook));
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Enter hook registered for state: %s", getStateName(state));
}
void StateManager::registerOnExit(AppState state, Hook hook) {
if (!isValidState(state)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for exit hook registration");
return;
}
m_onExitHooks[stateToInt(state)].push_back(std::move(hook));
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Exit hook registered for state: %s", getStateName(state));
}
// Legacy compatibility: overload accepting a no-arg function as handler
void StateManager::registerHandler(AppState s, std::function<void()> h) {
EventHandler wrapper = [h = std::move(h)](const SDL_Event&) { h(); };
registerEventHandler(s, std::move(wrapper));
}
bool StateManager::setState(AppState newState) {
if (!isValidState(newState)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Attempted to set invalid state");
return false;
}
if (newState == m_currentState) {
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Attempted to set same state: %s", getStateName(newState));
return true;
}
if (!canTransitionTo(newState)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Invalid state transition from %s to %s",
getStateName(m_currentState), getStateName(newState));
return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "State transition: %s -> %s",
getStateName(m_currentState), getStateName(newState));
// Persistent trace for debugging abrupt exits
{
FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "setState start %s -> %s\n", getStateName(m_currentState), getStateName(newState)); fclose(f); }
}
// Execute exit hooks for current state
executeExitHooks(m_currentState);
// Update state
m_previousState = m_currentState;
m_currentState = newState;
// Execute enter hooks for new state
executeEnterHooks(m_currentState);
// Trace completion
{
FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "setState end %s\n", getStateName(m_currentState)); fclose(f); }
}
return true;
}
void StateManager::handleEvent(const SDL_Event& event) {
auto it = m_eventHandlers.find(stateToInt(m_currentState));
if (it == m_eventHandlers.end()) {
return;
}
for (auto& handler : it->second) {
try {
handler(event);
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in event handler for state %s: %s",
getStateName(m_currentState), e.what());
}
}
}
void StateManager::update(float deltaTime) {
auto it = m_updateHandlers.find(stateToInt(m_currentState));
if (it == m_updateHandlers.end()) {
return;
}
for (auto& handler : it->second) {
try {
handler(deltaTime);
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in update handler for state %s: %s",
getStateName(m_currentState), e.what());
}
}
}
void StateManager::render(RenderManager& renderer) {
auto it = m_renderHandlers.find(stateToInt(m_currentState));
if (it == m_renderHandlers.end()) {
return;
}
for (auto& handler : it->second) {
try {
handler(renderer);
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in render handler for state %s: %s",
getStateName(m_currentState), e.what());
}
}
}
bool StateManager::isValidState(AppState state) const {
switch (state) {
case AppState::Loading:
case AppState::Video:
case AppState::Menu:
case AppState::Options:
case AppState::LevelSelector:
case AppState::Playing:
case AppState::LevelSelect:
case AppState::GameOver:
return true;
default:
return false;
}
}
bool StateManager::canTransitionTo(AppState newState) const {
// For now, allow all transitions. Can be enhanced later with state machine validation
return isValidState(newState);
}
const char* StateManager::getStateName(AppState state) const {
switch (state) {
case AppState::Loading: return "Loading";
case AppState::Video: return "Video";
case AppState::Menu: return "Menu";
case AppState::Options: return "Options";
case AppState::LevelSelector: return "LevelSelector";
case AppState::Playing: return "Playing";
case AppState::LevelSelect: return "LevelSelect";
case AppState::GameOver: return "GameOver";
default: return "Unknown";
}
}
void StateManager::executeEnterHooks(AppState state) {
auto it = m_onEnterHooks.find(stateToInt(state));
if (it == m_onEnterHooks.end()) {
return;
}
int idx = 0;
for (auto& hook : it->second) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Executing enter hook %d for state %s", idx, getStateName(state));
// Also write to trace file for persistent record
{
FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "executeEnterHook %d %s\n", idx, getStateName(state)); fclose(f); }
}
try {
hook();
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in enter hook for state %s: %s",
getStateName(state), e.what());
}
++idx;
}
}
void StateManager::executeExitHooks(AppState state) {
auto it = m_onExitHooks.find(stateToInt(state));
if (it == m_onExitHooks.end()) {
return;
}
int idx = 0;
for (auto& hook : it->second) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Executing exit hook %d for state %s", idx, getStateName(state));
{
FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "executeExitHook %d %s\n", idx, getStateName(state)); fclose(f); }
}
try {
hook();
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in exit hook for state %s: %s",
getStateName(state), e.what());
}
++idx;
}
}