Extra2D/docs/module_system.md

506 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Extra2D 模块系统
## 概述
Extra2D 采用模块化架构设计,所有核心功能都通过模块系统管理。模块系统提供:
- **统一的生命周期管理**:初始化、关闭、依赖处理
- **优先级排序**:确保模块按正确顺序初始化
- **配置驱动**:每个模块可独立配置
- **解耦设计**Application 只协调模块,不直接管理任何依赖
## 架构图
```
┌─────────────────────────────────────────────────────────────┐
│ Application │
│ (只负责协调模块,不直接管理任何依赖) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ ModuleRegistry │
│ (模块注册表,管理所有模块的生命周期) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Logger Module │ │ Config Module │ │Platform Module│
│ (日志系统) │ │ (配置管理) │ │ (平台初始化) │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ │
└─────────────────────┼─────────────────────┘
┌───────────────┐
│ Window Module │
│ (窗口管理) │
└───────────────┘
┌───────────────┐
│ Render Module │
│ (渲染系统) │
└───────────────┘
```
## 模块优先级
模块按优先级从小到大初始化,关闭时逆序执行:
| 优先级值 | 枚举名称 | 用途 | 模块示例 |
|---------|---------|------|---------|
| 0 | `Core` | 核心模块,最先初始化 | Logger, Config, Platform, Window |
| 50 | `Input` | 输入处理 | Input |
| 100 | `Graphics` | 图形渲染 | Render |
| 200 | `Audio` | 音频系统 | Audio |
| 300 | `Physics` | 物理系统 | Physics |
| 400 | `Gameplay` | 游戏逻辑 | Gameplay |
| 500 | `UI` | 用户界面 | UI |
## 核心接口
### IModuleConfig
模块配置接口,定义模块的元数据和配置:
```cpp
class IModuleConfig {
public:
virtual ~IModuleConfig() = default;
// 模块信息
virtual ModuleInfo getModuleInfo() const = 0;
// 配置管理
virtual std::string getConfigSectionName() const = 0;
virtual bool validate() const = 0;
virtual void resetToDefaults() = 0;
virtual bool loadFromJson(const void* jsonData) = 0;
virtual bool saveToJson(void* jsonData) const = 0;
// 平台约束
virtual void applyPlatformConstraints(PlatformType platform) {}
};
```
### IModuleInitializer
模块初始化器接口,管理模块的生命周期:
```cpp
class IModuleInitializer {
public:
virtual ~IModuleInitializer() = default;
// 模块标识
virtual ModuleId getModuleId() const = 0;
virtual ModulePriority getPriority() const = 0;
virtual std::vector<ModuleId> getDependencies() const = 0;
// 生命周期
virtual bool initialize(const IModuleConfig* config) = 0;
virtual void shutdown() = 0;
virtual bool isInitialized() const = 0;
};
```
### ModuleRegistry
模块注册表,管理所有模块:
```cpp
class ModuleRegistry {
public:
static ModuleRegistry& instance();
// 注册模块
ModuleId registerModule(
UniquePtr<IModuleConfig> config,
ModuleInitializerFactory factory
);
// 获取模块
IModuleConfig* getModuleConfig(ModuleId id);
IModuleInitializer* getInitializer(ModuleId id);
// 初始化顺序
std::vector<ModuleId> getInitializationOrder() const;
// 查询
bool hasModule(const std::string& name) const;
std::vector<std::string> getModuleNames() const;
};
```
## 创建新模块
### 步骤 1定义配置类
```cpp
// my_module.h
class MyModuleConfig : public IModuleConfig {
public:
int someSetting = 42;
std::string somePath;
ModuleInfo getModuleInfo() const override {
ModuleInfo info;
info.name = "MyModule";
info.version = "1.0.0";
info.priority = ModulePriority::Gameplay;
info.enabled = true;
return info;
}
std::string getConfigSectionName() const override {
return "my_module";
}
bool validate() const override {
return someSetting > 0;
}
void resetToDefaults() override {
someSetting = 42;
somePath.clear();
}
bool loadFromJson(const void* jsonData) override;
bool saveToJson(void* jsonData) const override;
};
```
### 步骤 2定义初始化器
```cpp
// my_module.h
class MyModuleInitializer : public IModuleInitializer {
public:
ModuleId getModuleId() const override { return moduleId_; }
ModulePriority getPriority() const override { return ModulePriority::Gameplay; }
std::vector<ModuleId> getDependencies() const override { return {}; }
bool initialize(const IModuleConfig* config) override {
if (initialized_) return true;
const MyModuleConfig* cfg = dynamic_cast<const MyModuleConfig*>(config);
if (!cfg) return false;
// 执行初始化逻辑...
initialized_ = true;
E2D_LOG_INFO("MyModule initialized");
return true;
}
void shutdown() override {
if (!initialized_) return;
// 执行清理逻辑...
initialized_ = false;
E2D_LOG_INFO("MyModule shutdown");
}
bool isInitialized() const override { return initialized_; }
void setModuleId(ModuleId id) { moduleId_ = id; }
private:
ModuleId moduleId_ = INVALID_MODULE_ID;
bool initialized_ = false;
};
```
### 步骤 3注册模块
```cpp
// my_module.cpp
#include <extra2d/config/module_registry.h>
namespace extra2d {
static ModuleId s_myModuleId = INVALID_MODULE_ID;
ModuleId get_my_module_id() {
return s_myModuleId;
}
void register_my_module() {
if (s_myModuleId != INVALID_MODULE_ID) return;
s_myModuleId = ModuleRegistry::instance().registerModule(
makeUnique<MyModuleConfig>(),
[]() -> UniquePtr<IModuleInitializer> {
auto initializer = makeUnique<MyModuleInitializer>();
initializer->setModuleId(s_myModuleId);
return initializer;
}
);
}
// 自动注册
namespace {
struct MyModuleAutoRegister {
MyModuleAutoRegister() {
register_my_module();
}
};
static MyModuleAutoRegister s_autoRegister;
}
} // namespace extra2d
```
### 步骤 4在 Application 中使用
```cpp
// application.cpp
#include <extra2d/my_module.h>
bool Application::init(const AppConfig& config) {
// 注册模块
register_my_module();
// 初始化所有模块
return initModules();
}
```
## 内置模块
### Logger 模块
**职责**:管理日志系统初始化和关闭
**配置**
```cpp
LoggerModuleConfig config;
config.logLevel = LogLevel::Info;
config.consoleOutput = true;
config.fileOutput = true;
config.logFilePath = "app.log";
```
**平台支持**
- Windows彩色控制台输出
- Linux/macOSANSI 彩色输出
- Switchlibnx console
---
### Config 模块
**职责**:管理 ConfigManager 和应用配置
**配置**
```cpp
ConfigModuleConfig config;
config.configPath = "config.json";
config.appConfig = AppConfig::createDefault();
```
---
### Platform 模块
**职责**:平台检测和平台特定初始化
**平台特定操作**
- Switch初始化 romfs 和 socket
**配置**
```cpp
PlatformModuleConfig config;
config.targetPlatform = PlatformType::Auto; // 自动检测
```
---
### Window 模块
**职责**:窗口创建和后端管理
**后端支持**
- SDL2跨平台
- GLFW跨平台
- Switch原生
**配置**
```cpp
WindowModuleConfig config;
config.backend = "sdl2";
config.windowConfig.title = "My App";
config.windowConfig.width = 1280;
config.windowConfig.height = 720;
config.windowConfig.vsync = true;
```
---
### Render 模块
**职责**:渲染器初始化和管理
**配置**
```cpp
RenderModuleConfig config;
config.backend = BackendType::OpenGL;
config.vsync = true;
config.targetFPS = 60;
config.multisamples = 4;
```
## 模块依赖
模块可以声明依赖关系:
```cpp
std::vector<ModuleId> getDependencies() const override {
return { get_window_module_id(), get_config_module_id() };
}
```
ModuleRegistry 会自动解析依赖并按正确顺序初始化。
## 配置文件格式
模块配置使用 JSON 格式:
```json
{
"logger": {
"logLevel": 2,
"consoleOutput": true,
"fileOutput": false
},
"window": {
"backend": "sdl2",
"title": "My Application",
"width": 1280,
"height": 720,
"vsync": true
},
"render": {
"backend": "opengl",
"targetFPS": 60,
"multisamples": 4
}
}
```
## 最佳实践
### 1. 单一职责
每个模块只负责一个明确的功能领域:
```
✅ Logger Module → 只管理日志
✅ Window Module → 只管理窗口
❌ Game Module → 管理输入、渲染、音频(职责过多)
```
### 2. 依赖最小化
模块应尽量减少对其他模块的依赖:
```cpp
// 好的做法:通过接口获取信息
std::vector<ModuleId> getDependencies() const override {
return { get_window_module_id() }; // 只依赖窗口
}
// 不好的做法:依赖过多
std::vector<ModuleId> getDependencies() const override {
return {
get_window_module_id(),
get_config_module_id(),
get_logger_module_id(),
get_render_module_id()
};
}
```
### 3. 延迟初始化
资源在需要时才初始化,不是在构造函数中:
```cpp
bool initialize(const IModuleConfig* config) override {
if (initialized_) return true;
// 在这里初始化资源
resource_ = createResource();
initialized_ = true;
return true;
}
```
### 4. 安全关闭
关闭时要检查状态,避免重复关闭:
```cpp
void shutdown() override {
if (!initialized_) return; // 避免重复关闭
// 清理资源
resource_.reset();
initialized_ = false;
}
```
### 5. 使用日志
在关键操作处添加日志:
```cpp
bool initialize(const IModuleConfig* config) override {
E2D_LOG_INFO("Initializing MyModule...");
if (!doSomething()) {
E2D_LOG_ERROR("Failed to do something");
return false;
}
E2D_LOG_INFO("MyModule initialized successfully");
return true;
}
```
## 调试
### 查看模块初始化顺序
```cpp
auto order = ModuleRegistry::instance().getInitializationOrder();
for (ModuleId id : order) {
auto* config = ModuleRegistry::instance().getModuleConfig(id);
if (config) {
auto info = config->getModuleInfo();
E2D_LOG_INFO("Module: {} (priority: {})",
info.name, static_cast<int>(info.priority));
}
}
```
### 检查模块状态
```cpp
auto* initializer = ModuleRegistry::instance().getInitializer(moduleId);
if (initializer && initializer->isInitialized()) {
E2D_LOG_INFO("Module is initialized");
}
```
## 示例
完整示例请参考:
- [examples/basic/main.cpp](../../examples/basic/main.cpp) - 基础示例
- [Extra2D/src/platform/window_module.cpp](../../Extra2D/src/platform/window_module.cpp) - Window 模块实现
- [Extra2D/src/graphics/render_module.cpp](../../Extra2D/src/graphics/render_module.cpp) - Render 模块实现