This repository is an Open Tibia Server named Canary. It is a hybrid C++ and Lua codebase with a strict bootstrap order, a large data-driven content layer, and a pragmatic mix of dependency injection plus global singleton-style accessors.
src/is the C++ engine and server runtime.data/is the shared core Lua and XML layer used by every datapack.data-canary/is the small custom datapack.data-otservbr-global/is the full production datapack and the default one inconfig.lua.dist.tests/contains C++ unit and integration tests, plus standalone Lua tests undertests/lua/.docs/system_specs/contains the generated system notes for this repository snapshot.
- Entry point:
src/main.cppresolvesCanaryServerfrom the DI container and callsrun(). - Startup authority:
src/canary_server.cppowns config loading, datapack validation, database init, Lua/module loading, world type selection, map loading, and server start. - Runtime authority:
Game,Dispatcher, andServiceManagerare the main control points. - Persistence authority:
src/io/is the application-facing persistence layer;src/database/is the SQL transport/transaction layer. - Content authority: gameplay behavior is heavily Lua-driven, but the C++ load order is strict and easy to break if changed casually.
- Do not reorder the startup sequence in
CanaryServer::loadModules()unless you understand every downstream dependency. - Do not move content between
data/and a datapack directory casually:data/is shared core/runtime infrastructure.data-canary/anddata-otservbr-global/are content packs.
data-otservbr-global/startup/is for startup-only tables and non-reloadable metadata. Treat it as boot-time state, not normal hot-reload content.- Reload support is partial:
SIGHUPreloads config, raids, items, mounts, events, channels, andcore.lua.- it does not recreate the entire server state cleanly.
- Many gameplay paths assume dispatcher-thread execution. Async work must return to the dispatcher before mutating world state.
- Prefer constructor injection when extending top-level systems, but expect existing code to use
g_*()globals andinject<T>(). - When editing Lua content, decide first whether the change belongs in:
- shared core
data/ - minimal datapack
data-canary/ - full datapack
data-otservbr-global/
- shared core
- When changing persistent player/world state:
- inspect
schema.sql - inspect
${DATA_DIRECTORY}/migrations/ - inspect
src/io/save/load code - inspect whether KV storage is a better fit than a new fixed SQL column
- inspect
- When changing network or packet behavior:
- inspect
src/server/network/protocol/ - inspect
data/modules/modules.xml - inspect Lua module handlers in
data/modules/
- inspect
- When changing scripts, remember the loader ignores files prefixed with
#.
Recommended CMake presets:
cmake --preset linux-debug
cmake --build --preset linux-debug
ctest --preset linux-debug
ctest --preset linux-debug -R unit
ctest --preset linux-debug -R integrationLua tests are separate:
luajit tests/lua/test_npc_messaging.luaUseful local helpers:
start.sh: starts the built server, createsconfig.luafromconfig.lua.distif missing, writes logs.recompile.sh: Linux-oriented convenience wrapper around preset-based CMake builds.docker/docker-compose.yml: local MariaDB + server + login stack.
docs/system_specs/README.mddocs/system_specs/ARCHITECTURE.mddocs/system_specs/DEVELOPMENT.mddocs/system_specs/DATAPACKS.mddocs/system_specs/SCRIPTING.mddocs/system_specs/WORLD_CONTENT.mddocs/system_specs/PERSISTENCE.mddocs/system_specs/TESTING.md