Skip to content
全文约 1501 字 预计阅读 5 分钟

架构概览

NOTE

以下内容取自 LOICollectionA 1.15.x 的代码结构,对于后续版本可能会有所不同。

LOICollectionA 是一个基于 LeviLamina 的 C++ 插件(NativeMod),整体采用微内核架构:核心只负责配置加载、服务注册与模块调度,所有功能以 模块(Module) 为单位独立实现,可在配置文件中按需开关。

插件入口与生命周期

插件入口位于 src/LOICollectionA/LOICollectionA.cpp,通过 LL_REGISTER_MOD 注册:

cpp
LL_REGISTER_MOD(LOICollection::A, LOICollection::A::getInstance());

A 继承 LeviLamina 的 ll::mod::NativeMod,四个阶段依次驱动所有模块:

阶段核心动作对模块的调用
load计算配置版本 → 生成/合并/加载 config.json → 注册全局服务 → 初始化数据库目录与语言目录逐个调用模块 load()
enable根据 ConsoleLanguage 设置默认语言 → 编译 LOICollectionAPI 脚本 →逐个调用模块 registry()
disable逐个调用模块 unregistry()
unload逐个调用模块 unload()

TIP

模块的四个阶段并非"开关"关系:load/unload 负责资源生命周期(数据库、日志、路径),registry/unregistry 负责运行时注册(命令、事件、UI)。详见 模块开发指南

模块系统

模块框架位于 src/LOICollectionA/include/,包含三个核心类:

ModuleBase(模块基类)

所有模块继承 LOICollection::modules::ModuleBase,需要实现四个纯虚方法:

cpp
class ModuleBase {
public:
    virtual std::string getName() = 0;                 // 模块名(注册名)
    virtual ModulePriority getPriority() = 0;          // 模块优先级
    virtual ll::Expected<bool> load() = 0;             // 资源加载
    virtual ll::Expected<bool> unload() = 0;           // 资源释放
    virtual ll::Expected<bool> registry() = 0;         // 运行时注册
    virtual ll::Expected<bool> unregistry() = 0;       // 运行时注销
};

AutoRegister(自动注册)

模块只需继承 modules::AutoRegister<Derived>,其静态初始化会在程序启动时自动调用 ModManager::registry(getShared(), name, priority),无需手动注册:

cpp
class BlacklistPlugin : public std::enable_shared_from_this<BlacklistPlugin>,
                        public modules::ModuleBase,
                        public modules::AutoRegister<BlacklistPlugin> {
    // ...
};

ModManager(模块管理器)

方法说明
registry(shared_ptr, name, priority)注册模块,priority 默认 Normal
unregistry(name)注销模块
getModule(name)按名字获取模块实例
mods()获取全部模块名列表(按优先级排序)

ModulePriority 枚举:Highest(0)、High(1)、Normal(2)、Low(3)、Lowest(4)。优先级影响模块的加载/注册顺序。

服务容器与依赖注入

模块之间不直接依赖,而是通过 ServiceProvider / ServiceContainer类型 + 名字 注册与获取服务:

cpp
// 注册
ServiceProvider::getInstance().registerInstance<TService>(instance, name);

// 获取
auto svc = ServiceProvider::getInstance().getService<TService>(name);

插件启动时注册的全局服务:

类型名字说明
ReadOnlyWrapper<Config::C_Config>"Config"只读配置(见下)
std::string"DataPath"插件数据目录(plugins/LOICollectionA/data
std::string"GuiPath"GUI 目录(plugins/LOICollectionA/gui
std::string"ConfigPath"配置目录(plugins/LOICollectionA/config
SQLiteStorage"SettingsDB"全局设置数据库(data/settings.db

NOTE

配置以 ReadOnlyWrapper<Config::C_Config> 注册,模块只能读取配置,无法修改。这是有意设计:配置只在启动时读取一次,运行期修改需要重启服务器。

配置系统

  • 配置结构定义在 src/LOICollectionA/ConfigPlugin.h,共 105 个配置键(嵌套结构 C_ConfigC_ServerConfigC_ServerPlugins / C_ServerProtableTool
  • 配置版本由插件版本号哈希生成(SynchronousPluginConfigVersion
  • 升级插件时,代码中的默认配置通过 MergePatch 递归合并进现有 config.json:新增键自动补入,已有键保留用户取值
  • 详细配置项说明见 数据文件

目录结构

txt
src/LOICollectionA/
├─ LOICollectionA.cpp / .h   # 插件入口(A 类)
├─ ConfigPlugin.h / .cpp     # 配置结构定义与加载
├─ base/                     # 基础设施:ServiceContainer、ServiceProvider、
│                            #   ReadOnlyWrapper、LRUKCache、Throttle、ScopeGuard
├─ data/                     # 数据层:SQLiteStorage(SQLite 连接池)、JsonStorage
├─ frontend/                 # LCUI 脚本引擎:Lexer、Parser、SemanticAnalyzer、
│   │                        #   AST、Callback(原生绑定注册表)、ir/(编译器、VM)
│   └─ builtin/              # 脚本内置实现:Math/Format/String 函数、
│                            #   mc/server 命令、ui/(表单与 Observable 原生类)
├─ include/                  # 对外公开头文件(安装后随插件分发)
│   ├─ ModuleBase.h / ModManager.h / ModulePriority.h
│   ├─ CallbackUtils.h       # LOICollectionAPI 变量注册入口
│   ├─ form/GUIManager.h     # GUI 管理器
│   ├─ server/Events/        # 自定义事件类型(网络包、玩家计分板变更等)
│   └─ server/Plugins/       # 各模块公开接口(BlacklistPlugin.h 等)
├─ modules/                  # 模块实现(client/ 与 server/ 按平台隔离)
├─ utils/                    # 工具:I18nUtils、MathUtils、mc-server 工具集
tests/                       # gtest 测试(common/ 跨平台、server/、client/)

双平台支持

插件同时支持 server(服务端)与 client(客户端)两种目标,通过 xmake 的 target_type 选项区分:

  • 编译宏:LL_PLAT_S(服务端)/ LL_PLAT_C(客户端)
  • 源码隔离:modules/server/*modules/client/* 按目标互斥编译
  • 头文件隔离:include/server/*include/client/* 按目标从分发清单中移除
  • 构建方式详见 构建与测试

数据层

所有持久化数据通过 SQLiteStorage(默认,支持读写连接池与事务)或 JsonStorage(简单 JSON 文件)访问,二者都返回 ll::Expected<T> 以支持链式错误处理。用法详见 模块开发指南 的"数据层"章节。

LOICollectionA · 开箱即用的 LeviLamina 多功能插件集