简介:AzerothCore-WotLK 是面向 MMORPG 服务端搭建与游戏逻辑二次开发的开源模块化框架,基于《魔兽世界:巫妖王之怒》版本,适合研究模拟器运行机制、服务端架构与功能扩展的开发者及游戏爱好者。它把账号、世界、脚本与数据库等环节纳入可定制体系,读者可据此理解 MMO 服务端的分层组织,并尝试添加、裁剪或改写玩法模块,而不必从零搭建底层。资源包约 138.19MB,文件总数统计为 0,类型明细暂无数据,实际内容以项目 master 源码及配套资源为主,下载后可按目录结构梳理核心代码与配置素材。页面显示已有 1403 人学习,说明该框架在开源游戏服务器社区具有一定关注度。整体更适合具备一定 C++、数据库与 Linux 编译基础的中高级读者,用于学习服务端框架、模块化设计和私有服务器实验,而非直接获得成品游戏。
1. 环境清单:azerothcore-wotlk 这套 MMO 服务端第一次装最容易错在哪
第一次在云主机上 clone 完 azerothcore-wotlk,直接make -j$(nproc),跑到八成被内核 OOM killer 干掉——这是我装这套模块化 MMO 服务端最常遇到的翻车。它不是一个单进程小服务,而是一整条 game-server 链:authserver 管登录鉴权,worldserver 承载世界逻辑,数据拆成 auth/characters/world 三个库,地图与寻路数据还得从 3.3.5a 客户端里现场提。装得好,一台机器能同时跑世界、副本、模块;装不好,要么编译阶段躺下,要么起来卡在角色列表不动。下面按「依赖 → 编译 → 建库 → 提取配置 → 排错 → 验证」走,适合第一次搭 MMORPG 服务端的开发者,也适合在 TrinityCore、MangOS 系之间来回切的老手。
2. 依赖安装:Ubuntu 22.04 上那几条 apt 与版本对照
2.1 系统版本怎么选,编译器卡在哪条线
这套 core 用 C++17 写,最低要求 GCC 11 或 Clang 10 以上,CMake 3.16 以上,Boost 1.74 以上,OpenSSL 1.1.1 以上(3.0 更稳),MySQL 8.0 或 MariaDB 10.6 以上。这个组合不是我拍脑袋列的,是踩过一轮之后固定下来的:Ubuntu 20.04 自带 GCC 9,编不过;Ubuntu 24.04 自带 GCC 13、Boost 1.83,能编,但某些第三方模块的 CMakeLists 还按老版本写,时不时给你来个找不到库。折中下来,22.04 LTS 是最省心的那一档,apt 源里的版本刚好全部踩在及格线上,不用自己编 Boost 和 OpenSSL。
我把三个常用发行版摊开对比过,最后固定用 22.04:
| 系统 | GCC 默认版本 | Boost 默认版本 | CMake | 结论 |
|---|---|---|---|---|
| Ubuntu 20.04 | 9.4 | 1.71 | 3.16 | GCC 版本不够,需要手动升级 |
| Ubuntu 22.04 | 11.4 | 1.74 | 3.22 | 全部达标,推荐 |
| Ubuntu 24.04 | 13.3 | 1.83 | 3.28 | 主程序可用,第三方模块偶发不兼容 |
内存是另一条硬线。编译阶段单个 translation unit 的峰值内存能到 1.5~2 GB,make -j开满核心数时,4 GB 的机器几乎必然被 OOM。我的经验值是:4 核 8 GB 用-j4比较稳,8 核 16 GB 用-j8,实在只有 4 GB 就加 4 GB swap 再上-j2。磁盘留 30 GB,源码加编译产物加客户端提取出来的数据,差不多就是这么个数。
2.2 一条 apt 把编译链装齐
# 更新索引,先确认系统是 22.04 lsb_release -a sudo apt update sudo apt install -y \ git cmake make build-essential \ gcc g++ \ libboost-all-dev \ libssl-dev \ libmysqlclient-dev \ libreadline-dev \ libncurses-dev \ libbz2-dev \ zlib1g-dev \ mysql-server \ unzip wget这段命令里,build-essential带进来的是 make、dpkg-dev 这一套;libboost-all-dev一次装全 Boost 的头文件和库,虽然体积大(1 GB 左右),但省得后面编译时一个个补libboost-system-dev、libboost-thread-dev、libboost-filesystem-dev、libboost-program-options-dev、libboost-iostreams-dev——这几个是 core 实际链接的模块,缺一个 cmake 阶段就报Could NOT find Boost。libmysqlclient-dev提供 MySQL 客户端头文件和libmysqlclient.so,编译时链接、运行时加载都靠它。libssl-dev走的是 OpenSSL 3.0,负责登录握手和密码哈希。libreadline-dev和libncurses-dev是给 worldserver 的交互式控制台用的,没有它们 worldserver 起来后就没有命令行提示符,只能盲跑。
提示:Ubuntu 22.04 的
libmysqlclient-dev对应 MySQL 8.0.3x,和 apt 装的mysql-server是同一套 ABI,别自己另外下 MySQL 官方的 tar 包混装,libmysqlclient.so.21和.so.22的 soname 一对不上,运行期就是error while loading shared libraries。
装完验证一下版本,不要凭感觉:
gcc --version | head -1 # 期望 gcc (Ubuntu 11.4.0-...) 11.4.0 cmake --version | head -1 # 期望 cmake version 3.22.x dpkg -s libboost-dev | grep Version # 期望 1.74.x mysql --version # 期望 mysql Ver 8.0.x2.3 MySQL 8.0 装完必须先处理的三件事
MySQL 8 默认的认证插件是caching_sha2_password,core 用libmysqlclient连它是没问题的,真正的坑在别处:默认只允许root@localhost走 socket 登录,你如果用mysql -h 127.0.0.1 -u root -p会直接被拒。先改 root 的登录方式,再建业务账号。
sudo mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的root密码';" sudo mysql -e "FLUSH PRIVILEGES;"第二件事是字符集。world 库里的任务文本、NPC 名字、物品描述大量是 UTF-8 多字节,库默认字符集要是latin1,后面导入 SQL 会出现一堆????,而且这个错误在导入阶段不报错,等玩家看到乱码才发现就晚了。建库时统一写utf8mb4:
CREATE DATABASE acore_world DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE DATABASE acore_characters DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE DATABASE acore_auth DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;第三件是时区表。worldserver 里有大量基于时间戳的刷新逻辑(日常重置、副本 CD),MySQL 的time_zone表没导入时,某些版本会报Unknown or incorrect time zone,进程直接退出。补一条:
sudo mysql_tzinfo_to_sql /usr/share/zoneinfo | sudo mysql mysql三件事做完再往下走,后面数据库导入和 core 启动都会顺很多。
3. CMake 编译:参数取舍、make -j 并行度和模块挂载
3.1 生成构建目录:每个 -D 参数到底在改什么
源码拉下来之后不要就地 cmake,单独开一个 build 目录,源码目录保持干净,后面切换分支或者重编都方便。
cd ~ git clone https://github.com/azerothcore/azerothcore-wotlk.git cd azerothcore-wotlk mkdir build && cd build cmake .. \ -DCMAKE_INSTALL_PREFIX=/home/acore/azeroth-server \ -DCMAKE_BUILD_TYPE=Release \ -DTOOLS_BUILD=all \ -DSCRIPTS=static \ -DMODULES=static \ -DWITH_WARNINGS=1CMAKE_INSTALL_PREFIX决定make install把可执行文件、配置模板、SQL 文件铺到哪。我一般单独建一个/home/acore/azeroth-server,好处是将来升级 core 时可以直接整个目录替换,不影响源码目录,也不影响数据库。CMAKE_BUILD_TYPE=Release一定要显式写,不写的话有些 CMake 版本默认走空 build type,编译出来的二进制没有优化,worldserver 启动时加载几万个 NPC 模板会慢一大截。
TOOLS_BUILD=all是这几项里最容易被忽略的。它负责编译map_extractor、vmap4_extractor、vmap4_assembler、mmaps_generator四个工具,不设成all(或至少设成包含这些工具的选项),后面第 4 章的数据提取根本没法做,你只能去别处找别人提好的数据,版本还未必对得上。
SCRIPTS=static和MODULES=static是两个独立的开关。SCRIPTS管的是 core 自带的脚本模块(副本机制、BOSS AI、任务脚本),static表示编译进 worldserver 二进制,启动快、部署简单;dynamic编成.so,热替换方便但多一层加载失败的风险。MODULES管的是你在modules/目录里自己挂的第三方模块,比如自动平衡、幻化、传送 NPC 这类,static同样是编进主程序。
注意:这两个选项都设成
static之后,每次改模块代码或加新模块,都必须重跑一次 cmake 再make,不能只make——CMake 的模块发现逻辑在 configure 阶段跑,只 make 的话新模块根本不会进编译列表。
3.2 make -j 开多大,install 之后目录长什么样
# 并行度按内存来,不要按核心数来 nproc # 先看核心数 free -g # 再看可用内存,8G 以内建议 -j4 make -j4 # 4 核 8G 的机器 # make -j8 # 8 核 16G 的机器 sudo make install第一次全量编译大概 30~60 分钟,取决于机器。make install完成后,安装目录下会生成这么几个关键位置:
| 路径 | 内容 | 用途 |
|---|---|---|
bin/ | authserver、worldserver | 两个主程序 |
bin/ | map_extractor、vmap4_extractor等 | 数据提取工具 |
etc/ | worldserver.conf.dist、authserver.conf.dist | 配置模板 |
data/ | sql/目录 | 建库脚本与更新脚本 |
data/ | dbc/、maps/、vmaps/、mmaps/ | 提取后的数据落点 |
sudo make install如果安装目录在 home 下其实不需要 sudo,加了反而可能把文件属主改成 root,后面 worldserver 写日志时权限不足。我一般直接make install,只有装到/opt这类系统目录才加 sudo。
3.3 模块怎么挂进编译:modules 目录与 hacktoberfest 那波新模块
这套 core 的模块化是真模块化:所有第三方模块放在源码根目录的modules/下,每个模块一个文件夹,里面自带CMakeLists.txt,根 CMake 会自动扫描并把它们挂进构建流程。挂载方式是在 modules 目录里 clone,而不是改主 CMakeLists:
cd ~/azerothcore-wotlk/modules # 以自动平衡模块为例,具体仓库按你实际用的填 git clone <模块仓库地址> mod-autobalance # 回到 build 目录重新 configure,让 cmake 重新扫描 modules/ cd ../build cmake .. -DCMAKE_INSTALL_PREFIX=/home/acore/azeroth-server \ -DTOOLS_BUILD=all -DSCRIPTS=static -DMODULES=static make -j4 && make install每次新加模块都要走一遍这个「clone → 重新 cmake → make → install」。编译输出里会多出一段Adding module: mod-autobalance,看到这行才算挂上了。如果没看到,多半是模块目录层级不对——必须是modules/mod-xxx/CMakeLists.txt,多套一层文件夹 cmake 就扫不到。
这套 game-server 生态里,TrinityCore、MangOS、SunwellCore 几个分支互相借鉴了很多年,脚本层从早期的 scriptdev2 一路演进过来,模块接口也换过几轮。所以从别处拿来的老模块,很可能按旧版接口写的,编译时报一堆undefined reference,这不是你环境的问题,是模块本身没跟上 core 的更新。每年 hacktoberfest 期间仓库会涌入一大批新模块 PR,尝鲜可以,生产环境还是要挑那些有近期提交记录的。
4. 数据库与配置:三个库的导入顺序、worldserver.conf 必改项、数据提取三件套
4.1 建库建用户:create_mysql.sql 到底做了什么
安装目录下的data/sql/create/create_mysql.sql是官方给的账号初始化脚本,它做的事很有限:删掉旧的acore用户、重建、把三个库的权限一次性授完。
cd /home/acore/azeroth-server/data/sql/create sudo mysql -u root -p < create_mysql.sql脚本内容大致等价于:
DROP USER IF EXISTS 'acore'@'localhost'; CREATE USER 'acore'@'localhost' IDENTIFIED BY 'acore'; GRANT ALL PRIVILEGES ON `acore_world`.* TO 'acore'@'localhost' WITH GRANT OPTION; GRANT ALL PRIVILEGES ON `acore_characters`.* TO 'acore'@'localhost' WITH GRANT OPTION; GRANT ALL PRIVILEGES ON `acore_auth`.* TO 'acore'@'localhost' WITH GRANT OPTION; FLUSH PRIVILEGES;注意两点。第一,它不建库,三个acore_*数据库必须先按 2.3 节手动建好,否则 GRANT 会失败。第二,默认密码是acore,host 限死localhost。如果你的 MySQL 和应用不在同一台机器,或者通过127.0.0.1走 TCP 连接,localhost这个 host 匹配的是 socket 连接,可能匹配不上——稳妥做法是再补一条'acore'@'127.0.0.1'或'acore'@'%'的用户授权。生产环境请把默认密码换掉,这个acore/acore组合是全网扫描器的重点目标。
4.2 base 与 updates 的导入顺序不能反
三个库的初始数据分两块:data/sql/base/是建表加初始数据,data/sql/updates/是历次版本变更的增量补丁。顺序必须是先 base 后 updates,反过来会有一堆Table already exists。
手动导入的顺序是 auth → characters → world:
cd /home/acore/azeroth-server sudo mysql -u acore -p acore_auth < data/sql/base/db_auth/auth.sql sudo mysql -u acore -p acore_characters < data/sql/base/db_characters/characters.sql sudo mysql -u acore -p acore_world < data/sql/base/db_world/world.sqlworld 库的 base 文件是所有里最大的,导入过程几分钟到十几分钟不等,中途不要 Ctrl+C,导一半的表结构后面补起来非常麻烦。
updates 有两种走法。一是让 worldserver 启动时自动执行,配置里Updates.EnableDatabases = 7、Updates.AutoSetup = 1,进程起来后会逐条扫data/sql/updates/下的文件并按文件名哈希记录到updates表,执行过的不会重复执行。二是用安装目录里带的 db_assembler 脚本,交互式菜单里选「Create and populate」,它会自动按顺序把 base 和 updates 一起灌进去,适合第一次装、想省事的情况。
cd /home/acore/azeroth-server/apps/db_assembler bash db_assembler.sh # 菜单里选 Create and populate the databases提示:自动更新虽好,但第一次导入建议先手动走 base,再用自动更新补 updates。原因是一旦自动更新在中途失败,
updates表里可能已经记了半条记录,下次启动会跳过它,导致表结构和 core 期望的对不上,排查起来非常费劲。
4.3 worldserver.conf 里必须改的六项
配置模板在etc/下,复制一份去掉.dist后缀再改,别直接改模板,升级时会被覆盖。
cd /home/acore/azeroth-server/etc cp worldserver.conf.dist worldserver.conf cp authserver.conf.dist authserver.conf数据库连接串的格式是"主机;端口;用户;密码;库名",用分号分隔,不是冒号也不是逗号,这里写错是最常见的启动失败原因:
| 配置项 | 建议值 | 说明 |
|---|---|---|
LoginDatabaseInfo | "127.0.0.1;3306;acore;密码;acore_auth" | 登录库 |
WorldDatabaseInfo | "127.0.0.1;3306;acore;密码;acore_world" | 世界库 |
CharacterDatabaseInfo | "127.0.0.1;3306;acore;密码;acore_characters" | 角色库 |
DataDir | "/home/acore/azeroth-server/data" | 指向含 dbc/maps/vmaps/mmaps 的父目录 |
WorldServerPort | 8085 | 世界服端口 |
BindIP | 0.0.0.0 | 只本机玩可写 127.0.0.1 |
DataDir是最容易指错的一项。它要的是父目录,不是maps目录本身。写对了,DataDir/dbc、DataDir/maps、DataDir/vmaps、DataDir/mmaps四个子目录都能被找到;写错一层,启动时就是Map file 'xxx' does not exist,报错信息不会告诉你它到底在找哪个路径。
authserver.conf里只需要改一项LoginDatabaseInfo,和上面保持同一个库同一套账号密码就行。
4.4 客户端数据提取:map、vmap、mmap 的生成顺序
这一步是整个安装里最耗时的,也是最容易半途而废的。四个工具必须按固定顺序跑,前一个的产物是后一个的输入:
cd /home/acore/azeroth-server/bin # 1. 提取 dbc 和 maps,输入是 3.3.5a 客户端目录 ./map_extractor -i /path/to/client -o /home/acore/azeroth-server/data # 2. 提取建筑碰撞,产出 Buildings 目录 ./vmap4_extractor -i /path/to/client -o /home/acore/azeroth-server/data # 3. 把 Buildings 组装成 vmaps ./vmap4_assembler /home/acore/azeroth-server/data/Buildings /home/acore/azeroth-server/data/vmaps # 4. 生成寻路数据 mmaps,最慢的一步 ./mmaps_generator --threads 4第一步产出dbc/和maps/,大小几百 MB 到 1 GB 多。第二步产出Buildings/,几个 GB。第三步组装出vmaps/,同样几个 GB。第四步的mmaps_generator默认从当前目录读配置和输出,跑之前确认工作目录对,它会吃掉大量内存和磁盘,--threads开到 4 以上内存压力就上来了,8 GB 机器建议 2~4 线程,整个流程 1~3 小时,中途断电或 Ctrl+C 就得重来。生成完的mmaps/大概 2~4 GB,是怪能不能绕开障碍物的关键,缺了它怪会直线穿墙。
四个目录全部就位后,这套 game emulator 的服务端侧就齐了。
5. 避坑排查:五条血泪记录,从编译被 kill 到 realm 显示离线
5.1 编译到八成被 killed
现象:make -j16跑到七八十,终端突然打出c++: fatal error: Killed signal terminated program cc1plus,或者干脆整段消失,回头看退出码 137。
原因:内核 OOM killer 出手了。Boost 模板展开和 worldserver 那几个巨型 translation unit 单文件编译峰值能到 2 GB,-j16就是 32 GB 瞬时占用,16 GB 机器必然被杀。这跟代码写得好不好没关系,纯粹是并行度和内存不匹配。
解决:先看dmesg | tail -20确认是Out of memory: Killed process,然后把-j降到内存GB数 / 2以下。已经编了一大半的不必重来,make会从断点继续。长期方案是加 swap:sudo fallocate -l 8G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile,再写进/etc/fstab。
5.2 worldserver 起来就报连不上数据库
现象:Could not connect to MySQL database at 127.0.0.1: Can't connect to MySQL server on '127.0.0.1' (111),或者Access denied for user 'acore'@'localhost'。
原因:三种情况最常见。一是连接串分隔符写错,把分号写成了冒号或逗号;二是 MySQL 没在监听 TCP,bind-address被改成127.0.0.1之外的地址或者干脆注释掉了;三是用户 host 不匹配,配置文件连的是127.0.0.1,但建的用户只有'acore'@'localhost',MySQL 里这两个是不同的 host 条目。
解决:先mysql -h 127.0.0.1 -P 3306 -u acore -p手动连一次,连不上就是 MySQL 侧的问题,sudo ss -lntp | grep 3306看监听地址。能连上但 core 连不上,基本就是配置串格式,逐字符对着"127.0.0.1;3306;user;pass;db"核一遍。
5.3 登录器能看到 realm,点进去卡在角色列表
现象:账号密码能过,realm 列表也刷出来了,选中服务器后进度条走到头就停住,或者直接提示服务器离线。
原因:acore_auth.realmlist表里的address字段不对。这张表告诉客户端「世界服在哪」,填127.0.0.1时只有本机能连;客户端在另一台机器上,就必须填这台服务器对客户端可达的 IP。另外port字段必须和worldserver.conf里的WorldServerPort一致,默认 8085。
解决:直接改表。
UPDATE acore_auth.realmlist SET address = '你的服务器可达IP', port = 8085 WHERE id = 1;改完不用重启 worldserver,客户端退出重进就生效。同时确认 8085 端口在防火墙里是放行的,sudo ufw status看一眼。
5.4 启动报 map 文件不存在
现象:Map file './maps/0014331.map' does not exist或者VMap file ... could not be loaded。
原因:DataDir指错层级,或者提取流程没跑完。前者是把DataDir指到了data/maps,后者是mmaps_generator跑到一半中断,mmaps目录里只有部分文件。
解决:先ls确认DataDir下同时存在dbc、maps、vmaps、mmaps四个目录,再看每个目录里有没有实际文件。mmaps目录如果是空的或者只有几个文件,重跑mmaps_generator;如果报的是特定地图编号缺失,多半是客户端不完整,换一份干净的 3.3.5a 客户端重新提取。
5.5 编译通过,运行时找不到 libmysqlclient
现象:./worldserver: error while loading shared libraries: libmysqlclient.so.21: cannot open shared object file。
原因:编译时链接的库版本和运行时系统里的不一致。常见于先装了某个版本的 MySQL 客户端库,后来又手动装/卸了另一版,libmysqlclient.so.21被换成了.so.22,二进制还指着老 soname。
解决:ldd bin/worldserver | grep mysql看它到底要哪个文件,再find / -name "libmysqlclient.so*"看系统里有哪些。版本对不上就sudo apt install --reinstall libmysqlclient-dev把 ABI 拉回一致。不要用软链接硬凑,soname 不同意味着 ABI 不同,链接上去运行时照样崩。
6. 装完怎么验证:从 account create 到第一次登入
服务端装没装成,不看编译有没有报错,只看三件事能不能闭环:进程在、端口通、账号能进世界。
先起 authserver,再起 worldserver,顺序不能反——worldserver 启动时要向 authserver 注册 realm,反过来会注册失败。两个进程都别用&甩到后台就完事,第一次启动盯着前台输出,Added realm "AzerothCore" at ...这行出现才说明 realm 注册成功。
cd /home/acore/azeroth-server ./bin/authserver # 一个终端 ./bin/worldserver # 另一个终端worldserver 起来后会给你一个交互式控制台,在这里建第一个账号:
account create testuser testpass account set gmlevel testuser 3 -1 server info第一行建号,第二行给 GM 3 级权限(-1表示对所有 realm 生效),第三行打印当前在线人数、运行时长、版本号。三行都正常回显,服务端侧就算通了。
然后按这张表逐项核一遍:
| 检查项 | 命令 | 期望结果 |
|---|---|---|
| 端口监听 | ss -lntp | grep -E '3724|8085' | 两个端口都在 LISTEN |
| 库连接 | mysql -u acore -p -e "show tables" acore_world | wc -l | 几百张表,不是 0 |
| 账号入库 | mysql -u acore -p -e "select username from account" acore_auth | 能看到 testuser |
| realm 配置 | mysql -u acore -p -e "select id,address,port from realmlist" acore_auth | address 与 port 正确 |
最后一步是客户端侧。把客户端目录下的realmlist.wtf改成set realmlist 127.0.0.1(服务端和客户端不在同一台机器就填服务器 IP),用 testuser 登录。能进到角色创建界面、能建号、能进世界看到地面和 NPC,这条链才算真正跑通。进世界后跑两步,看角色会不会掉到地图下面——掉下去就是mmaps或vmaps没生成全。
我自己的习惯是:每次装完、每次升级 core、每次加模块,都强制把「进程 → 端口 → 账号 → 进世界走两步」这套走一遍,不管前面编译多顺利。有次编译零警告、进程也起来了,结果DataDir少写一层,卡在角色列表半小时才反应过来。这套验证流程就是给自己留的后悔药。希望帮到你。
本文还有配套的精品资源,点击获取