refactor(module): 重构模块系统并添加核心模块实现

重构模块注册表结构以支持初始化器实例缓存,添加配置、日志、平台和渲染等核心模块实现
优化日志系统移除SDL依赖,改进跨平台支持
调整模块初始化流程,增强模块间依赖管理
This commit is contained in:
ChestnutYueyue 2026-02-15 10:08:44 +08:00
parent 38148a6c54
commit 8f7f1612eb
17 changed files with 1257 additions and 1471 deletions

View File

@ -4,8 +4,6 @@
#include <extra2d/config/app_config.h> #include <extra2d/config/app_config.h>
#include <extra2d/config/config_manager.h> #include <extra2d/config/config_manager.h>
#include <extra2d/config/module_config.h> #include <extra2d/config/module_config.h>
#include <extra2d/config/platform_config.h>
#include <extra2d/graphics/render_backend.h>
#include <extra2d/platform/iwindow.h> #include <extra2d/platform/iwindow.h>
#include <string> #include <string>
@ -18,198 +16,58 @@ class EventQueue;
class EventDispatcher; class EventDispatcher;
class Camera; class Camera;
class ViewportAdapter; class ViewportAdapter;
class RenderBackend;
/**
* @brief Application -
*
*
* ConfigManager ModuleRegistry
*/
class Application { class Application {
public: public:
/**
* @brief
* @return Application
*/
static Application& get(); static Application& get();
Application(const Application&) = delete; Application(const Application&) = delete;
Application& operator=(const Application&) = delete; Application& operator=(const Application&) = delete;
/**
* @brief 使
* @return true
*/
bool init(); bool init();
/**
* @brief 使
* @param config
* @return true
*/
bool init(const AppConfig& config); bool init(const AppConfig& config);
/**
* @brief
* @param configPath .json .ini
* @return true
*/
bool init(const std::string& configPath); bool init(const std::string& configPath);
/**
* @brief
*/
void shutdown(); void shutdown();
/**
* @brief
*/
void run(); void run();
/**
* @brief 退
*/
void quit(); void quit();
/**
* @brief
*/
void pause(); void pause();
/**
* @brief
*/
void resume(); void resume();
/**
* @brief
* @return true
*/
bool isPaused() const { return paused_; } bool isPaused() const { return paused_; }
/**
* @brief
* @return true
*/
bool isRunning() const { return running_; } bool isRunning() const { return running_; }
/**
* @brief
* @return
*/
IWindow& window() { return *window_; } IWindow& window() { return *window_; }
RenderBackend& renderer();
/**
* @brief
* @return
*/
RenderBackend& renderer() { return *renderer_; }
/**
* @brief
* @return
*/
IInput& input(); IInput& input();
/**
* @brief
* @return
*/
SceneManager& scenes(); SceneManager& scenes();
/**
* @brief
* @return
*/
TimerManager& timers(); TimerManager& timers();
/**
* @brief
* @return
*/
EventQueue& eventQueue(); EventQueue& eventQueue();
/**
* @brief
* @return
*/
EventDispatcher& eventDispatcher(); EventDispatcher& eventDispatcher();
/**
* @brief
* @return
*/
Camera& camera(); Camera& camera();
/**
* @brief
* @return
*/
ViewportAdapter& viewportAdapter(); ViewportAdapter& viewportAdapter();
/**
* @brief
* @param scene
*/
void enterScene(Ptr<class Scene> scene); void enterScene(Ptr<class Scene> scene);
/**
* @brief
* @return
*/
float deltaTime() const { return deltaTime_; } float deltaTime() const { return deltaTime_; }
/**
* @brief
* @return
*/
float totalTime() const { return totalTime_; } float totalTime() const { return totalTime_; }
/**
* @brief
* @return FPS
*/
int fps() const { return currentFps_; } int fps() const { return currentFps_; }
/**
* @brief
* @return
*/
ConfigManager& config(); ConfigManager& config();
/**
* @brief
* @return
*/
const AppConfig& getConfig() const; const AppConfig& getConfig() const;
private: private:
Application() = default; Application() = default;
~Application(); ~Application();
/** bool initModules();
* @brief
* @return true
*/
bool initImpl();
/**
* @brief
*/
void mainLoop(); void mainLoop();
/**
* @brief
*/
void update(); void update();
/**
* @brief
*/
void render(); void render();
UniquePtr<IWindow> window_; IWindow* window_ = nullptr;
UniquePtr<RenderBackend> renderer_;
UniquePtr<SceneManager> sceneManager_; UniquePtr<SceneManager> sceneManager_;
UniquePtr<TimerManager> timerManager_; UniquePtr<TimerManager> timerManager_;
UniquePtr<EventQueue> eventQueue_; UniquePtr<EventQueue> eventQueue_;
@ -228,10 +86,6 @@ private:
int frameCount_ = 0; int frameCount_ = 0;
float fpsTimer_ = 0.0f; float fpsTimer_ = 0.0f;
int currentFps_ = 0; int currentFps_ = 0;
ModuleId windowModuleId_ = INVALID_MODULE_ID;
ModuleId inputModuleId_ = INVALID_MODULE_ID;
ModuleId renderModuleId_ = INVALID_MODULE_ID;
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -0,0 +1,70 @@
#pragma once
#include <extra2d/config/module_config.h>
#include <extra2d/config/module_initializer.h>
#include <extra2d/config/app_config.h>
#include <extra2d/config/config_manager.h>
#include <string>
namespace extra2d {
class ConfigModuleConfig : public IModuleConfig {
public:
std::string configPath;
AppConfig appConfig;
ModuleInfo getModuleInfo() const override {
ModuleInfo info;
info.id = 0;
info.name = "Config";
info.version = "1.0.0";
info.priority = ModulePriority::Core;
info.enabled = true;
return info;
}
std::string getConfigSectionName() const override {
return "config";
}
bool validate() const override {
return true;
}
void resetToDefaults() override {
configPath.clear();
appConfig = AppConfig{};
}
bool loadFromJson(const void* jsonData) override;
bool saveToJson(void* jsonData) const override;
};
class ConfigModuleInitializer : public IModuleInitializer {
public:
ConfigModuleInitializer();
~ConfigModuleInitializer() override;
ModuleId getModuleId() const override { return moduleId_; }
ModulePriority getPriority() const override { return ModulePriority::Core; }
std::vector<ModuleId> getDependencies() const override { return {}; }
bool initialize(const IModuleConfig* config) override;
void shutdown() override;
bool isInitialized() const override { return initialized_; }
void setModuleId(ModuleId id) { moduleId_ = id; }
void setAppConfig(const AppConfig& config) { appConfig_ = config; }
void setConfigPath(const std::string& path) { configPath_ = path; }
private:
ModuleId moduleId_ = INVALID_MODULE_ID;
bool initialized_ = false;
AppConfig appConfig_;
std::string configPath_;
};
ModuleId get_config_module_id();
void register_config_module();
} // namespace extra2d

View File

@ -10,12 +10,13 @@ namespace extra2d {
/** /**
* @brief * @brief
* *
*/ */
struct ModuleEntry { struct ModuleEntry {
ModuleId id; ///< 模块标识符 ModuleId id; ///< 模块标识符
UniquePtr<IModuleConfig> config; ///< 模块配置 UniquePtr<IModuleConfig> config; ///< 模块配置
ModuleInitializerFactory initializerFactory;///< 初始化器工厂函数 ModuleInitializerFactory initializerFactory;///< 初始化器工厂函数
UniquePtr<IModuleInitializer> initializer; ///< 初始化器实例
bool initialized = false; ///< 是否已初始化 bool initialized = false; ///< 是否已初始化
}; };
@ -74,11 +75,11 @@ public:
IModuleConfig* getModuleConfigByName(const std::string& name) const; IModuleConfig* getModuleConfigByName(const std::string& name) const;
/** /**
* @brief * @brief
* @param id * @param id
* @return nullptr * @return nullptr
*/ */
UniquePtr<IModuleInitializer> createInitializer(ModuleId id) const; IModuleInitializer* getInitializer(ModuleId id);
/** /**
* @brief * @brief

View File

@ -1,212 +0,0 @@
#pragma once
// SDL2 键码定义
#include <SDL2/SDL.h>
namespace extra2d {
// ============================================================================
// 键盘按键码 (基于 SDL2)
// ============================================================================
namespace Key {
enum : int {
Unknown = SDLK_UNKNOWN,
Space = SDLK_SPACE,
Apostrophe = SDLK_QUOTE,
Comma = SDLK_COMMA,
Minus = SDLK_MINUS,
Period = SDLK_PERIOD,
Slash = SDLK_SLASH,
Num0 = SDLK_0,
Num1 = SDLK_1,
Num2 = SDLK_2,
Num3 = SDLK_3,
Num4 = SDLK_4,
Num5 = SDLK_5,
Num6 = SDLK_6,
Num7 = SDLK_7,
Num8 = SDLK_8,
Num9 = SDLK_9,
Semicolon = SDLK_SEMICOLON,
Equal = SDLK_EQUALS,
A = SDLK_a,
B = SDLK_b,
C = SDLK_c,
D = SDLK_d,
E = SDLK_e,
F = SDLK_f,
G = SDLK_g,
H = SDLK_h,
I = SDLK_i,
J = SDLK_j,
K = SDLK_k,
L = SDLK_l,
M = SDLK_m,
N = SDLK_n,
O = SDLK_o,
P = SDLK_p,
Q = SDLK_q,
R = SDLK_r,
S = SDLK_s,
T = SDLK_t,
U = SDLK_u,
V = SDLK_v,
W = SDLK_w,
X = SDLK_x,
Y = SDLK_y,
Z = SDLK_z,
LeftBracket = SDLK_LEFTBRACKET,
Backslash = SDLK_BACKSLASH,
RightBracket = SDLK_RIGHTBRACKET,
GraveAccent = SDLK_BACKQUOTE,
Escape = SDLK_ESCAPE,
Enter = SDLK_RETURN,
Tab = SDLK_TAB,
Backspace = SDLK_BACKSPACE,
Insert = SDLK_INSERT,
Delete = SDLK_DELETE,
Right = SDLK_RIGHT,
Left = SDLK_LEFT,
Down = SDLK_DOWN,
Up = SDLK_UP,
PageUp = SDLK_PAGEUP,
PageDown = SDLK_PAGEDOWN,
Home = SDLK_HOME,
End = SDLK_END,
CapsLock = SDLK_CAPSLOCK,
ScrollLock = SDLK_SCROLLLOCK,
NumLock = SDLK_NUMLOCKCLEAR,
PrintScreen = SDLK_PRINTSCREEN,
Pause = SDLK_PAUSE,
F1 = SDLK_F1,
F2 = SDLK_F2,
F3 = SDLK_F3,
F4 = SDLK_F4,
F5 = SDLK_F5,
F6 = SDLK_F6,
F7 = SDLK_F7,
F8 = SDLK_F8,
F9 = SDLK_F9,
F10 = SDLK_F10,
F11 = SDLK_F11,
F12 = SDLK_F12,
F13 = SDLK_F13,
F14 = SDLK_F14,
F15 = SDLK_F15,
F16 = SDLK_F16,
F17 = SDLK_F17,
F18 = SDLK_F18,
F19 = SDLK_F19,
F20 = SDLK_F20,
F21 = SDLK_F21,
F22 = SDLK_F22,
F23 = SDLK_F23,
F24 = SDLK_F24,
KP0 = SDLK_KP_0,
KP1 = SDLK_KP_1,
KP2 = SDLK_KP_2,
KP3 = SDLK_KP_3,
KP4 = SDLK_KP_4,
KP5 = SDLK_KP_5,
KP6 = SDLK_KP_6,
KP7 = SDLK_KP_7,
KP8 = SDLK_KP_8,
KP9 = SDLK_KP_9,
KPDecimal = SDLK_KP_PERIOD,
KPDivide = SDLK_KP_DIVIDE,
KPMultiply = SDLK_KP_MULTIPLY,
KPSubtract = SDLK_KP_MINUS,
KPAdd = SDLK_KP_PLUS,
KPEnter = SDLK_KP_ENTER,
KPEqual = SDLK_KP_EQUALS,
LeftShift = SDLK_LSHIFT,
LeftControl = SDLK_LCTRL,
LeftAlt = SDLK_LALT,
LeftSuper = SDLK_LGUI,
RightShift = SDLK_RSHIFT,
RightControl = SDLK_RCTRL,
RightAlt = SDLK_RALT,
RightSuper = SDLK_RGUI,
Menu = SDLK_MENU,
Last = SDLK_MENU
};
}
// ============================================================================
// 修饰键
// ============================================================================
namespace Mod {
enum : int {
Shift = KMOD_SHIFT,
Control = KMOD_CTRL,
Alt = KMOD_ALT,
Super = KMOD_GUI,
CapsLock = KMOD_CAPS,
NumLock = KMOD_NUM
};
}
// ============================================================================
// 鼠标按键码
// ============================================================================
namespace Mouse {
enum : int {
Button1 = 0,
Button2 = 1,
Button3 = 2,
Button4 = 3,
Button5 = 4,
Button6 = 5,
Button7 = 6,
Button8 = 7,
ButtonLast = Button8,
ButtonLeft = Button1,
ButtonRight = Button2,
ButtonMiddle = Button3
};
}
// ============================================================================
// 游戏手柄按键
// ============================================================================
namespace GamepadButton {
enum : int {
A = SDL_CONTROLLER_BUTTON_A,
B = SDL_CONTROLLER_BUTTON_B,
X = SDL_CONTROLLER_BUTTON_X,
Y = SDL_CONTROLLER_BUTTON_Y,
LeftBumper = SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
RightBumper = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
Back = SDL_CONTROLLER_BUTTON_BACK,
Start = SDL_CONTROLLER_BUTTON_START,
Guide = SDL_CONTROLLER_BUTTON_GUIDE,
LeftThumb = SDL_CONTROLLER_BUTTON_LEFTSTICK,
RightThumb = SDL_CONTROLLER_BUTTON_RIGHTSTICK,
DPadUp = SDL_CONTROLLER_BUTTON_DPAD_UP,
DPadRight = SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
DPadDown = SDL_CONTROLLER_BUTTON_DPAD_DOWN,
DPadLeft = SDL_CONTROLLER_BUTTON_DPAD_LEFT,
Last = SDL_CONTROLLER_BUTTON_DPAD_LEFT,
Cross = A,
Circle = B,
Square = X,
Triangle = Y
};
}
// ============================================================================
// 游戏手柄轴
// ============================================================================
namespace GamepadAxis {
enum : int {
LeftX = SDL_CONTROLLER_AXIS_LEFTX,
LeftY = SDL_CONTROLLER_AXIS_LEFTY,
RightX = SDL_CONTROLLER_AXIS_RIGHTX,
RightY = SDL_CONTROLLER_AXIS_RIGHTY,
LeftTrigger = SDL_CONTROLLER_AXIS_TRIGGERLEFT,
RightTrigger = SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
Last = SDL_CONTROLLER_AXIS_TRIGGERRIGHT
};
}
} // namespace extra2d

View File

@ -7,10 +7,6 @@
namespace extra2d { namespace extra2d {
/**
* @brief
* IModuleConfig
*/
class RenderModuleConfig : public IModuleConfig { class RenderModuleConfig : public IModuleConfig {
public: public:
BackendType backend = BackendType::OpenGL; BackendType backend = BackendType::OpenGL;
@ -20,10 +16,6 @@ public:
bool sRGBFramebuffer = false; bool sRGBFramebuffer = false;
int spriteBatchSize = 1000; int spriteBatchSize = 1000;
/**
* @brief
* @return
*/
ModuleInfo getModuleInfo() const override { ModuleInfo getModuleInfo() const override {
ModuleInfo info; ModuleInfo info;
info.name = "Render"; info.name = "Render";
@ -33,115 +25,41 @@ public:
return info; return info;
} }
/**
* @brief
* @return
*/
std::string getConfigSectionName() const override { return "render"; } std::string getConfigSectionName() const override { return "render"; }
/**
* @brief
* @return true
*/
bool validate() const override; bool validate() const override;
/**
* @brief
*
* @param platform
*/
void applyPlatformConstraints(PlatformType platform) override; void applyPlatformConstraints(PlatformType platform) override;
/**
* @brief
*/
void resetToDefaults() override; void resetToDefaults() override;
/**
* @brief JSON
* @param jsonData JSON
* @return true
*/
bool loadFromJson(const void* jsonData) override; bool loadFromJson(const void* jsonData) override;
/**
* @brief JSON
* @param jsonData JSON
* @return true
*/
bool saveToJson(void* jsonData) const override; bool saveToJson(void* jsonData) const override;
}; };
/**
* @brief
* IModuleInitializer
*
*/
class RenderModuleInitializer : public IModuleInitializer { class RenderModuleInitializer : public IModuleInitializer {
public: public:
/**
* @brief
*/
RenderModuleInitializer(); RenderModuleInitializer();
/**
* @brief
*/
~RenderModuleInitializer() override; ~RenderModuleInitializer() override;
/**
* @brief
* @return
*/
ModuleId getModuleId() const override { return moduleId_; } ModuleId getModuleId() const override { return moduleId_; }
/**
* @brief
* @return
*/
ModulePriority getPriority() const override { return ModulePriority::Graphics; } ModulePriority getPriority() const override { return ModulePriority::Graphics; }
/**
* @brief
* @return
*/
std::vector<ModuleId> getDependencies() const override; std::vector<ModuleId> getDependencies() const override;
/**
* @brief
* @param config
* @return true
*/
bool initialize(const IModuleConfig* config) override; bool initialize(const IModuleConfig* config) override;
/**
* @brief
*/
void shutdown() override; void shutdown() override;
/**
* @brief
* @return true
*/
bool isInitialized() const override { return initialized_; } bool isInitialized() const override { return initialized_; }
/** void setModuleId(ModuleId id) { moduleId_ = id; }
* @brief void setWindow(IWindow* window) { window_ = window; }
* @return
*/
RenderBackend* getRenderer() const { return renderer_.get(); }
/** RenderBackend* getRenderer() const { return renderer_.get(); }
* @brief
* @param windowModuleId
*/
void setWindowModuleId(ModuleId windowModuleId) { windowModuleId_ = windowModuleId; }
private: private:
ModuleId moduleId_ = INVALID_MODULE_ID; ModuleId moduleId_ = INVALID_MODULE_ID;
ModuleId windowModuleId_ = INVALID_MODULE_ID; IWindow* window_ = nullptr;
UniquePtr<RenderBackend> renderer_; UniquePtr<RenderBackend> renderer_;
bool initialized_ = false; bool initialized_ = false;
}; };
ModuleId get_render_module_id();
void register_render_module();
} // namespace extra2d } // namespace extra2d

View File

@ -0,0 +1,72 @@
#pragma once
#include <extra2d/config/module_config.h>
#include <extra2d/config/module_initializer.h>
#include <extra2d/config/platform_config.h>
namespace extra2d {
class PlatformModuleConfig : public IModuleConfig {
public:
PlatformType targetPlatform = PlatformType::Auto;
ModuleInfo getModuleInfo() const override {
ModuleInfo info;
info.id = 0;
info.name = "Platform";
info.version = "1.0.0";
info.priority = ModulePriority::Core;
info.enabled = true;
return info;
}
std::string getConfigSectionName() const override {
return "platform";
}
bool validate() const override {
return true;
}
void resetToDefaults() override {
targetPlatform = PlatformType::Auto;
}
bool loadFromJson(const void* jsonData) override;
bool saveToJson(void* jsonData) const override;
};
class PlatformModuleInitializer : public IModuleInitializer {
public:
PlatformModuleInitializer();
~PlatformModuleInitializer() override;
ModuleId getModuleId() const override { return moduleId_; }
ModulePriority getPriority() const override { return ModulePriority::Core; }
std::vector<ModuleId> getDependencies() const override { return {}; }
bool initialize(const IModuleConfig* config) override;
void shutdown() override;
bool isInitialized() const override { return initialized_; }
void setModuleId(ModuleId id) { moduleId_ = id; }
void setPlatform(PlatformType platform) { targetPlatform_ = platform; }
PlatformType getPlatform() const { return resolvedPlatform_; }
PlatformConfig* getPlatformConfig() const { return platformConfig_.get(); }
private:
bool initSwitch();
void shutdownSwitch();
ModuleId moduleId_ = INVALID_MODULE_ID;
bool initialized_ = false;
PlatformType targetPlatform_ = PlatformType::Auto;
PlatformType resolvedPlatform_ = PlatformType::Windows;
UniquePtr<PlatformConfig> platformConfig_;
};
ModuleId get_platform_module_id();
void register_platform_module();
} // namespace extra2d

View File

@ -4,133 +4,74 @@
#include <extra2d/config/module_initializer.h> #include <extra2d/config/module_initializer.h>
#include <extra2d/config/app_config.h> #include <extra2d/config/app_config.h>
#include <extra2d/platform/iwindow.h> #include <extra2d/platform/iwindow.h>
#include <extra2d/core/types.h> #include <string>
namespace extra2d { namespace extra2d {
/**
* @brief
* IModuleConfig
*/
class WindowModuleConfig : public IModuleConfig { class WindowModuleConfig : public IModuleConfig {
public: public:
WindowConfigData windowConfig;
std::string backend = "sdl2"; std::string backend = "sdl2";
WindowConfigData windowConfig;
/**
* @brief
* @return
*/
ModuleInfo getModuleInfo() const override { ModuleInfo getModuleInfo() const override {
ModuleInfo info; ModuleInfo info;
info.id = 0;
info.name = "Window"; info.name = "Window";
info.version = "1.0.0"; info.version = "1.0.0";
info.priority = ModulePriority::Platform; info.priority = ModulePriority::Core;
info.enabled = true; info.enabled = true;
return info; return info;
} }
/** std::string getConfigSectionName() const override {
* @brief return "window";
* @return }
*/
std::string getConfigSectionName() const override { return "window"; }
/** bool validate() const override {
* @brief return windowConfig.width > 0 && windowConfig.height > 0;
* @return true }
*/
bool validate() const override;
/** void resetToDefaults() override {
* @brief backend = "sdl2";
* windowConfig = WindowConfigData{};
* @param platform }
*/
void applyPlatformConstraints(PlatformType platform) override;
/**
* @brief
*/
void resetToDefaults() override;
/**
* @brief JSON
* @param jsonData JSON
* @return true
*/
bool loadFromJson(const void* jsonData) override; bool loadFromJson(const void* jsonData) override;
/**
* @brief JSON
* @param jsonData JSON
* @return true
*/
bool saveToJson(void* jsonData) const override; bool saveToJson(void* jsonData) const override;
}; };
/**
* @brief
* IModuleInitializer
*/
class WindowModuleInitializer : public IModuleInitializer { class WindowModuleInitializer : public IModuleInitializer {
public: public:
/**
* @brief
*/
WindowModuleInitializer(); WindowModuleInitializer();
/**
* @brief
*/
~WindowModuleInitializer() override; ~WindowModuleInitializer() override;
/**
* @brief
* @return
*/
ModuleId getModuleId() const override { return moduleId_; } ModuleId getModuleId() const override { return moduleId_; }
ModulePriority getPriority() const override { return ModulePriority::Core; }
/**
* @brief
* @return
*/
ModulePriority getPriority() const override { return ModulePriority::Platform; }
/**
* @brief
* @return
*/
std::vector<ModuleId> getDependencies() const override { return {}; } std::vector<ModuleId> getDependencies() const override { return {}; }
/**
* @brief
* @param config
* @return true
*/
bool initialize(const IModuleConfig* config) override; bool initialize(const IModuleConfig* config) override;
/**
* @brief
*/
void shutdown() override; void shutdown() override;
/**
* @brief
* @return true
*/
bool isInitialized() const override { return initialized_; } bool isInitialized() const override { return initialized_; }
/** void setModuleId(ModuleId id) { moduleId_ = id; }
* @brief void setWindowConfig(const WindowConfigData& config) { windowConfig_ = config; }
* @return
*/
IWindow* getWindow() const { return window_.get(); } IWindow* getWindow() const { return window_.get(); }
private: private:
bool initBackend();
bool createWindow(const std::string& backend, const WindowConfigData& config);
void shutdownBackend();
ModuleId moduleId_ = INVALID_MODULE_ID; ModuleId moduleId_ = INVALID_MODULE_ID;
UniquePtr<IWindow> window_;
bool initialized_ = false; bool initialized_ = false;
bool backendInitialized_ = false;
std::string backend_;
WindowConfigData windowConfig_;
UniquePtr<IWindow> window_;
}; };
ModuleId get_window_module_id();
void register_window_module();
} // namespace extra2d } // namespace extra2d

View File

@ -6,254 +6,186 @@
#include <string> #include <string>
#include <type_traits> #include <type_traits>
// SDL2 日志头文件
#include <SDL.h>
namespace extra2d { namespace extra2d {
// ============================================================================
// 日志级别枚举 - 映射到 SDL_LogPriority
// ============================================================================
enum class LogLevel { enum class LogLevel {
Trace = SDL_LOG_PRIORITY_VERBOSE, // SDL 详细日志 Trace = 0,
Debug = SDL_LOG_PRIORITY_DEBUG, // SDL 调试日志 Debug = 1,
Info = SDL_LOG_PRIORITY_INFO, // SDL 信息日志 Info = 2,
Warn = SDL_LOG_PRIORITY_WARN, // SDL 警告日志 Warn = 3,
Error = SDL_LOG_PRIORITY_ERROR, // SDL 错误日志 Error = 4,
Fatal = SDL_LOG_PRIORITY_CRITICAL, // SDL 严重日志 Fatal = 5,
Off = SDL_LOG_PRIORITY_CRITICAL + 1 // 关闭日志 (使用 Critical+1 作为关闭标记) Off = 6
}; };
// ============================================================================
// 简单的 fmt-style {} 格式化器
// ============================================================================
namespace detail { namespace detail {
// 将单个参数转为字符串
template <typename T> inline std::string to_string_arg(const T &value) { template <typename T> inline std::string to_string_arg(const T &value) {
if constexpr (std::is_same_v<T, std::string>) { if constexpr (std::is_same_v<T, std::string>) {
return value; return value;
} else if constexpr (std::is_same_v<T, const char *> || } else if constexpr (std::is_same_v<T, const char *> ||
std::is_same_v<T, char *>) { std::is_same_v<T, char *>) {
return value ? std::string(value) : std::string("(null)"); return value ? std::string(value) : std::string("(null)");
} else if constexpr (std::is_same_v<T, bool>) { } else if constexpr (std::is_same_v<T, bool>) {
return value ? "true" : "false"; return value ? "true" : "false";
} else if constexpr (std::is_arithmetic_v<T>) { } else if constexpr (std::is_arithmetic_v<T>) {
// 对浮点数使用特殊格式 if constexpr (std::is_floating_point_v<T>) {
if constexpr (std::is_floating_point_v<T>) { char buf[64];
char buf[64]; snprintf(buf, sizeof(buf), "%.2f", static_cast<double>(value));
snprintf(buf, sizeof(buf), "%.2f", static_cast<double>(value)); return buf;
return buf; } else {
return std::to_string(value);
}
} else { } else {
return std::to_string(value); std::ostringstream oss;
oss << value;
return oss.str();
} }
} else {
std::ostringstream oss;
oss << value;
return oss.str();
}
} }
// 格式化基础情况:没有更多参数
inline std::string format_impl(const char *fmt) { inline std::string format_impl(const char *fmt) {
std::string result; std::string result;
while (*fmt) { while (*fmt) {
if (*fmt == '{' && *(fmt + 1) == '}') { if (*fmt == '{' && *(fmt + 1) == '}') {
result += "{}"; // 无参数可替换,保留原样 result += "{}";
fmt += 2; fmt += 2;
} else { } else {
result += *fmt; result += *fmt;
++fmt; ++fmt;
}
} }
} return result;
return result;
} }
// 格式化递归:替换第一个 {} 并递归处理剩余
template <typename T, typename... Args> template <typename T, typename... Args>
inline std::string format_impl(const char *fmt, const T &first, inline std::string format_impl(const char *fmt, const T &first,
const Args &...rest) { const Args &...rest) {
std::string result; std::string result;
while (*fmt) { while (*fmt) {
if (*fmt == '{') { if (*fmt == '{') {
// 检查 {:#x} 等格式说明符 if (*(fmt + 1) == '}') {
if (*(fmt + 1) == '}') { result += to_string_arg(first);
result += to_string_arg(first); fmt += 2;
fmt += 2; result += format_impl(fmt, rest...);
result += format_impl(fmt, rest...); return result;
return result; } else if (*(fmt + 1) == ':') {
} else if (*(fmt + 1) == ':') { const char *end = fmt + 2;
// 跳过格式说明符直到 } while (*end && *end != '}')
const char *end = fmt + 2; ++end;
while (*end && *end != '}') if (*end == '}') {
++end; std::string spec(fmt + 2, end);
if (*end == '}') { if (spec.find('x') != std::string::npos ||
// 检查是否是十六进制格式 spec.find('X') != std::string::npos) {
std::string spec(fmt + 2, end); if constexpr (std::is_integral_v<T>) {
if (spec.find('x') != std::string::npos || char buf[32];
spec.find('X') != std::string::npos) { snprintf(buf, sizeof(buf), "0x%x",
if constexpr (std::is_integral_v<T>) { static_cast<unsigned int>(first));
char buf[32]; result += buf;
snprintf(buf, sizeof(buf), "0x%x", } else {
static_cast<unsigned int>(first)); result += to_string_arg(first);
result += buf; }
} else { } else if (spec.find('f') != std::string::npos ||
result += to_string_arg(first); spec.find('.') != std::string::npos) {
} if constexpr (std::is_arithmetic_v<T>) {
} else if (spec.find('f') != std::string::npos || int precision = 2;
spec.find('.') != std::string::npos) { auto dot = spec.find('.');
if constexpr (std::is_arithmetic_v<T>) { if (dot != std::string::npos) {
// 解析精度 precision = 0;
int precision = 2; for (size_t i = dot + 1;
auto dot = spec.find('.'); i < spec.size() && spec[i] >= '0' && spec[i] <= '9'; ++i) {
if (dot != std::string::npos) { precision = precision * 10 + (spec[i] - '0');
precision = 0; }
for (size_t i = dot + 1; }
i < spec.size() && spec[i] >= '0' && spec[i] <= '9'; ++i) { char fmtbuf[16];
precision = precision * 10 + (spec[i] - '0'); snprintf(fmtbuf, sizeof(fmtbuf), "%%.%df", precision);
char buf[64];
snprintf(buf, sizeof(buf), fmtbuf, static_cast<double>(first));
result += buf;
} else {
result += to_string_arg(first);
}
} else {
result += to_string_arg(first);
}
fmt = end + 1;
result += format_impl(fmt, rest...);
return result;
} }
}
char fmtbuf[16];
snprintf(fmtbuf, sizeof(fmtbuf), "%%.%df", precision);
char buf[64];
snprintf(buf, sizeof(buf), fmtbuf, static_cast<double>(first));
result += buf;
} else {
result += to_string_arg(first);
} }
} else {
result += to_string_arg(first);
}
fmt = end + 1;
result += format_impl(fmt, rest...);
return result;
} }
} result += *fmt;
++fmt;
} }
result += *fmt; return result;
++fmt;
}
return result;
} }
} // namespace detail } // namespace detail
// 顶层格式化函数
template <typename... Args> template <typename... Args>
inline std::string e2d_format(const char *fmt, const Args &...args) { inline std::string e2d_format(const char *fmt, const Args &...args) {
return detail::format_impl(fmt, args...); return detail::format_impl(fmt, args...);
} }
// 无参数版本
inline std::string e2d_format(const char *fmt) { return std::string(fmt); } inline std::string e2d_format(const char *fmt) { return std::string(fmt); }
// ============================================================================
// Logger 类 - 使用 SDL2 日志系统
// ============================================================================
class Logger { class Logger {
public: public:
/** static void init();
* @brief static void shutdown();
*/
static void init();
/** static void setLevel(LogLevel level);
* @brief static void setConsoleOutput(bool enable);
*/ static void setFileOutput(const std::string &filename);
static void shutdown();
/** static LogLevel getLevel() { return level_; }
* @brief
* @param level
*/
static void setLevel(LogLevel level);
/** template <typename... Args>
* @brief static void log(LogLevel level, const char *fmt, const Args &...args) {
* @param enable if (static_cast<int>(level) < static_cast<int>(level_))
*/ return;
static void setConsoleOutput(bool enable); std::string msg = e2d_format(fmt, args...);
outputLog(level, msg.c_str());
}
/** static void log(LogLevel level, const char *msg) {
* @brief if (static_cast<int>(level) < static_cast<int>(level_))
* @param filename return;
*/ outputLog(level, msg);
static void setFileOutput(const std::string &filename); }
/**
* @brief
* @return
*/
static LogLevel getLevel() { return level_; }
/**
* @brief
* @param level
* @param fmt
* @param args
*/
template <typename... Args>
static void log(LogLevel level, const char *fmt, const Args &...args) {
if (static_cast<int>(level) < static_cast<int>(level_))
return;
std::string msg = e2d_format(fmt, args...);
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level), "[%s] %s",
getLevelString(level), msg.c_str());
}
/**
* @brief
* @param level
* @param msg
*/
static void log(LogLevel level, const char *msg) {
if (static_cast<int>(level) < static_cast<int>(level_))
return;
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level), "[%s] %s",
getLevelString(level), msg);
}
private: private:
static LogLevel level_; // 当前日志级别 static void outputLog(LogLevel level, const char *msg);
static bool initialized_; // 是否已初始化 static const char *getLevelString(LogLevel level);
static bool consoleOutput_; // 是否输出到控制台 static void writeToConsole(LogLevel level, const char *msg);
static bool fileOutput_; // 是否输出到文件 static void writeToFile(LogLevel level, const char *msg);
static std::string logFile_; // 日志文件路径
/** static LogLevel level_;
* @brief static bool initialized_;
* @param level static bool consoleOutput_;
* @return static bool fileOutput_;
*/ static std::string logFile_;
static const char *getLevelString(LogLevel level); static void *logFileHandle_;
}; };
// ============================================================================
// 日志宏
// ============================================================================
#ifdef E2D_DEBUG #ifdef E2D_DEBUG
#define E2D_LOG_TRACE(...) \ #define E2D_LOG_TRACE(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Trace, __VA_ARGS__) ::extra2d::Logger::log(::extra2d::LogLevel::Trace, __VA_ARGS__)
#define E2D_LOG_DEBUG(...) \ #define E2D_LOG_DEBUG(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Debug, __VA_ARGS__) ::extra2d::Logger::log(::extra2d::LogLevel::Debug, __VA_ARGS__)
#else #else
#define E2D_LOG_TRACE(...) #define E2D_LOG_TRACE(...)
#define E2D_LOG_DEBUG(...) #define E2D_LOG_DEBUG(...)
#endif #endif
#define E2D_LOG_INFO(...) \ #define E2D_LOG_INFO(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Info, __VA_ARGS__) ::extra2d::Logger::log(::extra2d::LogLevel::Info, __VA_ARGS__)
#define E2D_LOG_WARN(...) \ #define E2D_LOG_WARN(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Warn, __VA_ARGS__) ::extra2d::Logger::log(::extra2d::LogLevel::Warn, __VA_ARGS__)
#define E2D_LOG_ERROR(...) \ #define E2D_LOG_ERROR(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Error, __VA_ARGS__) ::extra2d::Logger::log(::extra2d::LogLevel::Error, __VA_ARGS__)
#define E2D_LOG_FATAL(...) \ #define E2D_LOG_FATAL(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Fatal, __VA_ARGS__) ::extra2d::Logger::log(::extra2d::LogLevel::Fatal, __VA_ARGS__)
// 简化的日志宏
#define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__) #define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__)
#define E2D_WARN(...) E2D_LOG_WARN(__VA_ARGS__) #define E2D_WARN(...) E2D_LOG_WARN(__VA_ARGS__)
#define E2D_ERROR(...) E2D_LOG_ERROR(__VA_ARGS__) #define E2D_ERROR(...) E2D_LOG_ERROR(__VA_ARGS__)

