Skip to main content
The module system lets you customize server behavior without modifying the core codebase. Modules live in the modules/ directory and are loaded according to modules/init.txt. Because your changes stay separate from the core files, upgrading the server is straightforward and your customizations are easy to track. There are three module types, each suited to a different kind of change:
TypeFile extensionWhen it runsUse for
Lua.luaRuntime, on server startOverriding game logic, NPC behavior, spells, abilities
C++.cppCompile timeNew packet handlers, low-level engine extensions
SQL.sqlDatabase update (dbtool update)Item data, mob drops, spawn points, NPC records

Folder structure

modules/
├── init.txt              # Controls which modules are loaded
├── module_utils.lua      # Helper utilities for Lua modules
├── example/              # Official worked examples
│   ├── commands/
│   └── lua/
├── custom/               # Recommended home for your own modules
│   ├── cpp/
│   ├── lua/
│   └── sql/
├── testing/              # Testing helpers
├── tools/                # Developer tools (e.g. packet capture)
├── renamer/              # Name-related utilities
├── cop/                  # Era accuracy: Chains of Promathia
├── toau/                 # Era accuracy: Treasures of Aht Urhgan
├── wotg/                 # Era accuracy: Wings of the Goddess
├── abyssea/              # Era accuracy: Abyssea Add-ons
├── soa/                  # Era accuracy: Seekers of Adoulin
└── rov/                  # Era accuracy: Rhapsodies of Vana'diel

Loading modules with init.txt

modules/init.txt controls which modules are active. It is tracked in git but marked with --assume-unchanged so your local edits are not committed accidentally.
init.txt
# Load everything under custom/commands/
custom/commands/

# Load only a specific file
custom/lua/claim_shield.lua
Rules:
  • One entry per line. Empty lines and lines beginning with # are ignored.
  • A folder path loads all valid files inside it (*.lua, *.cpp, *.sql).
  • A file path loads only that file.
  • The trailing / on folder names is optional.

When to use each module type

Use a Lua module when you want to change gameplay logic that is already implemented in Lua — overriding a spell’s effect, adjusting NPC dialogue, tweaking experience rates, or adding new commands. Lua modules load at runtime and do not require a server rebuild. Use a C++ module when you need to intercept or extend the engine itself — replacing a packet handler, hooking into low-level systems, or adding new server-side bindings. C++ modules require recompiling the server. Use a SQL module when you need to change data stored in the database — adding items, modifying mob drop tables, relocating spawn points, or inserting custom NPC records. SQL modules are applied when you run dbtool update.

Mounting modules in Docker

For Lua and SQL modules, bind-mount your local modules/ directory into the container. No rebuild needed.
docker run ... \
  --mount type=bind,src="$(pwd)"/modules,dst=/server/modules \
  ghcr.io/landsandboat/server:latest
C++ modules must be compiled into the server binary. A bind mount alone is not enough — you must rebuild the Docker image with your .cpp files present. See the C++ modules page for details.
In Docker Compose, uncomment the modules volume line in your docker-compose.yml:
docker-compose.yml
volumes:
  - losmeshes:/server/losmeshes
  - navmeshes:/server/navmeshes
  - ./modules:/server/modules   # <-- add this

Module pages

Lua modules

Override game logic at runtime. No rebuild required.

C++ modules

Extend the engine with new packet handlers and bindings.

SQL modules

Modify database content such as drops, spawns, and items.

Era accuracy modules

Revert content to match a specific expansion era.

Build docs developers (and LLMs) love