AzerothCore 自定义脚本(Custom Scripts)接入指南:CMake 注册、脚本加载器与模块化开发
【免费下载链接】azerothcore-wotlkComplete Open Source and Modular solution for MMO项目地址: https://gitcode.com/GitHub_Trending/az/azerothcore-wotlk
AzerothCore 为服务端开发者保留了独立的Custom脚本目录,让你可以在不污染官方源码的前提下,将自研的 NPC、法术、事件等脚本直接编译进worldserver。本指南基于 src/server/scripts/Custom/README.md 完整展开,并结合仓库内 CMake 宏定义、脚本加载器模板与真实脚本样例,说明如何从零注册一个自定义脚本、理解其编译链路,以及如何权衡"简单脚本"与"官方模块系统"两种方案。
Custom 目录的定位:被 Git 忽略的官方保留地
在 src/server/scripts 目录下,Custom是与World、Spells、EasternKingdoms、Northrend等官方脚本模块并列的一级目录。它的特殊之处体现在仓库根目录的 .gitignore 中:
/src/server/scripts/Custom/* !/src/server/scripts/Custom/README.md也就是说,该目录下除 README 外的所有内容都会被 Git 忽略。这意味着:
- 你新增的
.cpp、.h以及CMakeLists.txt不会进入版本控制,也不会在git status/git diff中干扰官方更新; - 官方升级、拉取上游代码时不会与你的私有脚本冲突;
- 该目录天然适合存放仅在本服使用的、不便公开的定制逻辑。
需要强调的是,该目录并非空目录——仓库内已预置一个最小示例 src/server/scripts/Custom/custom_script_loader.cpp,它定义了AddCustomScripts()空实现,作为你注册脚本时的参考起点。只要该文件存在,即使你尚未添加任何自定义脚本,构建系统也能正常工作。
为什么官方建议"优先使用模块系统"
README 中有一段醒目的建议(原文以**/!\ BTW**强调):强烈建议开发者使用模块系统(module system)来创建强大的自定义模块,而不是简单脚本。
这一建议与仓库的实际结构相互印证:
- modules 目录下提供了完整的模块化开发入口:modules/how_to_make_a_module.md 说明了模块创建流程(核心是运行 modules/create_module.sh),modules/CMakeLists.txt 展示了模块如何被独立收集、编译并链接进世界服务器;
- 模块可以自带配置、数据库 SQL、独立的目录结构与社区分享能力,而 Custom 脚本仅能提供纯 C++ 逻辑。
因此合理的决策路径是:临时性、规模小、无需共享的逻辑放进Custom脚本;具备完整功能、需要配置项或打算开源分享的功能,走模块系统。本指南剩余部分专注于 Custom 脚本的接入细节。
第一步:在 Custom 目录创建 CMakeLists.txt
完整的注册模板
在 src/server/scripts/Custom/ 下新建CMakeLists.txt,内容如下(README 明确说明"以下所有内容都是必需的",只需替换成你自己的脚本名):
set(scripts_STAT_SRCS ${scripts_STAT_SRCS} ${AC_SCRIPTS_DIR}/Custom/your_script.cpp ${AC_SCRIPTS_DIR}/Custom/your_script.h ) AC_ADD_SCRIPT_LOADER("Custom" "ScriptLoader.h") message(" -> Prepared: My custom scripts")各部分的作用:
| 片段 | 作用 |
|---|---|
set(scripts_STAT_SRCS ...) | 把自定义脚本的.cpp/.h追加到脚本库源码列表中,${AC_SCRIPTS_DIR}指向src/server/scripts目录,因此${AC_SCRIPTS_DIR}/Custom/...即定位到本目录下的源文件 |
AC_ADD_SCRIPT_LOADER("Custom" "ScriptLoader.h") | 核心注册宏:声明并登记名为Custom的加载函数(详见下文"宏的底层行为") |
message(...) | 在 CMake 配置阶段输出一行提示,便于确认你的脚本已被收集 |
宏的底层行为:AC_ADD_SCRIPT_LOADER 做了什么
该宏定义在 src/cmake/ac_macros.cmake 中,核心逻辑如下:
- 它以
"Custom"为参数,向全局列表AC_ADD_SCRIPTS_LIST追加条目AddCustomScripts();——即加载函数命名约定为Add${目录名}Scripts(); - 第二个参数
"ScriptLoader.h"会被登记进AC_ADD_SCRIPTS_INCLUDE全局列表,用于后续生成加载器时的头文件引用; - 宏还支持可变参数
ARGN:当传入额外模块名时,可把低优先级模块从脚本列表中原位移除并重新追加,实现加载顺序的精细控制(对应源码中-- ${script_dec} demands lower priority: ...的提示信息)。
这些全局列表最终流向 modules/CMakeLists.txt:宏收集到的AC_ADD_SCRIPTS_LIST会生成void AddCustomScripts();前向声明与调用语句,说明Custom 脚本与模块共享同一套加载器生成机制。
第二步:将脚本登记进 ScriptLoader.cpp
README 的第二个步骤是:打开ScriptLoader.cpp,翻到文件末尾,按同样的风格追加脚本声明与调用。
注意:ScriptLoader.cpp 是自动生成的
与直觉相反,src/server/scripts 目录下并不存在手写的ScriptLoader.cpp。它由模板 src/server/scripts/ScriptLoader.cpp.in.cmake 在 CMake 配置阶段自动生成,生成逻辑位于 src/server/scripts/CMakeLists.txt 的ConfigureScriptLoader函数中:
- 模板中的
@ACORE_SCRIPTS_FORWARD_DECL@被替换为一系列加载函数的前向声明; @ACORE_SCRIPTS_INVOKE@被替换为AddScripts()内部对每个加载函数的调用;- 生成文件输出到构建目录(如
gen_scriptloader/.../ScriptLoader.cpp),因此不要在源码树中手改生成文件——正确做法是通过 CMake 宏登记,然后重新运行 CMake 配置。
以官方模块 src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp 为参照,其结构为"先声明void AddSC_xxx();,再在AddEasternKingdomsScripts()中逐一调用"。Custom 目录的对应文件 src/server/scripts/Custom/custom_script_loader.cpp 同样遵循此模式:
// 示例:声明你的脚本加载函数 // void AddSC_your_script_name(); void AddCustomScripts() { // AddSC_your_script_name(); }注意其注释明确提示:加载函数名必须与Add${目录名}Scripts()约定一致,此处目录名为Custom,故为AddCustomScripts()。该函数经由宏登记后,会被自动生成的前向声明与AddScripts()调用链挂载,最终由世界服务器启动时通过ScriptMgr完成注册。
第三步:编写脚本本体并注册到 ScriptMgr
脚本文件的基本形态
以官方示例 src/server/scripts/World/npc_innkeeper.cpp 为模板,一个最简脚本通常包含两部分:
- 脚本类:继承
CreatureScript、SpellScript、PlayerScript等基类(见 src/server/scripts/ScriptPCH.h 中的预编译头引用),在构造函数中传入脚本名; - 加载函数:与声明一一对应的
AddSC_xxx()函数,内部执行new npc_xxx;完成实例化:
void AddSC_npc_innkeeper() { new npc_innkeeper; }对象构造时,脚本会经由 src/server/game/Scripting/ScriptDefines 下的ScriptRegistry<T>::AddScript(...)注册进全局ScriptMgr,之后即可被世界服务器按脚本名检索调用。
完整接入步骤总结
- 将
your_script.cpp(及可选的your_script.h)放入 src/server/scripts/Custom/; - 按上文模板创建
CMakeLists.txt并替换文件名; - 在 src/server/scripts/Custom/custom_script_loader.cpp 中声明并调用你的
AddSC_xxx(); - 重新运行 CMake 配置并重新编译世界服务器(静态编译模式下脚本直接链接进
worldserver,动态模式下则生成独立的脚本共享库,构建细节见 src/server/scripts/CMakeLists.txt 中的SCRIPTS构建选项)。
验证与排错
- CMake 配置输出:若你的
CMakeLists.txt生效,配置阶段应能看到-> Prepared: My custom scripts提示,同时脚本模块树(script graph)中会列出Custom模块及其链接方式(static / dynamic / disabled); Add${目录名}Scripts命名约定:加载函数名与目录名不一致是常见的链接报错来源,可对照 src/server/scripts/CMakeLists.txt 中"Add${LOCALE_SCRIPT_MODULE}Scripts()"的生成规则逐一核对;- Git 状态确认:由于
.gitignore忽略规则,git status中不应出现Custom目录下的新文件,这是该机制生效的直观信号。
结语:两条路径的取舍
本文完整复刻并深化了 src/server/scripts/Custom/README.md 的全部步骤:创建CMakeLists.txt收集源码、通过AC_ADD_SCRIPT_LOADER宏登记加载器、在自动生成的ScriptLoader.cpp链路中挂载AddCustomScripts(),最终由ScriptMgr注册生效。若你的需求只是少量私有逻辑,Custom 目录是零侵入的快捷通道;若你希望功能具备配置能力、可维护性与社区分享价值,则应转向 modules/how_to_make_a_module.md 描述的模块系统——两者共享同一套脚本注册机制,迁移成本也因此保持在较低水平。
【免费下载链接】azerothcore-wotlkComplete Open Source and Modular solution for MMO项目地址: https://gitcode.com/GitHub_Trending/az/azerothcore-wotlk
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考