View File

@ -0,0 +1,66 @@
#pragma once
#include <extra2d/config/module_config.h>
#include <extra2d/config/module_initializer.h>
#include <extra2d/utils/logger.h>
#include <string>
namespace extra2d {
class LoggerModuleConfig : public IModuleConfig {
public:
LogLevel logLevel = LogLevel::Info;
bool consoleOutput = true;
bool fileOutput = false;
std::string logFilePath;
ModuleInfo getModuleInfo() const override {
ModuleInfo info;
info.id = 0;
info.name = "Logger";
info.version = "1.0.0";
info.priority = ModulePriority::Core;
info.enabled = true;
return info;
}
std::string getConfigSectionName() const override {
return "logger";
}
bool validate() const override {
return true;
}
void resetToDefaults() override {
logLevel = LogLevel::Info;
consoleOutput = true;
fileOutput = false;
logFilePath.clear();
}
bool loadFromJson(const void* jsonData) override;
bool saveToJson(void* jsonData) const override;
};
class LoggerModuleInitializer : public IModuleInitializer {
public:
LoggerModuleInitializer();
~LoggerModuleInitializer() override;
ModuleId getModuleId() const override { return moduleId_; }
ModulePriority getPriority() const override { return ModulePriority::Core; }
std::vector<ModuleId> getDependencies() const override { return {}; }
bool initialize(const IModuleConfig* config) override;
void shutdown() override;
bool isInitialized() const override { return initialized_; }
void setModuleId(ModuleId id) { moduleId_ = id; }
private:
ModuleId moduleId_ = INVALID_MODULE_ID;
bool initialized_ = false;
};
} // namespace extra2d

