AzerothCore 模块系统详解:从零开始开发自定义模块 原创
AzerothCore 作为最受欢迎的开源魔兽世界服务器模拟器之一,其最强大的特性之一就是模块化架构。与传统的直接修改核心代码不同,AzerothCore 的模块系统允许开发者在不触碰核心源码的前提下,扩展服务器功能、添加自定义脚本、甚至修改游戏逻辑。本文将深入解析 AzerothCore 模块系统的工作原理,并手把手教你从零开始开发自己的第一个模块。
一、什么是 AzerothCore 模块?
1.1 模块的定义
AzerothCore 模块是一个独立的 C++ 项目,它通过 CMake 子项目的方式编译链接到主服务器中。模块可以:
- 注册自定义脚本(Script):实现副本 Boss 逻辑、自定义 NPC、任务脚本等
- 添加钩子函数(Hooks):在游戏事件的关键节点注入自定义逻辑
- 扩展数据库表:通过模块自带的 SQL 迁移脚本管理自定义数据
- 添加控制台命令:注册 GM 命令用于管理和调试
1.2 模块 vs 直接修改核心
| 特性 | 直接修改核心 | 模块方式 |
|---|---|---|
| 核心更新兼容性 | ❌ 每次更新需手动合并冲突 | ✅ 独立维护,不受核心更新影响 |
| 代码隔离性 | ❌ 混入核心代码 | ✅ 完全独立的代码库 |
| 分享与分发 | ❌ 难以分享 | ✅ 可作为独立仓库发布 |
| 编译方式 | 修改源文件后全量编译 | CMake 自动链接,增量编译 |
| 维护成本 | 高 | 低 |
1.3 模块目录结构
一个标准的 AzerothCore 模块目录结构如下:
my-custom-module/
├── CMakeLists.txt # CMake 构建配置
├── include.sh # 模块元数据(名称、作者、版本)
├── sql/ # 数据库迁移脚本
│ └── base/
│ └── my_module_base.sql
├── src/ # C++ 源代码
│ ├── my_module.cpp # 主模块类
│ └── my_module_scripts.cpp # 自定义脚本
└── README.md # 文档说明
二、模块系统底层原理
2.1 CMake 集成机制
AzerothCore 在根 CMakeLists.txt 中通过 add_subdirectory() 自动扫描 modules/ 目录下的所有子项目:
# 核心 CMakeLists.txt 中的模块加载逻辑
if(USE_MODULES)
file(GLOB MODULES RELATIVE ${CMAKE_SOURCE_DIR}/modules modules/*)
foreach(MODULE ${MODULES})
if(EXISTS ${CMAKE_SOURCE_DIR}/modules/${MODULE}/CMakeLists.txt)
add_subdirectory(modules/${MODULE})
endif()
endforeach()
endif()
这意味着只要将模块文件夹放入 modules/ 目录并包含 CMakeLists.txt,核心编译系统就会自动发现并链接它。
2.2 模块生命周期
模块的生命周期由 ACModule 基类管理:
class ACModule {
public:
virtual ~ACModule() = default;
// 模块初始化时调用
virtual void OnStartup() {}
// 世界初始化完成时调用
virtual void OnWorldReady() {}
// 服务器关闭时调用
virtual void OnShutdown() {}
};
核心在启动流程的不同阶段调用这些方法,模块可以在此注册脚本、读取配置、初始化数据。
2.3 钩子系统(Hook System)
AzerothCore 提供了丰富的钩子函数,允许模块在游戏事件的关键节点注入逻辑。常用钩子包括:
| 钩子名称 | 触发时机 | 参数 |
|---|---|---|
OnPlayerLogin |
玩家登录完成 | Player* player |
OnPlayerLogout |
玩家开始登出 | Player* player |
OnCreatureKill |
怪物被击杀 | Unit* killer, Creature* creature |
OnSpellCast |
施法开始时 | Player* caster, Spell* spell |
OnAreaTrigger |
进入区域触发器 | Player* player, uint32 triggerId |
三、实战:开发第一个模块
3.1 创建模块骨架
我们以开发一个“登录欢迎系统”模块为例——当玩家登录时发送自定义欢迎消息,并记录登录次数。
include.sh
# 模块元数据
AC_MODULE_NAME="Login Welcome Module"
AC_MODULE_AUTHOR="YourName"
AC_MODULE_DESCRIPTION="Sends a welcome message on player login and tracks login count"
AC_MODULE_VERSION="1.0.0"
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(login_welcome_module)
# 收集源文件
file(GLOB_RECURSE SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
)
# 创建静态库
add_library(${PROJECT_NAME} STATIC ${SOURCES})
# 链接到 AzerothCore
target_link_libraries(${PROJECT_NAME} game)
# 包含目录
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/src/server/game
${CMAKE_SOURCE_DIR}/src/server/shared
)
# 安装 SQL 文件
install(
DIRECTORY sql/base DESTINATION modules/login_welcome_module/sql
FILES_MATCHING PATTERN "*.sql"
)
3.2 实现主模块类
src/login_welcome_module.h
#ifndef LOGIN_WELCOME_MODULE_H
#define LOGIN_WELCOME_MODULE_H
#include "Module.h"
class LoginWelcomeModule : public ACModule {
public:
static LoginWelcomeModule* instance();
void OnStartup() override;
void OnWorldReady() override;
private:
LoginWelcomeModule() = default;
};
#endif
src/login_welcome_module.cpp
#include "login_welcome_module.h"
#include "ScriptMgr.h"
#include "Player.h"
#include "Chat.h"
#include "DatabaseEnv.h"
LoginWelcomeModule* LoginWelcomeModule::instance() {
static LoginWelcomeModule instance;
return &instance;
}
void LoginWelcomeModule::OnStartup() {
TC_LOG_INFO("module.login_welcome", "Login Welcome Module loaded");
}
void LoginWelcomeModule::OnWorldReady() {
// 注册玩家脚本钩子
new LoginWelcomePlayerScript();
}
// 模块实例注册(必须)
void AddLoginWelcomeModuleScripts() {
LoginWelcomeModule::instance();
}
3.3 实现玩家脚本
src/login_welcome_scripts.cpp
#include "login_welcome_module.h"
#include "ScriptMgr.h"
#include "Player.h"
#include "Chat.h"
#include "DatabaseEnv.h"
class LoginWelcomePlayerScript : public PlayerScript {
public:
LoginWelcomePlayerScript() : PlayerScript("LoginWelcomePlayerScript") {}
void OnLogin(Player* player, bool /*firstLogin*/) override {
// 获取登录次数
uint32 loginCount = GetLoginCount(player->GetGUID());
// 发送欢迎消息
ChatHandler(player).PSendSysMessage(
"|cff00ff00================================|r"
);
ChatHandler(player).PSendSysMessage(
"|cff00ff00欢迎来到服务器, %s!|r", player->GetName().c_str()
);
ChatHandler(player).PSendSysMessage(
"|cff00ff00这是您的第 %d 次登录|r", loginCount + 1
);
ChatHandler(player).PSendSysMessage(
"|cff00ff00================================|r"
);
// 更新登录次数
UpdateLoginCount(player->GetGUID(), loginCount + 1);
}
private:
uint32 GetLoginCount(ObjectGuid guid) {
QueryResult result = CharacterDatabase.PQuery(
"SELECT login_count FROM custom_login_stats WHERE guid = %u",
guid.GetCounter()
);
if (result)
return (*result)[0].GetUInt32();
return 0;
}
void UpdateLoginCount(ObjectGuid guid, uint32 count) {
CharacterDatabase.PExecute(
"REPLACE INTO custom_login_stats (guid, login_count, last_login) "
"VALUES (%u, %u, UNIX_TIMESTAMP())",
guid.GetCounter(), count
);
}
};
3.4 数据库脚本
sql/base/custom_login_stats.sql
-- 登录统计表
CREATE TABLE IF NOT EXISTS `custom_login_stats` (
`guid` int(10) unsigned NOT NULL,
`login_count` int(10) unsigned NOT NULL DEFAULT 0,
`last_login` int(10) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`guid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Custom login statistics table';
3.5 编译与安装
# 1. 将模块放入 modules 目录
cd /path/to/azerothcore-wotlk
mkdir -p modules
git submodule add <your-module-repo-url> modules/login-welcome-module
# 2. 重新配置 CMake
cmake -B build -DCMAKE_BUILD_TYPE=Release
# 3. 编译
cd build
make -j$(nproc)
# 4. 运行数据库迁移
mysql -u root -p ac_world < ../modules/login-welcome-module/sql/base/custom_login_stats.sql
# 5. 启动服务器
./authserver & ./worldserver
四、高级模块开发技巧
4.1 配置文件支持
模块可以通过 mod-*.conf 文件提供可配置参数:
// 读取配置文件
bool enabled = sConfigMgr->GetBoolDefault("LoginWelcome.Enabled", true);
std::string welcomeMsg = sConfigMgr->GetStringDefault(
"LoginWelcome.Message",
"欢迎来到我们的服务器!"
);
对应的配置文件 mod-login-welcome.conf:
LoginWelcome.Enabled = 1
LoginWelcome.Message = "欢迎来到 AzerothCore 服务器!"
LoginWelcome.ShowLoginCount = 1
4.2 注册自定义 GM 命令
class LoginWelcomeCommandScript : public CommandScript {
public:
LoginWelcomeCommandScript() : CommandScript("LoginWelcomeCommandScript") {}
ChatCommandTable GetCommands() const override {
static ChatCommandTable commandTable = {
HandleResetLoginCount, // .login reset <player>
};
return commandTable;
}
static bool HandleResetLoginCount(ChatHandler* handler, const char* args) {
// 实现命令逻辑
return true;
}
};
4.3 模块间通信
多个模块可以通过全局事件总线进行通信:
// 发送事件
sScriptMgr->OnPlayerEvent(player, PLAYER_EVENT_LOGIN, count);
// 在另一个模块中监听
void OnPlayerEvent(Player* player, uint32 eventId, uint32 data) override {
if (eventId == PLAYER_EVENT_LOGIN) {
// 处理登录事件
}
}
五、最新 RBAC 更新与模块兼容性
在最近的 AzerothCore 更新中(2026年5月),核心修复了 RBAC 权限系统对 .whispers 命令的过滤逻辑。这一改动意味着模块在注册自定义 GM 命令时,需要特别注意权限配置:
5.1 RBAC 权限检查最佳实践
// 在模块命令中加入权限检查
static bool HandleMyCommand(ChatHandler* handler, const char* args) {
// 检查玩家是否有对应的 RBAC 权限
if (!handler->GetSession()->GetSecurity() >= RBAC_SEC_MODERATOR) {
handler->SendSysMessage("权限不足");
return false;
}
// 执行命令逻辑
return true;
}
5.2 注册自定义 RBAC 权限
-- 在模块 SQL 中注册自定义权限
INSERT INTO `rbac_permissions` (`id`, `name`) VALUES
(50000, 'Command: mymodule');
-- 关联到角色
INSERT INTO `rbac_linked_permissions` (`id`, `linkedId`) VALUES
(192, 50000); -- 192 = Sec Level 2 (Moderator)
六、模块开发常见陷阱
6.1 空指针检查
核心对象(如 Player、Creature)在某些钩子中可能为 nullptr:
void OnCreatureKill(Unit* killer, Creature* creature) override {
Player* player = killer->ToPlayer();
if (!player) return; // 必须检查!可能是宠物击杀
// ...
}
6.2 数据库事务
涉及多个表的操作应使用事务:
Transaction trans = CharacterDatabase.BeginTransaction();
trans->Append(...);
trans->Append(...);
CharacterDatabase.CommitTransaction(trans);
6.3 线程安全
WorldSession 钩子在特定上下文中执行,避免使用全局锁:
// ❌ 不好:使用互斥锁阻塞世界线程
std::lock_guard<std::mutex> lock(g_mutex);
// ✅ 好:使用异步队列
sWorld->AddTask([this, data]() { ProcessData(data); });
七、优秀开源模块推荐
| 模块名称 | 功能 | 仓库 |
|---|---|---|
| mod-eluna | Lua 脚本引擎 | github.com/azerothcore/mod-eluna |
| mod-anticheat | 反作弊系统 | github.com/azerothcore/mod-anticheat |
| mod-npcbeastmaster | NPC 宠物训练师 | github.com/azerothcore/mod-npcbeastmaster |
| mod-transmog | 幻化系统 | github.com/azerothcore/mod-transmog |
| mod-bg-reward | 战场奖励系统 | github.com/azerothcore/mod-bg-reward |
八、总结
AzerothCore 的模块系统是扩展服务器功能的最佳方式。通过模块化开发,你可以:
- ✅ 保持核心代码干净,方便跟随官方更新
- ✅ 独立测试和调试,降低开发风险
- ✅ 轻松分享和复用,构建社区生态
- ✅ 灵活的权限管理,与 RBAC 系统无缝集成
无论你是想添加自定义 NPC、实现新的副本机制,还是开发独特的游戏系统,模块系统都为你提供了强大的基础。开始动手吧!
本文基于 AzerothCore 最新版本编写,适用于 2026 年 5 月后的主分支。