Run custom C++ code inside Total War: Attila's Lua engine. (Rome 2 support is frozen.)
- Download latest release from Releases
- Extract
twdll_attila.dllto your game folder - The DLL only activates when called by a mod
Load the DLL in your Lua script:
twdll = package.loadlib("twdll_attila.dll", "luaopen_twdll")()
-- Use the API
twdll.core.Log("Hello from C++!")Where to load twdll
Load the DLL from the campaign script —
campaigns/<campaign>/scripting.lua— exactly as the test suite does (tests/attila/pack/campaigns/main_attila/scripting.lua). On first load twdll hooks game singletons (world, campaign UI, campaign model), and those objects are only constructed once a campaign world is created. Calling singleton-dependent functions any earlier (e.g. from the main menu or a global script) can returnnilor crash.Not every function requires this: plain utilities (
twdll.core.Log,twdll.core.GameBuild) and functions that read fixed module offsets work regardless of where they are called. This is how the current hook architecture works, not a permanent contract — it may change in the future.
Rome 2 support is totally frozen. No code, build, or docs changes are made for Rome 2 — the
src/rome2/,tests/rome2/, anddocs/rome2/trees are preserved as-is. The Rome 2 docs below are stale and will stay that way until an incoming game update lands, invalidating all internal offsets, and structures are re-verified. Active development targets Attila only.
- Visual Studio 2022 (C++)
- CMake 3.29+
# Rome 2
cmake --preset rome2
cmake --build --preset rome2
# Attila
cmake --preset attila
cmake --build --preset attilasrc/
├── main.cpp # DLL entry point
├── common/ # Shared utilities
├── rome2/ # Rome 2 specific code
└── attila/ # Attila specific code
twdll works by injecting C++ into Attila's Lua engine: it finds game functions and structs by byte-signature scanning and fixed offsets, then hooks them at runtime. Most contribution work is reverse-engineering the game to map out what to hook.
The Steam Linux build of Attila is 64-bit and ships with DWARF debug symbols built in. The Windows binary this project targets is 32-bit and stripped, so it is far harder to read. If you want to contribute:
- Download the Linux version of Attila from Steam.
- Load it in IDA/Ghidra (or a DWARF-aware tool) to read real function names, class layouts and vtables instead of guessing from disassembly.
- Map what you learn from the 64-bit Linux symbols back onto the 32-bit Windows binary — addresses differ, but the class structures and logic carry over.
- Register the module in
src/main.cpp. - Add an LDoc comment (
@function,@tparam,@treturn) for every exposed function. - List the source in
TWDLL_DOC_SOURCESindocs/CMakeLists.txt. - Add test cases in
tests/shared/testing.lua. - Regenerate docs and commit them (see
AGENTS.md→ Documentation).
Questions and ideas go in Issues.
- Update
CHANGELOG.md - Commit and push to master
- Create git tag:
git tag v1.0.0 && git push origin v1.0.0 - GitHub Actions builds and releases automatically