Imported from dawidcxx/arenacraft (
AGENTS.md). Install upstream withnpx skills add dawidcxx/arenacraft. Copyright stays with the author.
AGENTS.md
Arenacraft: invite-only WoW 3.3.5 arena private server. C++23 core forked from AzerothCore, now fully ours — we do NOT rebase against Trinity/AzerothCore anymore. Modify the core directly; don't build plugin/module abstractions or preserve upstream compatibility.
Gameplay loop: player logs in at level 80, gears up via vendors (gear templates
planned), plays arena. Custom gameplay is plain core code in src/game/, not a
script/plugin layer.
Strong preference: patch the core directly. Do NOT put custom behavior in a plugin/module layer, a separate patch file, or a "wrapper" namespace built to keep our code out of the core. When a core function needs to behave differently for us, edit that core function in place (inline the arenacraft-specific bit, guarded as needed). Reach for the script registry only when the core already exposes the right hook; otherwise change the core. This fork is ours — there is no upstream to protect.
Static C++ over SQL/DB data. Server behavior the user dictates (arena
rotation, disabled features, balance tweaks, ...) belongs in C++ as a static
change — hardcode or inline it in the core, even if it feels crude. Do NOT
implement such rules as SQL migrations, disables/battleground_template rows,
or other DB/config content. data/sql is for schema and upstream content
parity only; new migrations are for real content fixes, not gameplay rules.
If a DB table mechanism exists for something (e.g. DisableMgr), ignore it and
patch the code path instead when the rule is ours.
Build & run
nix develop(or direnv) for the toolchain: zig, clang-tools, bun, mysql84, ...zig build ac— debug.-Doptimize=ReleaseFast acfor actual play.zig build run-ac [-- <args>]— build + runac, forwarding args.zig build test-build— dep/link smoke test.compile_commands.jsonis generated byzig build compile-commands(clangd); it is not part of the default build. Never read/parse it: to check a change compiles, just runzig build ac.- Single unified binary
ac; subcommands:ac worldserver,ac authserver,ac map_extractor,ac vmap4_extractor,ac vmap4_assembler,ac mmaps_generator. - Services:
podman compose up -d db valkey(mysql 8.0 + valkey). - Config is env-only, no .conf files:
./.envauto-loaded (template.env.example);AC_DATA_DIRresolves relative to the binary.
Tests
- Unit tests use doctest (nix package, header-only). Needs
nix develop; not available in the Docker/CI builder image. - Convention over configuration (
zig-build/TestHarness.zig): a module opts in by providingsrc/<module>/Tests.cpp, the only TU withDOCTEST_CONFIG_IMPLEMENT_WITH_MAIN. Test cases live in co-located*_test.cppfiles next to the code they cover and are picked up automatically — no source lists.Tests.cppand*_test.cppare excluded from the normal module libraries bySrc.queryModuleSources. - Currently enabled:
common,game. zig build test— build + run every enabled module's tests.- Per module:
zig build <module>-test-build(build + install the executable) andzig build run-<module>-test [-- <doctest args>](build + run). - To enable a new module: add a
Tests.cppentrypoint, register it in theTargetenum (TestHarness.zig) and inAcGraph.buildUnitTests.
Database
scripts/db_sync(bun) — forward-only sync ofdata/sqlintoacore_*dbs. No rollbacks; the AC in-core updater was removed on purpose.- Schema = squashed base in
data/sql/base/db_{auth,characters,world}. - New migration: add
data/sql/updates/db_<name>/YYYY_MM_DD_NN.sql. Never edit an applied file — sha1-tracked, modified files get skipped. - Idempotent SQL (REPLACE INTO etc.) may go in
data/sql/custom/(gitignored). data/dbc|maps|vmaps[,mmaps]come fromscripts/extract_assets— generated, never commit or hand-edit.- Reading DBCs: use
bun scripts/src/dbc_dump.ts <DbcName|path> -n <nameField>(e.g.Faction -n 23 --search cenarion); the WDBC reader isscripts/src/lib/dbc.ts. Don't write throwaway parsers — the name field index differs per DBC (Faction = 23), pass it with-n.
Where gameplay code goes
- Write custom systems directly in the core (
src/game/). For event hooks use the script registries insrc/game/Scripting/ScriptDefines/(PlayerScript, AllCreatureScript, ArenaScript, ...). - Custom player lifecycle (login/level-80 flow): write plain core code in
src/game/. - Spell fixes:
- runtime DBC-level corrections →
src/game/Spells/SpellInfoCorrections.cpp(ApplySpellFix(...)) - scripted behavior →
src/game/Scripts/Spells/spell_<class>.cppwithRegisterSpellScript, bound viaspell_script_namesDB table
- runtime DBC-level corrections →
- Vendor items:
npc_vendorrows or code-driven stock (seeai-doc/vendor.md). - Scripts are plain game code, compiled into the
gamelibrary (no separate module, no dynamic loading). The registry is hand-maintained — edit directly, no globs:src/game/Scripts/ScriptLoader.cpp.
Conventions & traps
- clang-format touched files (LLVM base, Allman braces, 120 cols, 2-space —
see
.clang-format). - Commits: lowercase,
arenacraft(scope): summary/feat:/fix:. - Repo was restructured from upstream:
src/server/game/...→src/game/.... AC wiki docs and old diffs reference stale paths. .envholds secrets; never commit it.ai-doc/holds living reference notes (e.g.ai-doc/vendor.md). If a doc is outdated or wrong when you touch the area it describes, update/reify it in the same change — don't leave it stale.