|
1 | 1 | # CLAUDE.md |
2 | 2 |
|
3 | | -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 3 | +AzerothCore is a C++ MMORPG server emulator for World of Warcraft 3.3.5a (WotLK), built with CMake, backed by MySQL. |
4 | 4 |
|
5 | | -## Project Overview |
| 5 | +## Agent rules |
6 | 6 |
|
7 | | -AzerothCore is an open-source MMORPG server emulator for World of Warcraft patch 3.3.5a (Wrath of the Lich King). It's a C++ project built with CMake, using MySQL for data storage. Licensed under GNU GPL v2. |
| 7 | +- **Do not configure or build unless explicitly asked.** Builds are slow (CMake + compile of a large C++ codebase) and rarely needed to make code changes. |
| 8 | +- **Never edit SQL files outside `data/sql/updates/pending_db_*/`.** `data/sql/base/`, `data/sql/archive/`, and `data/sql/updates/db_*/` are immutable (do not modify). |
| 9 | +- **Do not run git commands that modify repo state** (commit, branch, merge, rebase, reset, push, …) unless explicitly requested, and do not include them in plans. Read-only git (status, diff, log) is fine. |
8 | 10 |
|
9 | | -## Build Commands |
| 11 | +## Build |
10 | 12 |
|
11 | | -### Configure and build (out-of-source build required) |
12 | | - |
13 | | -- Skip building unless explicitly requested. |
| 13 | +Out-of-source build is required (in-source is blocked by CMake). |
14 | 14 |
|
15 | 15 | ```bash |
16 | | -# Create build directory and configure |
17 | 16 | mkdir -p build && cd build |
18 | 17 | cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/azeroth-server -DCMAKE_BUILD_TYPE=RelWithDebInfo \ |
19 | 18 | -DSCRIPTS=static -DMODULES=static |
20 | | - |
21 | | -# Build (use appropriate core count) |
22 | | -make -j$(nproc) |
23 | | -make install |
| 19 | +make -j$(nproc) && make install |
24 | 20 | ``` |
25 | 21 |
|
26 | | -### Key CMake options |
27 | | - |
28 | | -- `SCRIPTS`: none, static, dynamic, minimal-static, minimal-dynamic (default: static) |
29 | | -- `MODULES`: none, static, dynamic (default: static) |
30 | | -- `APPS_BUILD`: none, all, auth-only, world-only (default: all) |
31 | | -- `TOOLS_BUILD`: none, all, db-only, maps-only (default: none) |
32 | | -- `BUILD_TESTING`: Enable unit tests (default: OFF) |
33 | | -- `USE_COREPCH` / `USE_SCRIPTPCH`: Precompiled headers (default: ON) |
| 22 | +Compiler: **C++20** required (`CMAKE_CXX_STANDARD 20`). Useful CMake flags: `BUILD_TESTING=ON` (Google Test), `NOPCH=1` (disable precompiled headers). Full flag set in `conf/dist/config.cmake`. `compile_commands.json` is exported automatically. |
34 | 23 |
|
35 | | -### Unit tests |
36 | | - |
37 | | -```bash |
38 | | -# Configure with testing enabled |
39 | | -cmake .. -DBUILD_TESTING=ON |
40 | | -make -j$(nproc) |
41 | | - |
42 | | -# Run tests |
43 | | -./src/test/unit_tests |
44 | | -# or |
45 | | -ctest |
46 | | -``` |
| 24 | +Tests (Google Test, in `src/test/`): configure with `-DBUILD_TESTING=ON`, then `ctest` or `./src/test/unit_tests` from the build dir. |
47 | 25 |
|
48 | | -Tests use Google Test and live in `src/test/`. The test binary links against the `game` library. |
| 26 | +## Repository layout |
49 | 27 |
|
50 | | -## Architecture |
| 28 | +- `src/common/` — networking (Asio), crypto, config, logging, shared utilities. |
| 29 | +- `src/server/game/` — core gameplay; compiled into worldserver. |
| 30 | +- `src/server/scripts/` — content scripts grouped by region (`EasternKingdoms/`, `Northrend/`, …), class (`Spells/spell_mage.cpp`, …), and domain (`Commands/`, `Pet/`, `OutdoorPvP/`, `World/`). |
| 31 | +- `src/server/database/` — DB abstraction and schema updater. |
| 32 | +- `src/server/shared/` — code shared by auth and world servers. |
| 33 | +- `src/server/apps/{authserver,worldserver}/` — entry points (ports 3724 and 8085). |
| 34 | +- `src/test/` — Google Test unit tests + mocks. |
| 35 | +- `data/sql/` — `base/` (historical schema), `updates/db_*/` (merged), `updates/pending_db_*/` (in-flight, **edit here**), `custom/` (gitignored). |
| 36 | +- `modules/` — external modules (each a subdir with its own `CMakeLists.txt`). Disable with `-DDISABLED_AC_MODULES="mod1;mod2"`. See `modules/how_to_make_a_module.md`. |
| 37 | +- `apps/` — helper scripts; `apps/codestyle/` holds the lint scripts (see below). |
| 38 | +- `conf/dist/` — distributed config templates; `conf/*.conf` is gitignored. |
| 39 | +- `deps/` — vendored third-party dependencies. |
51 | 40 |
|
52 | | -### Two server executables |
53 | | -- **authserver** (`src/server/apps/authserver/`): Handles authentication and realm selection (port 3724) |
54 | | -- **worldserver** (`src/server/apps/worldserver/`): Main game server handling all gameplay (port 8085) |
| 41 | +## Adding SQL updates |
55 | 42 |
|
56 | | -### Source layout (`src/`) |
| 43 | +1. `cd data/sql/updates/pending_db_world/` (or `pending_db_auth` / `pending_db_characters`). |
| 44 | +2. `./create_sql.sh` generates an empty `rev_<timestamp>.sql` you write into. |
| 45 | +3. Required SQL conventions (enforced by `apps/codestyle/codestyle-sql.py`): |
| 46 | + - Every `INSERT` must be preceded by a matching `DELETE` (idempotency). |
| 47 | + - 4-space indent (no tabs), trailing newline, no double semicolons, no multiple blank lines. |
| 48 | + - Tables must use the InnoDB engine. |
57 | 49 |
|
58 | | -- **`src/common/`** - Shared libraries: networking (Asio), cryptography, configuration, logging, threading, collision detection, utilities |
59 | | -- **`src/server/game/`** - Core game logic (~52 subsystems), the heart of the worldserver |
60 | | -- **`src/server/scripts/`** - Content scripts (bosses, spells, commands, instances) |
61 | | -- **`src/server/database/`** - Database abstraction layer and schema updater |
62 | | -- **`src/server/shared/`** - Code shared between auth and world servers (packets, network, realm definitions) |
63 | | -- **`src/test/`** - Unit tests (Google Test) |
| 50 | +The three databases: |
64 | 51 |
|
65 | | -### Key game subsystems (`src/server/game/`) |
| 52 | +- `acore_auth` — accounts, realm list, IP/account bans, session keys. Shared across all realms. |
| 53 | +- `acore_characters` — per-character state: characters, inventory, in-progress quests, mail, guilds, arena teams, achievements. One per realm. |
| 54 | +- `acore_world` — static game content: creature/gameobject/item/quest templates, spawn lists, loot tables, SmartAI scripts, gossip, conditions. Read-mostly; rebuilt from SQL. |
66 | 55 |
|
67 | | -- **Entities/** - Core game objects: `Player`, `Creature`, `Unit`, `Item`, `GameObject` |
68 | | -- **Spells/** - Spell mechanics, aura system, spell effects |
69 | | -- **Maps/** - Map management, grid system, instancing |
70 | | -- **Handlers/** - Client packet handlers (one file per system: `MovementHandler.cpp`, `SpellHandler.cpp`, etc.). These are methods on `WorldSession` |
71 | | -- **AI/** - Creature AI framework |
72 | | -- **Scripting/** - Script system with typed base classes (`ScriptObject` subclasses: `CreatureScript`, `SpellScript`, `InstanceMapScript`, `GameObjectScript`, `CommandScript`, etc.) |
73 | | -- **Server/** - `WorldSession` (per-player connection), `World` (global state), opcode definitions |
| 56 | +## Code style |
74 | 57 |
|
75 | | -### Scripting system |
| 58 | +Run the linters before claiming a change is done: |
76 | 59 |
|
77 | | -Scripts follow a registration pattern: |
78 | | -1. Define a class inheriting from `SpellScript`, `CreatureScript`, etc. |
79 | | -2. Implement an `AddSC_*()` function that calls `RegisterSpellScript(ClassName)` (or similar) |
80 | | -3. The `AddSC_*()` is declared and called from the regional `*_script_loader.cpp` |
81 | | -4. Script loaders per region: `spells_script_loader.cpp`, `eastern_kingdoms_script_loader.cpp`, `northrend_script_loader.cpp`, etc. |
82 | | -5. Spell script files are organized by class: `spell_dk.cpp`, `spell_mage.cpp`, `spell_generic.cpp`, etc. |
83 | | - |
84 | | -### Three databases |
85 | | -- **acore_auth** - Accounts, realm list, bans (`data/sql/base/db_auth/`) |
86 | | -- **acore_characters** - Character data, inventories, progress (`data/sql/base/db_characters/`) |
87 | | -- **acore_world** - Game content: creatures, items, quests, spells, loot (`data/sql/base/db_world/`) |
| 60 | +```bash |
| 61 | +python apps/codestyle/codestyle-cpp.py # C++ |
| 62 | +python apps/codestyle/codestyle-sql.py # SQL (compares to origin/master) |
| 63 | +``` |
88 | 64 |
|
89 | | -- SQL updates go in `data/sql/updates/pending_*` with separate subdirectories per database until pull request is merged. Pending SQL files are assigned random names. |
90 | | -- SQL updates go in `data/sql/updates/` with separate subdirectories per database after their pull request is merged. |
91 | | -- SQL files outside the `data/sql/updates/pending_*` folders should never be updated. |
| 65 | +Hard rules (also enforced by CI with `-Werror`): |
92 | 66 |
|
93 | | -### Module system |
| 67 | +- 4-space indent for C++ (tabs forbidden); 2-space for JSON/YAML/sh/ts/js. UTF-8, LF, max 80 cols, trailing newline. |
| 68 | +- Allman braces. No braces around single-line statements. `if (x)` — never `if(x)` or `if ( x )`. |
| 69 | +- `auto const&` (not `const auto&`); `Type const*` (not `const Type*`). |
| 70 | +- Use `{}` format specifiers (`fmt`-style), not `%u`/`%s`. |
| 71 | +- Use the typed helpers, not raw flag access: |
| 72 | + - `IsPlayer()`, `IsCreature()`, `IsItem()`, … instead of `GetTypeId() == TYPEID_*`. |
| 73 | + - `GetNpcFlags()`, `HasNpcFlag()`, `SetNpcFlag()`, `RemoveNpcFlag()`, `ReplaceAllNpcFlags()` instead of `*Flag(UNIT_NPC_FLAGS, …)`. |
| 74 | + - `IsRefundable()`, `IsBOPTradable()`, `IsWrapped()` instead of `HasFlag(ITEM_FIELD_FLAGS, …)`. |
| 75 | + - `HasFlag(ItemFlag)` / `HasFlag2(ItemFlag2)` / `HasFlagCu(ItemFlagsCustom)` instead of bitwise `Flags & ITEM_FLAG…`. |
| 76 | + - `ObjectGuid::ToString().c_str()` instead of `ObjectGuid::GetCounter()`. |
94 | 77 |
|
95 | | -External modules are loaded from the `modules/` directory. Each module is a subdirectory with its own `CMakeLists.txt`. Disable specific modules with `-DDISABLED_AC_MODULES="mod1;mod2"`. Module skeleton: https://github.com/azerothcore/skeleton-module/ |
| 78 | +CI also runs `cppcheck`. |
96 | 79 |
|
97 | | -### Dependencies |
| 80 | +## Project conventions |
98 | 81 |
|
99 | | -Bundled in `deps/`: boost, MySQL client, OpenSSL, zlib, recastnavigation (pathfinding), g3dlite (geometry), fmt, argon2, jemalloc, and others. |
| 82 | +- **Logging**: `LOG_INFO("category.sub", "msg with {}", arg)` (also `LOG_WARN`, `LOG_ERROR`, `LOG_DEBUG`, `LOG_TRACE`). Categories are hierarchical, dot-separated (e.g. `server.loading`, `entities.player`, `sql.dev`). No `printf`-style; no `sLog->`; no `TC_LOG_*`. Macro in `src/common/Logging/Log.h`. |
| 83 | +- **Random**: use project helpers from `src/common/Utilities/Random.h` — `urand`, `irand`, `frand`, `rand32`, `rand_chance`, `roll_chance_f`, `roll_chance_i`. Do not use `std::rand` or `<random>` directly. |
| 84 | +- **Strings**: `Acore::StringFormat(fmt, args...)` (wraps `fmt::format`, `{}` placeholders) — `src/common/Utilities/StringFormat.h`. |
| 85 | +- **Config**: read options with `sConfigMgr->GetOption<T>("Name", default)`. |
| 86 | +- **Namespace**: project-wide is `Acore::` (no `Trinity::` remnants — agents porting from upstream forks must rename). |
| 87 | +- **Long-lived references**: do not store a raw `Player*` / `Creature*` / `Unit*` past the current call/tick — the object can be removed (logout, despawn, instance unload) and the pointer dangles. Store the `ObjectGuid` and resolve at use time via `ObjectAccessor::FindPlayer(guid)`, `ObjectAccessor::GetCreature(*from, guid)`, `Map::GetCreature(guid)`, etc. |
| 88 | +- **DB queries**: use `PreparedStatement` (via `WorldDatabase` / `CharacterDatabase` / `LoginDatabase` and the prepared-statement enums) rather than raw query strings. Reads that don't need to block the world tick go through the async path: `_queryProcessor.AddCallback(db.AsyncQuery(stmt).WithPreparedCallback(...))` (or `WithCallback` for non-prepared). Multi-statement writes wrap in `SQLTransaction` + `Execute` / `AppendPreparedStatement`. |
| 89 | +- **Timed actions in AI**: use `EventMap` (event id → delay; simple) or `TaskScheduler` (lambdas, repeats, cancellation). Both are members of `CreatureAI`; see any boss script under `src/server/scripts/` for examples — don't roll your own tick counters. |
100 | 90 |
|
101 | | -## Commit Message Format |
| 91 | +## Scripting registration |
102 | 92 |
|
103 | | -Uses Conventional Commits: |
104 | | -``` |
105 | | -Type(Scope/Subscope): Short description (max 50 chars) |
106 | | -``` |
| 93 | +Scripts inherit from a `ScriptObject` subclass (`SpellScript`, `AuraScript`, `CreatureScript`, `InstanceMapScript`, `GameObjectScript`, `CommandScript`, …). Two registration styles coexist: |
107 | 94 |
|
108 | | -- **Types**: feat, fix, refactor, style, docs, test, chore |
109 | | -- **Scopes**: Core (C++ changes), DB (SQL changes) |
110 | | -- **Examples**: `fix(Core/Spells): Fix damage calculation for Fireball`, `fix(DB/SAI): Missing spell to NPC Hogger` |
| 95 | +- **Spell / aura scripts**: use the `RegisterSpellScript(ClassName)` (or `RegisterSpellAndAuraScriptPair(...)`) macro inside `AddSC_<name>()`. |
| 96 | +- **Creature scripts**: prefer `RegisterCreatureAI(ClassName)` for new code; legacy zones still use `new ClassName();`. Match the surrounding pattern. |
111 | 97 |
|
112 | | -## Code Style |
| 98 | +Then declare and call `AddSC_<name>()` from the regional loader: `Spells/spells_script_loader.cpp`, `EasternKingdoms/eastern_kingdoms_script_loader.cpp`, etc. |
113 | 99 |
|
114 | | -- 4-space indentation for C++ (no tabs) |
115 | | -- 2-space indentation for JSON, YAML, shell scripts |
116 | | -- UTF-8 encoding, LF line endings |
117 | | -- Max 80 character line length |
118 | | -- No braces around single-line statements |
119 | | -- Use {} to parse variables into output instead of %u etc. |
120 | | -- CI enforces code style checks and compiles with `-Werror` |
| 100 | +**SmartAI** (data-driven creature behaviour) lives in the world DB's `smart_scripts` table — not in C++. Engine: `src/server/game/AI/SmartScripts/`. For new creature behaviour prefer SmartAI (added via the SQL update workflow); reach for `CreatureScript` only when SmartAI's event/action vocabulary isn't enough. |
121 | 101 |
|
122 | | -## PR Requirements |
| 102 | +**Module hooks** (e.g. `OnPlayerLogin`, `OnWorldUpdate`, `OnSpellCast`) are declared in `src/server/game/Scripting/ScriptDefines/*.h`. Implement by inheriting the matching base (`PlayerScript`, `WorldScript`, …) and registering with `new MyClass();` (or its `RegisterXxxScript` macro where one exists) inside `AddSC_<name>()`. Full hook list: https://www.azerothcore.org/wiki/hooks-script. |
123 | 103 |
|
124 | | -- AI tool usage must be disclosed in PRs |
125 | | -- In-game testing expected |
126 | | -- Changes to generic code require regression testing of related systems |
| 104 | +Custom (non-upstream) scripts go in `src/server/scripts/Custom/` (gitignored). |
0 commit comments