View File

@ -1,39 +1,23 @@
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/config/config_manager.h> #include <extra2d/config/config_module.h>
#include <extra2d/config/module_registry.h> #include <extra2d/config/module_registry.h>
#include <extra2d/event/event_dispatcher.h> #include <extra2d/event/event_dispatcher.h>
#include <extra2d/event/event_queue.h> #include <extra2d/event/event_queue.h>
#include <extra2d/graphics/camera.h> #include <extra2d/graphics/camera.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/render_module.h>
#include <extra2d/graphics/viewport_adapter.h> #include <extra2d/graphics/viewport_adapter.h>
#include <extra2d/graphics/vram_manager.h> #include <extra2d/graphics/vram_manager.h>
#include <extra2d/platform/iinput.h> #include <extra2d/platform/iinput.h>
#include <extra2d/platform/platform_module.h> #include <extra2d/platform/platform_init_module.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/scene/scene_manager.h> #include <extra2d/scene/scene_manager.h>
#include <extra2d/utils/logger.h>
#include <extra2d/utils/timer.h> #include <extra2d/utils/timer.h>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <SDL.h>
#ifdef __SWITCH__
#include <switch.h>
#endif
#ifdef E2D_BACKEND_SDL2
namespace extra2d {
void initSDL2Backend();
}
#endif
namespace extra2d { namespace extra2d {
/**
* @brief
* @return
*/
static double getTimeSeconds() { static double getTimeSeconds() {
#ifdef __SWITCH__ #ifdef __SWITCH__
struct timespec ts; struct timespec ts;
@ -67,170 +51,97 @@ bool Application::init(const AppConfig& config) {
return true; return true;
} }
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) != 0) { register_config_module();
E2D_LOG_ERROR("Failed to initialize SDL: {}", SDL_GetError()); register_platform_module();
return false; register_window_module();
register_render_module();
auto* configInit = ModuleRegistry::instance().getInitializer(get_config_module_id());
if (configInit) {
auto* cfgInit = dynamic_cast<ConfigModuleInitializer*>(configInit);
if (cfgInit) {
cfgInit->setAppConfig(config);
}
} }
Logger::init(); return initModules();
E2D_LOG_INFO("Initializing application with config...");
if (!ConfigManager::instance().initialize()) {
E2D_LOG_ERROR("Failed to initialize ConfigManager");
return false;
}
ConfigManager::instance().setAppConfig(config);
return initImpl();
} }
bool Application::init(const std::string& configPath) { bool Application::init(const std::string& configPath) {
if (initialized_) { if (initialized_) {
E2D_LOG_WARN("Application already initialized");
return true; return true;
} }
E2D_LOG_INFO("Initializing application from config file: {}", configPath); register_config_module();
register_platform_module();
register_window_module();
register_render_module();
if (!ConfigManager::instance().initialize(configPath)) { auto* configInit = ModuleRegistry::instance().getInitializer(get_config_module_id());
E2D_LOG_WARN("Failed to load config from file, using defaults"); if (configInit) {
if (!ConfigManager::instance().initialize()) { auto* cfgInit = dynamic_cast<ConfigModuleInitializer*>(configInit);
E2D_LOG_ERROR("Failed to initialize ConfigManager"); if (cfgInit) {
return false; cfgInit->setConfigPath(configPath);
} }
} }
return initImpl(); return initModules();
} }
bool Application::initImpl() { bool Application::initModules() {
#ifdef E2D_BACKEND_SDL2
initSDL2Backend();
#endif
auto& configMgr = ConfigManager::instance();
AppConfig& appConfig = configMgr.appConfig();
PlatformType platform = appConfig.targetPlatform;
if (platform == PlatformType::Auto) {
#ifdef __SWITCH__
platform = PlatformType::Switch;
#else
#ifdef _WIN32
platform = PlatformType::Windows;
#elif defined(__linux__)
platform = PlatformType::Linux;
#elif defined(__APPLE__)
platform = PlatformType::macOS;
#else
platform = PlatformType::Windows;
#endif
#endif
}
E2D_LOG_INFO("Target platform: {} ({})", getPlatformTypeName(platform),
static_cast<int>(platform));
UniquePtr<PlatformConfig> platformConfig = createPlatformConfig(platform);
if (!platformConfig) {
E2D_LOG_ERROR("Failed to create platform config");
return false;
}
appConfig.applyPlatformConstraints(*platformConfig);
const auto& capabilities = platformConfig->capabilities();
E2D_LOG_INFO("Platform capabilities: windowed={}, fullscreen={}, cursor={}, DPI={}",
capabilities.supportsWindowed, capabilities.supportsFullscreen,
capabilities.supportsCursor, capabilities.supportsDPIAwareness);
if (platform == PlatformType::Switch) {
#ifdef __SWITCH__
Result rc;
rc = romfsInit();
if (R_SUCCEEDED(rc)) {
E2D_LOG_INFO("RomFS initialized successfully");
} else {
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc);
}
rc = socketInitializeDefault();
if (R_FAILED(rc)) {
E2D_LOG_WARN("socketInitializeDefault failed, nxlink will not be available");
}
#endif
}
auto initOrder = ModuleRegistry::instance().getInitializationOrder(); auto initOrder = ModuleRegistry::instance().getInitializationOrder();
E2D_LOG_INFO("Initializing {} registered modules...", initOrder.size());
for (ModuleId moduleId : initOrder) { for (ModuleId moduleId : initOrder) {
auto initializer = ModuleRegistry::instance().createInitializer(moduleId); auto* initializer = ModuleRegistry::instance().getInitializer(moduleId);
if (!initializer) { if (!initializer) {
continue; continue;
} }
auto* moduleConfig = ModuleRegistry::instance().getModuleConfig(moduleId); auto* moduleConfig = ModuleRegistry::instance().getModuleConfig(moduleId);
if (!moduleConfig) { if (!moduleConfig) {
E2D_LOG_WARN("Module {} has no config, skipping", moduleId);
continue; continue;
} }
auto info = moduleConfig->getModuleInfo(); auto info = moduleConfig->getModuleInfo();
if (!info.enabled) { if (!info.enabled) {
E2D_LOG_INFO("Module '{}' is disabled, skipping", info.name);
continue; continue;
} }
E2D_LOG_INFO("Initializing module '{}' (priority: {})", if (info.name == "Render") {
info.name, static_cast<int>(info.priority)); continue;
}
if (!initializer->initialize(moduleConfig)) { if (!initializer->initialize(moduleConfig)) {
E2D_LOG_ERROR("Failed to initialize module '{}'", info.name);
}
}
std::string backend = "sdl2";
#ifdef __SWITCH__
backend = "switch";
#endif
if (!BackendFactory::has(backend)) {
E2D_LOG_ERROR("Backend '{}' not available", backend);
auto backends = BackendFactory::backends();
if (backends.empty()) {
E2D_LOG_ERROR("No backends registered!");
return false; return false;
} }
backend = backends[0];
E2D_LOG_WARN("Using fallback backend: {}", backend);
} }
window_ = BackendFactory::createWindow(backend); auto* windowInit = ModuleRegistry::instance().getInitializer(get_window_module_id());
if (!windowInit || !windowInit->isInitialized()) {
return false;
}
auto* windowModule = dynamic_cast<WindowModuleInitializer*>(windowInit);
if (!windowModule) {
return false;
}
window_ = windowModule->getWindow();
if (!window_) { if (!window_) {
E2D_LOG_ERROR("Failed to create window for backend: {}", backend);
return false; return false;
} }
WindowConfigData winConfig = appConfig.window; auto* renderInit = ModuleRegistry::instance().getInitializer(get_render_module_id());
if (renderInit) {
auto* renderModule = dynamic_cast<RenderModuleInitializer*>(renderInit);
if (renderModule) {
renderModule->setWindow(window_);
if (platform == PlatformType::Switch) { auto* renderConfig = ModuleRegistry::instance().getModuleConfig(get_render_module_id());
winConfig.mode = WindowMode::Fullscreen; if (renderConfig && !renderInit->initialize(renderConfig)) {
winConfig.resizable = false; return false;
} }
}
if (!window_->create(winConfig)) {
E2D_LOG_ERROR("Failed to create window");
return false;
}
renderer_ = RenderBackend::create(appConfig.render.backend);
if (!renderer_ || !renderer_->init(window_.get())) {
E2D_LOG_ERROR("Failed to initialize renderer");
window_->destroy();
return false;
} }
sceneManager_ = makeUnique<SceneManager>(); sceneManager_ = makeUnique<SceneManager>();
@ -242,13 +153,12 @@ bool Application::initImpl() {
viewportAdapter_ = makeUnique<ViewportAdapter>(); viewportAdapter_ = makeUnique<ViewportAdapter>();
ViewportConfig vpConfig; ViewportConfig vpConfig;
vpConfig.logicWidth = static_cast<float>(appConfig.window.width); vpConfig.logicWidth = static_cast<float>(window_->width());
vpConfig.logicHeight = static_cast<float>(appConfig.window.height); vpConfig.logicHeight = static_cast<float>(window_->height());
vpConfig.mode = ViewportMode::AspectRatio; vpConfig.mode = ViewportMode::AspectRatio;
viewportAdapter_->setConfig(vpConfig); viewportAdapter_->setConfig(vpConfig);
camera_->setViewportAdapter(viewportAdapter_.get()); camera_->setViewportAdapter(viewportAdapter_.get());
viewportAdapter_->update(window_->width(), window_->height()); viewportAdapter_->update(window_->width(), window_->height());
window_->onResize([this](int width, int height) { window_->onResize([this](int width, int height) {
@ -272,12 +182,6 @@ bool Application::initImpl() {
initialized_ = true; initialized_ = true;
running_ = true; running_ = true;
E2D_LOG_INFO("Application initialized successfully");
E2D_LOG_INFO(" Window: {}x{}", window_->width(), window_->height());
E2D_LOG_INFO(" Backend: {}", backend);
E2D_LOG_INFO(" VSync: {}", appConfig.render.vsync);
E2D_LOG_INFO(" Target FPS: {}", appConfig.render.targetFPS);
return true; return true;
} }
@ -285,8 +189,6 @@ void Application::shutdown() {
if (!initialized_) if (!initialized_)
return; return;
E2D_LOG_INFO("Shutting down application...");
VRAMMgr::get().printStats(); VRAMMgr::get().printStats();
if (sceneManager_) { if (sceneManager_) {
@ -301,59 +203,24 @@ void Application::shutdown() {
eventQueue_.reset(); eventQueue_.reset();
eventDispatcher_.reset(); eventDispatcher_.reset();
if (renderer_) { window_ = nullptr;
renderer_->shutdown();
renderer_.reset();
}
if (window_) {
window_->destroy();
window_.reset();
}
auto modules = ModuleRegistry::instance().getAllModules();
auto initOrder = ModuleRegistry::instance().getInitializationOrder(); auto initOrder = ModuleRegistry::instance().getInitializationOrder();
for (auto it = initOrder.rbegin(); it != initOrder.rend(); ++it) { for (auto it = initOrder.rbegin(); it != initOrder.rend(); ++it) {
ModuleId moduleId = *it; ModuleId moduleId = *it;
auto initializer = ModuleRegistry::instance().createInitializer(moduleId); auto* initializer = ModuleRegistry::instance().getInitializer(moduleId);
if (initializer && initializer->isInitialized()) { if (initializer && initializer->isInitialized()) {
auto* moduleConfig = ModuleRegistry::instance().getModuleConfig(moduleId);
if (moduleConfig) {
auto info = moduleConfig->getModuleInfo();
E2D_LOG_INFO("Shutting down module '{}'", info.name);
}
initializer->shutdown(); initializer->shutdown();
} }
} }
PlatformType platform = ConfigManager::instance().appConfig().targetPlatform;
if (platform == PlatformType::Auto) {
#ifdef __SWITCH__
platform = PlatformType::Switch;
#else
platform = PlatformType::Windows;
#endif
}
if (platform == PlatformType::Switch) {
#ifdef __SWITCH__
romfsExit();
socketExit();
#endif
}
ConfigManager::instance().shutdown();
initialized_ = false; initialized_ = false;
running_ = false; running_ = false;
E2D_LOG_INFO("Application shutdown complete");
} }
void Application::run() { void Application::run() {
if (!initialized_) { if (!initialized_) {
E2D_LOG_ERROR("Application not initialized");
return; return;
} }
@ -372,7 +239,6 @@ void Application::quit() {
void Application::pause() { void Application::pause() {
if (!paused_) { if (!paused_) {
paused_ = true; paused_ = true;
E2D_LOG_INFO("Application paused");
} }
} }
@ -380,7 +246,6 @@ void Application::resume() {
if (paused_) { if (paused_) {
paused_ = false; paused_ = false;
lastFrameTime_ = getTimeSeconds(); lastFrameTime_ = getTimeSeconds();
E2D_LOG_INFO("Application resumed");
} }
} }
@ -436,24 +301,30 @@ void Application::update() {
} }
void Application::render() { void Application::render() {
if (!renderer_) { auto* renderInit = ModuleRegistry::instance().getInitializer(get_render_module_id());
E2D_LOG_ERROR("Render failed: renderer is null"); RenderBackend* renderer = nullptr;
if (renderInit) {
auto* renderModule = dynamic_cast<RenderModuleInitializer*>(renderInit);
if (renderModule) {
renderer = renderModule->getRenderer();
}
}
if (!renderer) {
return; return;
} }
if (viewportAdapter_) { if (viewportAdapter_) {
const auto& vp = viewportAdapter_->getViewport(); const auto& vp = viewportAdapter_->getViewport();
renderer_->setViewport( renderer->setViewport(
static_cast<int>(vp.origin.x), static_cast<int>(vp.origin.y), static_cast<int>(vp.origin.x), static_cast<int>(vp.origin.y),
static_cast<int>(vp.size.width), static_cast<int>(vp.size.height)); static_cast<int>(vp.size.width), static_cast<int>(vp.size.height));
} else { } else {
renderer_->setViewport(0, 0, window_->width(), window_->height()); renderer->setViewport(0, 0, window_->width(), window_->height());
} }
if (sceneManager_) { if (sceneManager_) {
sceneManager_->render(*renderer_); sceneManager_->render(*renderer);
} else {
E2D_LOG_WARN("Render: sceneManager is null");
} }
window_->swap(); window_->swap();
@ -463,6 +334,21 @@ IInput& Application::input() {
return *window_->input(); return *window_->input();
} }
RenderBackend& Application::renderer() {
auto* renderInit = ModuleRegistry::instance().getInitializer(get_render_module_id());
if (renderInit) {
auto* renderModule = dynamic_cast<RenderModuleInitializer*>(renderInit);
if (renderModule && renderModule->getRenderer()) {
return *renderModule->getRenderer();
}
}
static RenderBackend* dummy = nullptr;
if (!dummy) {
dummy = RenderBackend::create(BackendType::OpenGL).release();
}
return *dummy;
}
SceneManager& Application::scenes() { SceneManager& Application::scenes() {
return *sceneManager_; return *sceneManager_;
} }

View File

@ -0,0 +1,114 @@
#include <extra2d/config/config_module.h>
#include <extra2d/config/module_registry.h>
#include <extra2d/utils/logger.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace extra2d {
static ModuleId s_configModuleId = INVALID_MODULE_ID;
ModuleId get_config_module_id() {
return s_configModuleId;
}
bool ConfigModuleConfig::loadFromJson(const void* jsonData) {
if (!jsonData) return false;
try {
const json& j = *static_cast<const json*>(jsonData);
if (j.contains("configPath")) {
configPath = j["configPath"].get<std::string>();
}
return true;
} catch (...) {
return false;
}
}
bool ConfigModuleConfig::saveToJson(void* jsonData) const {
if (!jsonData) return false;
try {
json& j = *static_cast<json*>(jsonData);
j["configPath"] = configPath;
return true;
} catch (...) {
return false;
}
}
ConfigModuleInitializer::ConfigModuleInitializer()
: moduleId_(INVALID_MODULE_ID)
, initialized_(false) {
}
ConfigModuleInitializer::~ConfigModuleInitializer() {
if (initialized_) {
shutdown();
}
}
bool ConfigModuleInitializer::initialize(const IModuleConfig* config) {
if (initialized_) return true;
const ConfigModuleConfig* configModule = dynamic_cast<const ConfigModuleConfig*>(config);
if (!configPath_.empty()) {
if (!ConfigManager::instance().initialize(configPath_)) {
if (!ConfigManager::instance().initialize()) {
return false;
}
}
} else {
if (!ConfigManager::instance().initialize()) {
return false;
}
}
if (configModule && !configModule->appConfig.appName.empty()) {
ConfigManager::instance().setAppConfig(configModule->appConfig);
} else if (!appConfig_.appName.empty()) {
ConfigManager::instance().setAppConfig(appConfig_);
}
initialized_ = true;
E2D_LOG_INFO("Config module initialized");
return true;
}
void ConfigModuleInitializer::shutdown() {
if (!initialized_) return;
E2D_LOG_INFO("Config module shutting down");
ConfigManager::instance().shutdown();
initialized_ = false;
}
void register_config_module() {
if (s_configModuleId != INVALID_MODULE_ID) return;
s_configModuleId = ModuleRegistry::instance().registerModule(
makeUnique<ConfigModuleConfig>(),
[]() -> UniquePtr<IModuleInitializer> {
auto initializer = makeUnique<ConfigModuleInitializer>();
initializer->setModuleId(s_configModuleId);
return initializer;
}
);
}
namespace {
struct ConfigModuleAutoRegister {
ConfigModuleAutoRegister() {
register_config_module();
}
};
static ConfigModuleAutoRegister s_autoRegister;
}
} // namespace extra2d

View File

@ -1,6 +1,6 @@
#include <extra2d/config/module_registry.h> #include <extra2d/config/module_registry.h>
#include <extra2d/utils/logger.h>
#include <algorithm> #include <algorithm>
#include <cstdio>
namespace extra2d { namespace extra2d {
@ -26,7 +26,7 @@ ModuleId ModuleRegistry::registerModule(
ModuleInitializerFactory initializerFactory ModuleInitializerFactory initializerFactory
) { ) {
if (!config) { if (!config) {
E2D_LOG_ERROR("Cannot register null module config"); std::fprintf(stderr, "[ERROR] Cannot register null module config\n");
return INVALID_MODULE_ID; return INVALID_MODULE_ID;
} }
@ -35,7 +35,7 @@ ModuleId ModuleRegistry::registerModule(
ModuleInfo info = config->getModuleInfo(); ModuleInfo info = config->getModuleInfo();
if (nameToId_.find(info.name) != nameToId_.end()) { if (nameToId_.find(info.name) != nameToId_.end()) {
E2D_LOG_ERROR("Module '{}' already registered", info.name); std::fprintf(stderr, "[ERROR] Module '%s' already registered\n", info.name.c_str());
return INVALID_MODULE_ID; return INVALID_MODULE_ID;
} }
@ -50,7 +50,6 @@ ModuleId ModuleRegistry::registerModule(
modules_[id] = std::move(entry); modules_[id] = std::move(entry);
nameToId_[info.name] = id; nameToId_[info.name] = id;
E2D_LOG_INFO("Registered module '{}' with id {}", info.name, id);
return id; return id;
} }
@ -65,7 +64,6 @@ bool ModuleRegistry::unregisterModule(ModuleId id) {
auto it = modules_.find(id); auto it = modules_.find(id);
if (it == modules_.end()) { if (it == modules_.end()) {
E2D_LOG_WARN("Module with id {} not found for unregistration", id);
return false; return false;
} }
@ -73,7 +71,6 @@ bool ModuleRegistry::unregisterModule(ModuleId id) {
nameToId_.erase(info.name); nameToId_.erase(info.name);
modules_.erase(it); modules_.erase(it);
E2D_LOG_INFO("Unregistered module '{}' (id: {})", info.name, id);
return true; return true;
} }
@ -113,11 +110,11 @@ IModuleConfig* ModuleRegistry::getModuleConfigByName(const std::string& name) co
} }
/** /**
* @brief * @brief
* @param id * @param id
* @return nullptr * @return nullptr
*/ */
UniquePtr<IModuleInitializer> ModuleRegistry::createInitializer(ModuleId id) const { IModuleInitializer* ModuleRegistry::getInitializer(ModuleId id) {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
auto it = modules_.find(id); auto it = modules_.find(id);
@ -125,7 +122,11 @@ UniquePtr<IModuleInitializer> ModuleRegistry::createInitializer(ModuleId id) con
return nullptr; return nullptr;
} }
return it->second.initializerFactory(); if (!it->second.initializer) {
it->second.initializer = it->second.initializerFactory();
}
return it->second.initializer.get();
} }
/** /**
@ -197,8 +198,6 @@ void ModuleRegistry::clear() {
modules_.clear(); modules_.clear();
nameToId_.clear(); nameToId_.clear();
nextId_ = 1; nextId_ = 1;
E2D_LOG_INFO("Module registry cleared");
} }
/** /**

View File

@ -9,85 +9,47 @@ using json = nlohmann::json;
namespace extra2d { namespace extra2d {
// ============================================================================ static ModuleId s_renderModuleId = INVALID_MODULE_ID;
// RenderModuleConfig 实现
// ============================================================================ ModuleId get_render_module_id() {
return s_renderModuleId;
}
/**
* @brief
*
*
* - 1-240
* - 0248 16
* - 0
*
* @return true
*/
bool RenderModuleConfig::validate() const { bool RenderModuleConfig::validate() const {
if (targetFPS < 1 || targetFPS > 240) { if (targetFPS < 1 || targetFPS > 240) {
E2D_LOG_ERROR("Invalid target FPS: {}, must be between 1 and 240", targetFPS);
return false; return false;
} }
if (multisamples != 0 && multisamples != 2 && multisamples != 4 && if (multisamples != 0 && multisamples != 2 && multisamples != 4 &&
multisamples != 8 && multisamples != 16) { multisamples != 8 && multisamples != 16) {
E2D_LOG_ERROR("Invalid multisample count: {}, must be 0, 2, 4, 8 or 16", multisamples);
return false; return false;
} }
if (spriteBatchSize <= 0) { if (spriteBatchSize <= 0) {
E2D_LOG_ERROR("Invalid sprite batch size: {}, must be greater than 0", spriteBatchSize);
return false; return false;
} }
return true; return true;
} }
/**
* @brief
*
*
* - Switch MSAA 4 sRGB
* -
*
* @param platform
*/
void RenderModuleConfig::applyPlatformConstraints(PlatformType platform) { void RenderModuleConfig::applyPlatformConstraints(PlatformType platform) {
switch (platform) { switch (platform) {
case PlatformType::Switch: case PlatformType::Switch:
if (multisamples > 4) { if (multisamples > 4) {
E2D_LOG_WARN("Switch platform limits MSAA to 4x, reducing from {}", multisamples);
multisamples = 4; multisamples = 4;
} }
if (sRGBFramebuffer) { if (sRGBFramebuffer) {
E2D_LOG_WARN("Switch platform does not support sRGB framebuffer, disabling");
sRGBFramebuffer = false; sRGBFramebuffer = false;
} }
if (targetFPS > 60) { if (targetFPS > 60) {
E2D_LOG_WARN("Switch platform target FPS capped at 60");
targetFPS = 60; targetFPS = 60;
} }
break; break;
case PlatformType::Windows:
case PlatformType::Linux:
case PlatformType::macOS:
default: default:
break; break;
} }
} }
/**
* @brief
*
*
* - OpenGL
* -
* - 60
* -
* - sRGB
* - 1000
*/
void RenderModuleConfig::resetToDefaults() { void RenderModuleConfig::resetToDefaults() {
backend = BackendType::OpenGL; backend = BackendType::OpenGL;
vsync = true; vsync = true;
@ -97,19 +59,8 @@ void RenderModuleConfig::resetToDefaults() {
spriteBatchSize = 1000; spriteBatchSize = 1000;
} }
/**
* @brief JSON
*
* JSON
*
* @param jsonData JSON nlohmann::json
* @return true
*/
bool RenderModuleConfig::loadFromJson(const void* jsonData) { bool RenderModuleConfig::loadFromJson(const void* jsonData) {
if (!jsonData) { if (!jsonData) return false;
E2D_LOG_ERROR("Null JSON data provided");
return false;
}
try { try {
const json& j = *static_cast<const json*>(jsonData); const json& j = *static_cast<const json*>(jsonData);
@ -118,9 +69,6 @@ bool RenderModuleConfig::loadFromJson(const void* jsonData) {
std::string backendStr = j["backend"].get<std::string>(); std::string backendStr = j["backend"].get<std::string>();
if (backendStr == "opengl") { if (backendStr == "opengl") {
backend = BackendType::OpenGL; backend = BackendType::OpenGL;
} else {
E2D_LOG_WARN("Unknown backend type: {}, defaulting to OpenGL", backendStr);
backend = BackendType::OpenGL;
} }
} }
@ -144,124 +92,57 @@ bool RenderModuleConfig::loadFromJson(const void* jsonData) {
spriteBatchSize = j["spriteBatchSize"].get<int>(); spriteBatchSize = j["spriteBatchSize"].get<int>();
} }
E2D_LOG_INFO("Render config loaded from JSON");
return true; return true;
} catch (const json::exception& e) { } catch (...) {
E2D_LOG_ERROR("Failed to parse render config from JSON: {}", e.what());
return false; return false;
} }
} }
/**
* @brief JSON
*
* JSON
*
* @param jsonData JSON nlohmann::json
* @return true
*/
bool RenderModuleConfig::saveToJson(void* jsonData) const { bool RenderModuleConfig::saveToJson(void* jsonData) const {
if (!jsonData) { if (!jsonData) return false;
E2D_LOG_ERROR("Null JSON data provided");
return false;
}
try { try {
json& j = *static_cast<json*>(jsonData); json& j = *static_cast<json*>(jsonData);
j["backend"] = "opengl";
std::string backendStr = "opengl";
switch (backend) {
case BackendType::OpenGL:
backendStr = "opengl";
break;
default:
backendStr = "opengl";
break;
}
j["backend"] = backendStr;
j["vsync"] = vsync; j["vsync"] = vsync;
j["targetFPS"] = targetFPS; j["targetFPS"] = targetFPS;
j["multisamples"] = multisamples; j["multisamples"] = multisamples;
j["sRGBFramebuffer"] = sRGBFramebuffer; j["sRGBFramebuffer"] = sRGBFramebuffer;
j["spriteBatchSize"] = spriteBatchSize; j["spriteBatchSize"] = spriteBatchSize;
E2D_LOG_INFO("Render config saved to JSON");
return true; return true;
} catch (const json::exception& e) { } catch (...) {
E2D_LOG_ERROR("Failed to save render config to JSON: {}", e.what());
return false; return false;
} }
} }
// ============================================================================
// RenderModuleInitializer 实现
// ============================================================================
/**
* @brief
*
*
*/
RenderModuleInitializer::RenderModuleInitializer() RenderModuleInitializer::RenderModuleInitializer()
: moduleId_(INVALID_MODULE_ID) : moduleId_(INVALID_MODULE_ID)
, windowModuleId_(INVALID_MODULE_ID) , window_(nullptr)
, renderer_(nullptr)
, initialized_(false) { , initialized_(false) {
} }
/**
* @brief
*
*
*/
RenderModuleInitializer::~RenderModuleInitializer() { RenderModuleInitializer::~RenderModuleInitializer() {
if (initialized_) { if (initialized_) {
shutdown(); shutdown();
} }
} }
/**
* @brief
*
*
*
* @return
*/
std::vector<ModuleId> RenderModuleInitializer::getDependencies() const { std::vector<ModuleId> RenderModuleInitializer::getDependencies() const {
if (windowModuleId_ != INVALID_MODULE_ID) {
return {windowModuleId_};
}
return {}; return {};
} }
/**
* @brief
*
*
*
* @param config
* @return true
*/
bool RenderModuleInitializer::initialize(const IModuleConfig* config) { bool RenderModuleInitializer::initialize(const IModuleConfig* config) {
if (initialized_) { if (initialized_) return true;
E2D_LOG_WARN("Render module already initialized");
return true;
}
if (!config) { if (!config) return false;
E2D_LOG_ERROR("Null config provided for render module initialization");
return false;
}
const RenderModuleConfig* renderConfig = dynamic_cast<const RenderModuleConfig*>(config); const RenderModuleConfig* renderConfig = dynamic_cast<const RenderModuleConfig*>(config);
if (!renderConfig) { if (!renderConfig) return false;
E2D_LOG_ERROR("Invalid config type for render module");
return false;
}
if (!renderConfig->validate()) { if (!renderConfig->validate()) return false;
E2D_LOG_ERROR("Invalid render module configuration");
if (!window_) {
E2D_LOG_ERROR("Render module requires window to be set");
return false; return false;
} }
@ -271,35 +152,19 @@ bool RenderModuleInitializer::initialize(const IModuleConfig* config) {
return false; return false;
} }
IWindow* window = nullptr; if (!renderer_->init(window_)) {
if (windowModuleId_ != INVALID_MODULE_ID) { E2D_LOG_ERROR("Failed to initialize renderer");
ModuleRegistry& registry = ModuleRegistry::instance(); renderer_.reset();
IModuleConfig* windowConfig = registry.getModuleConfig(windowModuleId_); return false;
if (windowConfig) {
E2D_LOG_INFO("Render module found window module dependency");
}
} }
E2D_LOG_INFO("Render module initialized successfully");
E2D_LOG_INFO(" Backend: {}", renderConfig->backend == BackendType::OpenGL ? "OpenGL" : "Unknown");
E2D_LOG_INFO(" VSync: {}", renderConfig->vsync ? "enabled" : "disabled");
E2D_LOG_INFO(" Target FPS: {}", renderConfig->targetFPS);
E2D_LOG_INFO(" Multisamples: {}", renderConfig->multisamples);
E2D_LOG_INFO(" Sprite Batch Size: {}", renderConfig->spriteBatchSize);
initialized_ = true; initialized_ = true;
E2D_LOG_INFO("Render module initialized");
return true; return true;
} }
/**
* @brief
*
*
*/
void RenderModuleInitializer::shutdown() { void RenderModuleInitializer::shutdown() {
if (!initialized_) { if (!initialized_) return;
return;
}
if (renderer_) { if (renderer_) {
renderer_->shutdown(); renderer_->shutdown();
@ -307,7 +172,30 @@ void RenderModuleInitializer::shutdown() {
} }
initialized_ = false; initialized_ = false;
E2D_LOG_INFO("Render module shutdown complete"); E2D_LOG_INFO("Render module shutdown");
}
void register_render_module() {
if (s_renderModuleId != INVALID_MODULE_ID) return;
s_renderModuleId = ModuleRegistry::instance().registerModule(
makeUnique<RenderModuleConfig>(),
[]() -> UniquePtr<IModuleInitializer> {
auto initializer = makeUnique<RenderModuleInitializer>();
initializer->setModuleId(s_renderModuleId);
return initializer;
}
);
}
namespace {
struct RenderModuleAutoRegister {
RenderModuleAutoRegister() {
register_render_module();
}
};
static RenderModuleAutoRegister s_autoRegister;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -0,0 +1,171 @@
#include <extra2d/platform/platform_init_module.h>
#include <extra2d/config/module_registry.h>
#include <extra2d/config/config_manager.h>
#include <extra2d/utils/logger.h>
#include <nlohmann/json.hpp>
#ifdef __SWITCH__
#include <switch.h>
#endif
using json = nlohmann::json;
namespace extra2d {
static ModuleId s_platformModuleId = INVALID_MODULE_ID;
ModuleId get_platform_module_id() {
return s_platformModuleId;
}
bool PlatformModuleConfig::loadFromJson(const void* jsonData) {
if (!jsonData) return false;
try {
const json& j = *static_cast<const json*>(jsonData);
if (j.contains("targetPlatform")) {
int platform = j["targetPlatform"].get<int>();
if (platform >= 0 && platform <= 5) {
targetPlatform = static_cast<PlatformType>(platform);
}
}
return true;
} catch (...) {
return false;
}
}
bool PlatformModuleConfig::saveToJson(void* jsonData) const {
if (!jsonData) return false;
try {
json& j = *static_cast<json*>(jsonData);
j["targetPlatform"] = static_cast<int>(targetPlatform);
return true;
} catch (...) {
return false;
}
}
PlatformModuleInitializer::PlatformModuleInitializer()
: moduleId_(INVALID_MODULE_ID)
, initialized_(false)
, targetPlatform_(PlatformType::Auto)
, resolvedPlatform_(PlatformType::Windows) {
}
PlatformModuleInitializer::~PlatformModuleInitializer() {
if (initialized_) {
shutdown();
}
}
bool PlatformModuleInitializer::initialize(const IModuleConfig* config) {
if (initialized_) return true;
const PlatformModuleConfig* platformConfig = dynamic_cast<const PlatformModuleConfig*>(config);
if (platformConfig) {
targetPlatform_ = platformConfig->targetPlatform;
}
resolvedPlatform_ = targetPlatform_;
if (resolvedPlatform_ == PlatformType::Auto) {
#ifdef __SWITCH__
resolvedPlatform_ = PlatformType::Switch;
#else
#ifdef _WIN32
resolvedPlatform_ = PlatformType::Windows;
#elif defined(__linux__)
resolvedPlatform_ = PlatformType::Linux;
#elif defined(__APPLE__)
resolvedPlatform_ = PlatformType::macOS;
#else
resolvedPlatform_ = PlatformType::Windows;
#endif
#endif
}
platformConfig_ = createPlatformConfig(resolvedPlatform_);
if (!platformConfig_) {
E2D_LOG_ERROR("Failed to create platform config");
return false;
}
auto& appConfig = ConfigManager::instance().appConfig();
appConfig.applyPlatformConstraints(*platformConfig_);
if (resolvedPlatform_ == PlatformType::Switch) {
if (!initSwitch()) {
return false;
}
}
initialized_ = true;
E2D_LOG_INFO("Platform module initialized ({})", getPlatformTypeName(resolvedPlatform_));
return true;
}
bool PlatformModuleInitializer::initSwitch() {
#ifdef __SWITCH__
Result rc;
rc = romfsInit();
if (R_SUCCEEDED(rc)) {
E2D_LOG_INFO("RomFS initialized successfully");
} else {
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc);
}
rc = socketInitializeDefault();
if (R_FAILED(rc)) {
E2D_LOG_WARN("socketInitializeDefault failed, nxlink will not be available");
}
#endif
return true;
}
void PlatformModuleInitializer::shutdown() {
if (!initialized_) return;
E2D_LOG_INFO("Platform module shutting down");
if (resolvedPlatform_ == PlatformType::Switch) {
shutdownSwitch();
}
platformConfig_.reset();
initialized_ = false;
}
void PlatformModuleInitializer::shutdownSwitch() {
#ifdef __SWITCH__
romfsExit();
socketExit();
#endif
}
void register_platform_module() {
if (s_platformModuleId != INVALID_MODULE_ID) return;
s_platformModuleId = ModuleRegistry::instance().registerModule(
makeUnique<PlatformModuleConfig>(),
[]() -> UniquePtr<IModuleInitializer> {
auto initializer = makeUnique<PlatformModuleInitializer>();
initializer->setModuleId(s_platformModuleId);
return initializer;
}
);
}
namespace {
struct PlatformModuleAutoRegister {
PlatformModuleAutoRegister() {
register_platform_module();
}
};
static PlatformModuleAutoRegister s_autoRegister;
}
} // namespace extra2d

View File

@ -1,340 +1,249 @@
#include <extra2d/platform/window_module.h> #include <extra2d/platform/window_module.h>
#include <extra2d/config/module_registry.h> #include <extra2d/config/module_registry.h>
#include <extra2d/config/config_manager.h>
#include <extra2d/platform/platform_module.h> #include <extra2d/platform/platform_module.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#ifdef E2D_BACKEND_SDL2
#include <SDL.h>
#endif
#ifdef E2D_BACKEND_GLFW
#include <GLFW/glfw3.h>
#endif
#ifdef __SWITCH__
#include <switch.h>
#endif
using json = nlohmann::json; using json = nlohmann::json;
namespace extra2d { namespace extra2d {
// ============================================================================ static ModuleId s_windowModuleId = INVALID_MODULE_ID;
// WindowModuleConfig 实现
// ============================================================================
/** ModuleId get_window_module_id() {
* @brief return s_windowModuleId;
*
* @return true
*/
bool WindowModuleConfig::validate() const {
if (windowConfig.width <= 0) {
E2D_LOG_ERROR("Window width must be positive, got: {}", windowConfig.width);
return false;
}
if (windowConfig.height <= 0) {
E2D_LOG_ERROR("Window height must be positive, got: {}", windowConfig.height);
return false;
}
if (windowConfig.title.empty()) {
E2D_LOG_WARN("Window title is empty, using default title");
}
if (windowConfig.multisamples < 0) {
E2D_LOG_ERROR("MSAA samples cannot be negative, got: {}", windowConfig.multisamples);
return false;
}
if (windowConfig.multisamples != 0 &&
windowConfig.multisamples != 2 &&
windowConfig.multisamples != 4 &&
windowConfig.multisamples != 8 &&
windowConfig.multisamples != 16) {
E2D_LOG_WARN("MSAA samples should be 0, 2, 4, 8, or 16, got: {}", windowConfig.multisamples);
}
if (backend.empty()) {
E2D_LOG_ERROR("Backend name cannot be empty");
return false;
}
return true;
} }
/**
* @brief
*
* @param platform
*/
void WindowModuleConfig::applyPlatformConstraints(PlatformType platform) {
switch (platform) {
case PlatformType::Switch:
E2D_LOG_INFO("Applying Nintendo Switch platform constraints");
windowConfig.mode = WindowMode::Fullscreen;
windowConfig.resizable = false;
windowConfig.centered = false;
windowConfig.width = 1920;
windowConfig.height = 1080;
backend = "switch";
break;
case PlatformType::Windows:
case PlatformType::Linux:
case PlatformType::macOS:
E2D_LOG_INFO("Applying desktop platform constraints");
if (windowConfig.width <= 0) {
windowConfig.width = 1280;
}
if (windowConfig.height <= 0) {
windowConfig.height = 720;
}
break;
case PlatformType::Auto:
default:
E2D_LOG_INFO("Auto-detecting platform constraints");
break;
}
}
/**
* @brief
*
*/
void WindowModuleConfig::resetToDefaults() {
windowConfig = WindowConfigData{};
backend = "sdl2";
E2D_LOG_INFO("Window module config reset to defaults");
}
/**
* @brief JSON
* JSON
* @param jsonData JSON
* @return true
*/
bool WindowModuleConfig::loadFromJson(const void* jsonData) { bool WindowModuleConfig::loadFromJson(const void* jsonData) {
if (!jsonData) { if (!jsonData) return false;
E2D_LOG_ERROR("JSON data is null");
return false;
}
const json& obj = *static_cast<const json*>(jsonData); try {
const json& j = *static_cast<const json*>(jsonData);
if (!obj.is_object()) { if (j.contains("backend")) {
E2D_LOG_ERROR("JSON data must be an object"); backend = j["backend"].get<std::string>();
return false;
}
if (obj.contains("title") && obj["title"].is_string()) {
windowConfig.title = obj["title"].get<std::string>();
}
if (obj.contains("width") && obj["width"].is_number_integer()) {
windowConfig.width = obj["width"].get<int>();
}
if (obj.contains("height") && obj["height"].is_number_integer()) {
windowConfig.height = obj["height"].get<int>();
}
if (obj.contains("fullscreen") && obj["fullscreen"].is_boolean()) {
windowConfig.mode = obj["fullscreen"].get<bool>() ? WindowMode::Fullscreen : WindowMode::Windowed;
}
if (obj.contains("mode") && obj["mode"].is_string()) {
std::string modeStr = obj["mode"].get<std::string>();
if (modeStr == "fullscreen") {
windowConfig.mode = WindowMode::Fullscreen;
} else if (modeStr == "borderless") {
windowConfig.mode = WindowMode::Borderless;
} else {
windowConfig.mode = WindowMode::Windowed;
} }
}
if (obj.contains("resizable") && obj["resizable"].is_boolean()) { if (j.contains("title")) {
windowConfig.resizable = obj["resizable"].get<bool>(); windowConfig.title = j["title"].get<std::string>();
} }
if (j.contains("width")) {
windowConfig.width = j["width"].get<int>();
}
if (j.contains("height")) {
windowConfig.height = j["height"].get<int>();
}
if (j.contains("fullscreen")) {
windowConfig.mode = j["fullscreen"].get<bool>() ? WindowMode::Fullscreen : WindowMode::Windowed;
}
if (j.contains("vsync")) {
windowConfig.vsync = j["vsync"].get<bool>();
}
if (j.contains("resizable")) {
windowConfig.resizable = j["resizable"].get<bool>();
}
if (obj.contains("vsync") && obj["vsync"].is_boolean()) { return true;
windowConfig.vsync = obj["vsync"].get<bool>(); } catch (...) {
}
if (obj.contains("multisamples") && obj["multisamples"].is_number_integer()) {
windowConfig.multisamples = obj["multisamples"].get<int>();
}
if (obj.contains("msaaSamples") && obj["msaaSamples"].is_number_integer()) {
windowConfig.multisamples = obj["msaaSamples"].get<int>();
}
if (obj.contains("centered") && obj["centered"].is_boolean()) {
windowConfig.centered = obj["centered"].get<bool>();
}
if (obj.contains("centerWindow") && obj["centerWindow"].is_boolean()) {
windowConfig.centered = obj["centerWindow"].get<bool>();
}
if (obj.contains("visible") && obj["visible"].is_boolean()) {
windowConfig.visible = obj["visible"].get<bool>();
}
if (obj.contains("decorated") && obj["decorated"].is_boolean()) {
windowConfig.decorated = obj["decorated"].get<bool>();
}
if (obj.contains("backend") && obj["backend"].is_string()) {
backend = obj["backend"].get<std::string>();
}
E2D_LOG_INFO("Window module config loaded from JSON");
return true;
}
/**
* @brief JSON
* JSON
* @param jsonData JSON
* @return true
*/
bool WindowModuleConfig::saveToJson(void* jsonData) const {
if (!jsonData) {
E2D_LOG_ERROR("JSON data pointer is null");
return false; return false;
} }
json& obj = *static_cast<json*>(jsonData);
obj["title"] = windowConfig.title;
obj["width"] = windowConfig.width;
obj["height"] = windowConfig.height;
std::string modeStr = "windowed";
if (windowConfig.mode == WindowMode::Fullscreen) {
modeStr = "fullscreen";
} else if (windowConfig.mode == WindowMode::Borderless) {
modeStr = "borderless";
}
obj["mode"] = modeStr;
obj["resizable"] = windowConfig.resizable;
obj["vsync"] = windowConfig.vsync;
obj["multisamples"] = windowConfig.multisamples;
obj["centered"] = windowConfig.centered;
obj["visible"] = windowConfig.visible;
obj["decorated"] = windowConfig.decorated;
obj["backend"] = backend;
E2D_LOG_INFO("Window module config saved to JSON");
return true;
} }
// ============================================================================ bool WindowModuleConfig::saveToJson(void* jsonData) const {
// WindowModuleInitializer 实现 if (!jsonData) return false;
// ============================================================================
try {
json& j = *static_cast<json*>(jsonData);
j["backend"] = backend;
j["title"] = windowConfig.title;
j["width"] = windowConfig.width;
j["height"] = windowConfig.height;
j["fullscreen"] = (windowConfig.mode == WindowMode::Fullscreen);
j["vsync"] = windowConfig.vsync;
j["resizable"] = windowConfig.resizable;
return true;
} catch (...) {
return false;
}
}
/**
* @brief
*
*/
WindowModuleInitializer::WindowModuleInitializer() WindowModuleInitializer::WindowModuleInitializer()
: moduleId_(INVALID_MODULE_ID) : moduleId_(INVALID_MODULE_ID)
, window_(nullptr) , initialized_(false)
, initialized_(false) { , backendInitialized_(false) {
E2D_LOG_DEBUG("WindowModuleInitializer constructed");
} }
/**
* @brief
*
*/
WindowModuleInitializer::~WindowModuleInitializer() { WindowModuleInitializer::~WindowModuleInitializer() {
if (initialized_) { if (initialized_) {
shutdown(); shutdown();
} }
E2D_LOG_DEBUG("WindowModuleInitializer destructed");
} }
/** bool WindowModuleInitializer::initBackend() {
* @brief #ifdef E2D_BACKEND_SDL2
* 使 BackendFactory if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) != 0) {
* @param config E2D_LOG_ERROR("Failed to initialize SDL2: {}", SDL_GetError());
* @return true
*/
bool WindowModuleInitializer::initialize(const IModuleConfig* config) {
if (initialized_) {
E2D_LOG_WARN("Window module already initialized");
return true;
}
if (!config) {
E2D_LOG_ERROR("Window module config is null");
return false; return false;
} }
E2D_LOG_INFO("SDL2 backend initialized");
backendInitialized_ = true;
return true;
#endif
#ifdef E2D_BACKEND_GLFW
if (!glfwInit()) {
E2D_LOG_ERROR("Failed to initialize GLFW");
return false;
}
E2D_LOG_INFO("GLFW backend initialized");
backendInitialized_ = true;
return true;
#endif
#ifdef E2D_BACKEND_SWITCH
E2D_LOG_INFO("Switch backend (no init required)");
backendInitialized_ = true;
return true;
#endif
E2D_LOG_ERROR("No backend available");
return false;
}
void WindowModuleInitializer::shutdownBackend() {
if (!backendInitialized_) return;
#ifdef E2D_BACKEND_SDL2
SDL_Quit();
E2D_LOG_INFO("SDL2 backend shutdown");
#endif
#ifdef E2D_BACKEND_GLFW
glfwTerminate();
E2D_LOG_INFO("GLFW backend shutdown");
#endif
backendInitialized_ = false;
}
bool WindowModuleInitializer::initialize(const IModuleConfig* config) {
if (initialized_) return true;
const WindowModuleConfig* windowConfig = dynamic_cast<const WindowModuleConfig*>(config); const WindowModuleConfig* windowConfig = dynamic_cast<const WindowModuleConfig*>(config);
if (!windowConfig) { if (!windowConfig) {
E2D_LOG_ERROR("Invalid config type for window module"); E2D_LOG_ERROR("Invalid window module config");
return false; return false;
} }
ModuleInfo info = config->getModuleInfo(); backend_ = windowConfig->backend;
moduleId_ = info.id; windowConfig_ = windowConfig->windowConfig;
const std::string& backend = windowConfig->backend; #ifdef __SWITCH__
backend_ = "switch";
windowConfig_.mode = WindowMode::Fullscreen;
windowConfig_.resizable = false;
#endif
if (!BackendFactory::has(backend)) { if (!initBackend()) {
E2D_LOG_ERROR("Backend '{}' not available", backend); return false;
}
#ifdef E2D_BACKEND_SDL2
extern void initSDL2Backend();
initSDL2Backend();
#endif
if (!BackendFactory::has(backend_)) {
E2D_LOG_ERROR("Backend '{}' not available", backend_);
auto backends = BackendFactory::backends(); auto backends = BackendFactory::backends();
if (backends.empty()) { if (backends.empty()) {
E2D_LOG_ERROR("No backends registered!"); E2D_LOG_ERROR("No backends registered!");
shutdownBackend();
return false; return false;
} }
std::string backendList; backend_ = backends[0];
for (const auto& b : backends) { E2D_LOG_WARN("Using fallback backend: {}", backend_);
if (!backendList.empty()) backendList += ", "; }
backendList += b;
} if (!createWindow(backend_, windowConfig_)) {
E2D_LOG_WARN("Available backends: {}", backendList); E2D_LOG_ERROR("Failed to create window");
shutdownBackend();
return false; return false;
} }
initialized_ = true;
E2D_LOG_INFO("Window module initialized");
E2D_LOG_INFO(" Window: {}x{}", window_->width(), window_->height());
E2D_LOG_INFO(" Backend: {}", backend_);
E2D_LOG_INFO(" VSync: {}", windowConfig_.vsync);
return true;
}
bool WindowModuleInitializer::createWindow(const std::string& backend, const WindowConfigData& config) {
window_ = BackendFactory::createWindow(backend); window_ = BackendFactory::createWindow(backend);
if (!window_) { if (!window_) {
E2D_LOG_ERROR("Failed to create window for backend: {}", backend); E2D_LOG_ERROR("Failed to create window for backend: {}", backend);
return false; return false;
} }
if (!window_->create(windowConfig->windowConfig)) { if (!window_->create(config)) {
E2D_LOG_ERROR("Failed to create window with given config"); E2D_LOG_ERROR("Failed to create window");
window_.reset();
return false; return false;
} }
initialized_ = true;
E2D_LOG_INFO("Window module initialized successfully (backend: {}, {}x{})",
backend,
windowConfig->windowConfig.width,
windowConfig->windowConfig.height);
return true; return true;
} }
/**
* @brief
*
*/
void WindowModuleInitializer::shutdown() { void WindowModuleInitializer::shutdown() {
if (!initialized_) { if (!initialized_) return;
E2D_LOG_WARN("Window module not initialized, nothing to shutdown");
return; E2D_LOG_INFO("Window module shutting down");
}
if (window_) { if (window_) {
window_->destroy(); window_->destroy();
window_.reset(); window_.reset();
} }
initialized_ = false; shutdownBackend();
moduleId_ = INVALID_MODULE_ID;
E2D_LOG_INFO("Window module shutdown complete"); initialized_ = false;
}
void register_window_module() {
if (s_windowModuleId != INVALID_MODULE_ID) return;
s_windowModuleId = ModuleRegistry::instance().registerModule(
makeUnique<WindowModuleConfig>(),
[]() -> UniquePtr<IModuleInitializer> {
auto initializer = makeUnique<WindowModuleInitializer>();
initializer->setModuleId(s_windowModuleId);
return initializer;
}
);
}
namespace {
struct WindowModuleAutoRegister {
WindowModuleAutoRegister() {
register_window_module();
}
};
static WindowModuleAutoRegister s_autoRegister;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,114 +1,177 @@
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <cstdio>
#include <ctime>
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef __SWITCH__
#include <switch.h>
#endif
namespace extra2d { namespace extra2d {
// 静态成员定义
LogLevel Logger::level_ = LogLevel::Info; LogLevel Logger::level_ = LogLevel::Info;
bool Logger::initialized_ = false; bool Logger::initialized_ = false;
bool Logger::consoleOutput_ = true; bool Logger::consoleOutput_ = true;
bool Logger::fileOutput_ = false; bool Logger::fileOutput_ = false;
std::string Logger::logFile_; std::string Logger::logFile_;
void *Logger::logFileHandle_ = nullptr;
/**
* @brief
* @param level
* @return
*/
const char *Logger::getLevelString(LogLevel level) { const char *Logger::getLevelString(LogLevel level) {
switch (level) { switch (level) {
case LogLevel::Trace: case LogLevel::Trace:
return "TRACE"; return "TRACE";
case LogLevel::Debug: case LogLevel::Debug:
return "DEBUG"; return "DEBUG";
case LogLevel::Info: case LogLevel::Info:
return "INFO "; return "INFO ";
case LogLevel::Warn: case LogLevel::Warn:
return "WARN "; return "WARN ";
case LogLevel::Error: case LogLevel::Error:
return "ERROR"; return "ERROR";
case LogLevel::Fatal: case LogLevel::Fatal:
return "FATAL"; return "FATAL";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
}
void Logger::writeToConsole(LogLevel level, const char *msg) {
const char *levelStr = getLevelString(level);
#ifdef _WIN32
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
WORD color = 7;
switch (level) {
case LogLevel::Trace: color = 8; break;
case LogLevel::Debug: color = 8; break;
case LogLevel::Info: color = 7; break;
case LogLevel::Warn: color = 14; break;
case LogLevel::Error: color = 12; break;
case LogLevel::Fatal: color = 12 | FOREGROUND_INTENSITY; break;
default: break;
}
SetConsoleTextAttribute(hConsole, color);
printf("[%s] %s\n", levelStr, msg);
SetConsoleTextAttribute(hConsole, 7);
#else
const char *colorCode = "\033[0m";
switch (level) {
case LogLevel::Trace: colorCode = "\033[90m"; break;
case LogLevel::Debug: colorCode = "\033[90m"; break;
case LogLevel::Info: colorCode = "\033[0m"; break;
case LogLevel::Warn: colorCode = "\033[33m"; break;
case LogLevel::Error: colorCode = "\033[31m"; break;
case LogLevel::Fatal: colorCode = "\033[1;31m"; break;
default: break;
}
printf("%s[%s] %s\033[0m\n", colorCode, levelStr, msg);
#endif
}
void Logger::writeToFile(LogLevel level, const char *msg) {
if (!logFileHandle_) return;
FILE *fp = static_cast<FILE *>(logFileHandle_);
time_t now = time(nullptr);
struct tm *tm_info = localtime(&now);
char timeBuf[32];
strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", tm_info);
fprintf(fp, "[%s] [%s] %s\n", timeBuf, getLevelString(level), msg);
fflush(fp);
}
void Logger::outputLog(LogLevel level, const char *msg) {
if (consoleOutput_) {
writeToConsole(level, msg);
}
if (fileOutput_) {
writeToFile(level, msg);
}
} }
/**
* @brief
*
* SDL日志级别为详细模式
*/
void Logger::init() { void Logger::init() {
if (initialized_) { if (initialized_) {
return; return;
} }
// 设置 SDL 日志级别为详细模式(允许所有级别的日志) #ifdef _WIN32
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE); SetConsoleOutputCP(CP_UTF8);
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut != INVALID_HANDLE_VALUE) {
DWORD mode = 0;
if (GetConsoleMode(hOut, &mode)) {
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, mode);
}
}
#endif
initialized_ = true; #ifdef __SWITCH__
log(LogLevel::Info, "Logger initialized with SDL2"); consoleInit(NULL);
#endif
initialized_ = true;
log(LogLevel::Info, "Logger initialized");
} }
/**
* @brief
*
*
*/
void Logger::shutdown() { void Logger::shutdown() {
if (initialized_) { if (initialized_) {
log(LogLevel::Info, "Logger shutting down"); log(LogLevel::Info, "Logger shutting down");
} }
initialized_ = false;
if (logFileHandle_) {
fclose(static_cast<FILE *>(logFileHandle_));
logFileHandle_ = nullptr;
}
#ifdef __SWITCH__
consoleExit(NULL);
#endif
initialized_ = false;
fileOutput_ = false;
} }
/**
* @brief
* @param level
*
*
*/
void Logger::setLevel(LogLevel level) { void Logger::setLevel(LogLevel level) {
level_ = level; level_ = level;
// 同时设置 SDL 的日志级别
if (level != LogLevel::Off) {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level));
}
} }
/**
* @brief
* @param enable true启用控制台输出false禁用
*
* SDL日志优先级实现
*/
void Logger::setConsoleOutput(bool enable) { void Logger::setConsoleOutput(bool enable) {
consoleOutput_ = enable; consoleOutput_ = enable;
// SDL2 日志默认输出到控制台,通过设置日志优先级控制
if (!enable) {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_CRITICAL);
} else {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level_));
}
} }
/**
* @brief
* @param filename
*
*
*/
void Logger::setFileOutput(const std::string &filename) { void Logger::setFileOutput(const std::string &filename) {
logFile_ = filename; if (logFileHandle_) {
fileOutput_ = !filename.empty(); fclose(static_cast<FILE *>(logFileHandle_));
logFileHandle_ = nullptr;
}
if (fileOutput_) { logFile_ = filename;
// SDL2 使用 SDL_LogSetOutputFunction 可以重定向日志输出 fileOutput_ = !filename.empty();
// 这里我们记录文件路径,实际文件输出可以通过自定义回调实现
log(LogLevel::Info, "File output configured: {}", filename); if (fileOutput_) {
} #ifdef _WIN32
FILE *fp = nullptr;
fopen_s(&fp, filename.c_str(), "a");
#else
FILE *fp = fopen(filename.c_str(), "a");
#endif
logFileHandle_ = fp;
if (fp) {
time_t now = time(nullptr);
struct tm *tm_info = localtime(&now);
char timeBuf[32];
strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", tm_info);
fprintf(fp, "\n=== Log session started at %s ===\n", timeBuf);
fflush(fp);
}
}
} }
} // namespace extra2d } // namespace extra2d

View File

@ -0,0 +1,114 @@
#include <extra2d/utils/logger_module.h>
#include <extra2d/config/module_registry.h>
#include <extra2d/utils/logger.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace extra2d {
bool LoggerModuleConfig::loadFromJson(const void* jsonData) {
if (!jsonData) return false;
try {
const json& j = *static_cast<const json*>(jsonData);
if (j.contains("logLevel")) {
int level = j["logLevel"].get<int>();
if (level >= 0 && level <= 6) {
logLevel = static_cast<LogLevel>(level);
}
}
if (j.contains("consoleOutput")) {
consoleOutput = j["consoleOutput"].get<bool>();
}
if (j.contains("fileOutput")) {
fileOutput = j["fileOutput"].get<bool>();
}
if (j.contains("logFilePath")) {
logFilePath = j["logFilePath"].get<std::string>();
}
return true;
} catch (...) {
return false;
}
}
bool LoggerModuleConfig::saveToJson(void* jsonData) const {
if (!jsonData) return false;
try {
json& j = *static_cast<json*>(jsonData);
j["logLevel"] = static_cast<int>(logLevel);
j["consoleOutput"] = consoleOutput;
j["fileOutput"] = fileOutput;
j["logFilePath"] = logFilePath;
return true;
} catch (...) {
return false;
}
}
LoggerModuleInitializer::LoggerModuleInitializer()
: moduleId_(INVALID_MODULE_ID)
, initialized_(false) {
}
LoggerModuleInitializer::~LoggerModuleInitializer() {
if (initialized_) {
shutdown();
}
}
bool LoggerModuleInitializer::initialize(const IModuleConfig* config) {
if (initialized_) return true;
const LoggerModuleConfig* loggerConfig = dynamic_cast<const LoggerModuleConfig*>(config);
Logger::init();
if (loggerConfig) {
Logger::setLevel(loggerConfig->logLevel);
Logger::setConsoleOutput(loggerConfig->consoleOutput);
if (loggerConfig->fileOutput && !loggerConfig->logFilePath.empty()) {
Logger::setFileOutput(loggerConfig->logFilePath);
}
}
initialized_ = true;
E2D_LOG_INFO("Logger module initialized");
return true;
}
void LoggerModuleInitializer::shutdown() {
if (!initialized_) return;
E2D_LOG_INFO("Logger module shutting down");
Logger::shutdown();
initialized_ = false;
}
namespace {
static ModuleId s_loggerModuleId = INVALID_MODULE_ID;
struct LoggerModuleRegistrar {
LoggerModuleRegistrar() {
s_loggerModuleId = ModuleRegistry::instance().registerModule(
makeUnique<LoggerModuleConfig>(),
[]() -> UniquePtr<IModuleInitializer> {
auto initializer = makeUnique<LoggerModuleInitializer>();
initializer->setModuleId(s_loggerModuleId);
return initializer;
}
);
}
};
static LoggerModuleRegistrar s_registrar;
}
} // namespace extra2d