Skip to content

Latest commit

 

History

History
104 lines (75 loc) · 7.83 KB

File metadata and controls

104 lines (75 loc) · 7.83 KB

CLAUDE.md

AzerothCore is a C++ MMORPG server emulator for World of Warcraft 3.3.5a (WotLK), built with CMake, backed by MySQL.

Agent rules

  • 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.
  • 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).
  • 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.

Build

Out-of-source build is required (in-source is blocked by CMake).

mkdir -p build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/azeroth-server -DCMAKE_BUILD_TYPE=RelWithDebInfo \
  -DSCRIPTS=static -DMODULES=static
make -j$(nproc) && make install

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.

Tests (Google Test, in src/test/): configure with -DBUILD_TESTING=ON, then ctest or ./src/test/unit_tests from the build dir.

Repository layout

  • src/common/ — networking (Asio), crypto, config, logging, shared utilities.
  • src/server/game/ — core gameplay; compiled into worldserver.
  • src/server/scripts/ — content scripts grouped by region (EasternKingdoms/, Northrend/, …), class (Spells/spell_mage.cpp, …), and domain (Commands/, Pet/, OutdoorPvP/, World/).
  • src/server/database/ — DB abstraction and schema updater.
  • src/server/shared/ — code shared by auth and world servers.
  • src/server/apps/{authserver,worldserver}/ — entry points (ports 3724 and 8085).
  • src/test/ — Google Test unit tests + mocks.
  • data/sql/base/ (historical schema), updates/db_*/ (merged), updates/pending_db_*/ (in-flight, edit here), custom/ (gitignored).
  • 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.
  • apps/ — helper scripts; apps/codestyle/ holds the lint scripts (see below).
  • conf/dist/ — distributed config templates; conf/*.conf is gitignored.
  • deps/ — vendored third-party dependencies.

Adding SQL updates

  1. cd data/sql/updates/pending_db_world/ (or pending_db_auth / pending_db_characters).
  2. ./create_sql.sh generates an empty rev_<timestamp>.sql you write into.
  3. Required SQL conventions (enforced by apps/codestyle/codestyle-sql.py):
    • Every INSERT must be preceded by a matching DELETE (idempotency).
    • 4-space indent (no tabs), trailing newline, no double semicolons, no multiple blank lines.
    • Tables must use the InnoDB engine.

The three databases:

  • acore_auth — accounts, realm list, IP/account bans, session keys. Shared across all realms.
  • acore_characters — per-character state: characters, inventory, in-progress quests, mail, guilds, arena teams, achievements. One per realm.
  • acore_world — static game content: creature/gameobject/item/quest templates, spawn lists, loot tables, SmartAI scripts, gossip, conditions. Read-mostly; rebuilt from SQL.

Code style

Run the linters before claiming a change is done:

python apps/codestyle/codestyle-cpp.py     # C++
python apps/codestyle/codestyle-sql.py     # SQL (compares to origin/master)

Hard rules (also enforced by CI with -Werror):

  • 4-space indent for C++ (tabs forbidden); 2-space for JSON/YAML/sh/ts/js. UTF-8, LF, max 120 cols, trailing newline.
  • Allman braces. No braces around single-line statements. if (x) — never if(x) or if ( x ).
  • auto const& (not const auto&); Type const* (not const Type*).
  • Use {} format specifiers (fmt-style), not %u/%s.
  • Use the typed helpers, not raw flag access:
    • IsPlayer(), IsCreature(), IsItem(), … instead of GetTypeId() == TYPEID_*.
    • GetNpcFlags(), HasNpcFlag(), SetNpcFlag(), RemoveNpcFlag(), ReplaceAllNpcFlags() instead of *Flag(UNIT_NPC_FLAGS, …).
    • IsRefundable(), IsBOPTradable(), IsWrapped() instead of HasFlag(ITEM_FIELD_FLAGS, …).
    • HasFlag(ItemFlag) / HasFlag2(ItemFlag2) / HasFlagCu(ItemFlagsCustom) instead of bitwise Flags & ITEM_FLAG….
    • ObjectGuid::ToString().c_str() instead of ObjectGuid::GetCounter().

CI also runs cppcheck.

Project conventions

  • 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.
  • Random: use project helpers from src/common/Utilities/Random.hurand, irand, frand, rand32, rand_chance, roll_chance_f, roll_chance_i. Do not use std::rand or <random> directly.
  • Strings: Acore::StringFormat(fmt, args...) (wraps fmt::format, {} placeholders) — src/common/Utilities/StringFormat.h.
  • Config: read options with sConfigMgr->GetOption<T>("Name", default).
  • Namespace: project-wide is Acore:: (no Trinity:: remnants — agents porting from upstream forks must rename).
  • 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.
  • 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.
  • 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.

Scripting registration

Scripts inherit from a ScriptObject subclass (SpellScript, AuraScript, CreatureScript, InstanceMapScript, GameObjectScript, CommandScript, …). Two registration styles coexist:

  • Spell / aura scripts: use the RegisterSpellScript(ClassName) (or RegisterSpellAndAuraScriptPair(...)) macro inside AddSC_<name>().
  • Creature scripts: prefer RegisterCreatureAI(ClassName) for new code; legacy zones still use new ClassName();. Match the surrounding pattern.

Then declare and call AddSC_<name>() from the regional loader: Spells/spells_script_loader.cpp, EasternKingdoms/eastern_kingdoms_script_loader.cpp, etc.

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.

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.

Custom (non-upstream) scripts go in src/server/scripts/Custom/ (gitignored).