Path A — Self-hosted / P2P (native)¶
You run the authoritative server on infrastructure you trust — your own dedicated fleet, or a
player acting as a P2P host. The module is your code on your machines, so no sandbox is
needed: you build it native and link liblattice directly. This path is the full
07 — Custom Module Guide, distilled.
Prerequisites
Finish the common setup first (game registered, SDK integrated, sim
authored). Tooling: a C++20 compiler (g++/clang), and for Windows the MinGW-w64 -posix
cross-toolchain.
1. Build liblattice¶
This produces build/liblattice.so. Copy reference/include/lattice/lattice.h and the library
next to your module.
2. Build your module against the public header¶
Compile your game-sim/ once and link it into each target. The only difference between a
dedicated server and a P2P host is the start mode.
g++ -std=c++20 -O2 -Iinclude \
game-sim/schema.cpp game-sim/simulate.cpp server/main.cpp \
-o build/lattice-gameserver \
-Lbuild -llattice -Wl,-rpath,'$ORIGIN'
g++ -std=c++20 -O2 -Iinclude \
game-sim/schema.cpp game-sim/simulate.cpp host/main.cpp \
-o build/lattice-host \
-Lbuild -llattice -Wl,-rpath,'$ORIGIN'
lattice_runner_start(r, LATTICE_MODE_SERVER); /* dedicated server */
lattice_runner_start(r, LATTICE_MODE_HOST); /* P2P host */
Windows (MinGW-w64 cross-build)¶
Three Windows requirements are baked into the recipe: the -posix thread-model compiler
(x86_64-w64-mingw32-g++-posix), the -static -static-libgcc -static-libstdc++ flags (so the
DLL/EXE depend only on core Windows DLLs), and _WIN32_WINNT=0x0600. The resulting lattice.dll
is self-contained. Full detail in 07 §6.
3. Run it¶
# Ship liblattice.so next to the binary (found via $ORIGIN rpath)
./build/lattice-gameserver # opens the authority session on its listen port
Register the running instance with the director so clients can find it:
curl -s -X POST http://director/fleet/register -H "X-Fleet-Token: $FLEET" \
-H 'content-type: application/json' \
-d '{"instance_id":"i-1","endpoint":"1.2.3.4:9000","region":"eu","modes":["ffa"],"capacity":64}'
Then clients matchmake → resolve → connect (the common-setup sequence).
4. Wire up the server-side features¶
Because you control the process, you can use the authority-only ABI directly:
- Anti-cheat —
lattice_ac_configure+on_violation; report scores to analytics for the portal cheat panel. - Async jobs & mediated fetch — call external APIs without stalling the tick.
- Embedded store — persist profiles/inventories with the CQRS write/read split.
flowchart LR
SRC["game_sim.cpp (authored once)"] -->|"g++/clang + link liblattice"| BIN["native binary"]
BIN --> SRV["dedicated server (SERVER)"]
BIN --> HOST["P2P host (HOST)"]
SRV -->|fleet/register| DIR[director]
HOST -.player-hosted.-> DIR
Untrusted native code is a different story
Self-hosting assumes you trust the binary (it's yours). If you ever need to run untrusted native modules from third parties, that needs a microVM boundary — not an import scan. For untrusted code, prefer Path B — managed WASM. See 09 §8.
The exact same game_sim.cpp you built here also compiles to WASM with no code change — that's
Path B, explained in write-once, dual-target.