Local development environment for Beyond All Reason -- spins up Teiserver (lobby server), PostgreSQL, SPADS (autohost), and bar-lobby (game client) with a single command.
Everything server-side runs in Docker. The game client runs natively.
git clone https://github.com/beyond-all-reason/BAR-Devtools.git
cd BAR-Devtools
bash scripts/bootstrap.sh # installs `just` >= 1.31 to ~/.local/bin
exec "$SHELL" -l # reload PATH
just setup::init # full first-time setup (clones, builds, editor wiring — all prompts up front)
just services::up # start Postgres + TeiserverTeiserver comes up at http://localhost:4000 — log in with root@localhost / password. (Full port list under Ports.)
The whole stack runs inside WSL2 — nothing needs native Windows. From a PowerShell prompt, install the VS Code WSL bridge, cap WSL2's resource use, and pull a distro:
code --install-extension ms-vscode-remote.remote-wsl
winget install --id DEVCOM.JetBrainsMonoNerdFont # optional — Nerd Font for the starship prompt (see Nice-to-haves)
# Leave ~5-6 GB for Windows; tune to your machine:
@"
[wsl2]
memory=12GB
swap=8GB
processors=4
"@ | Set-Content $env:USERPROFILE\.wslconfig
wsl --install -d Ubuntu-24.04In Windows Terminal, set Ubuntu as your default profile (and, if you installed the Nerd Font above, pick JetBrainsMono Nerd Font under that profile's Appearance → Font face). Open a fresh Ubuntu terminal and follow the Linux Quick Start — it's identical:
git clone https://github.com/beyond-all-reason/BAR-Devtools.git
cd BAR-Devtools
bash scripts/bootstrap.sh
exec "$SHELL" -l
just setup::init
just services::upOn WSL, setup::init additionally prompts for a BAR_DATA_DIR and wires up the native-Windows launch path — the engine runs as a Windows process for performance. See Launching from WSL2 for how just bar::launch crosses the WSL/Windows boundary.
Optional — none of this is needed to build or run BAR.
A more beautiful shell prompt (starship)
A fresh WSL distro drops you at a barebones user@host:~$ — no git info, exit status, or color. starship fixes that. Install it into ~/.local/bin on the host (Linux or WSL):
curl -sS https://starship.rs/install.sh | sh -s -- -b ~/.local/bin
echo 'eval "$(starship init bash)"' >> ~/.bashrc && exec bashTargeting ~/.local/bin — rather than the installer's default /usr/local/bin, or a distro package's /usr/bin — is what makes the prompt follow you into the dev container: distrobox shares your home dir and ~/.bashrc, not host system dirs, so distrobox enter bar-dev picks up the same binary and init line for free.
Starship's defaults use Nerd Font glyphs. In the Windows terminal — including WSL, which renders through it — they show as boxes until you install a Nerd Font: use the winget line in Quick Setup (Windows), or strip the glyphs with starship preset plain-text-symbols -o ~/.config/starship.toml. Native Linux terminals usually have a capable font already.
VS Code test switcher
The test-switcher plugin jumps between BAR test and source files with Cmd+Shift+Y / Ctrl+Shift+Y. Add to your User Settings (JSON):
"test-switcher.rules": [
{
"pattern": "spec/(.*)_spec\\.lua",
"replacement": "$1.lua"
},
{
"pattern": "spec/builder_specs/(.*)_spec\\.lua",
"replacement": "spec/builders/$1.lua"
},
{
"pattern": "spec/builders/(.*)\\.lua",
"replacement": "spec/builder_specs/$1_spec.lua"
},
{
"pattern": "(luarules|common|luaui|gamedata)/(.*)\\.lua",
"replacement": "spec/$1/$2_spec.lua"
}
]Pre-commit hook (stylua + luacheck)
just setup::hooks installs a git pre-commit hook that runs stylua and luacheck on staged Lua before each commit.
repos.conf lists the default upstream repositories. To override any with your own fork or branch, add just the rows you want to change to repos.local.conf (gitignored — won't affect anyone else), then re-clone:
# repos.local.conf — only the entries you're changing:
teiserver https://github.com/yourname/teiserver.git your-branchjust repos::clone teiserverThe two files use different whitespace-delimited formats. repos.conf
carries the feature column; repos.local.conf does not (it only overrides
url/branch and, as an exception, a per-repo local_path):
# repos.conf directory url branch feature [local_path]
# repos.local.conf directory url branch [local_path]
teiserver https://github.com/yourname/teiserver.git your-branch
- directory -- local folder name (created by
clone) - url -- git clone URL
- branch -- branch to checkout
- feature -- comma-separated tags (
bar,recoil,teiserver,chobby,spads-source); a repo is pulled in by any of its tags (e.g.bar-lobbyserves bothbarandchobby).repos.confonly -- it's upstream classification, sorepos.local.confhas no feature column (a stray one is ignored and flagged byjust doctor). - local_path -- (optional) absolute or
~-relative path to symlink instead of clone (e.g.~/code/lua-doc-extractor). Inrepos.local.confit's the 4th column -- a per-repo exception to@local_root.
Two optional @-directives can lead the file:
@protocol ssh-- rewrite everyhttps://github.com/…entry to itsgit@github.com:…form (or@protocol httpsfor the reverse), so you don't hand-edit each URL for SSH.@local_root ~/code-- resolve any entry without alocal_pathto a symlink at<root>/<directory>instead of cloning -- for when you keep all the repos as siblings.
Run just with no arguments for the full recipe list.
just bar::fmt # format with stylua
just bar::lint # lint with luacheck
just bar::units # run busted unit tests
just bar::units-shell # interactive busted shell,
# run `busted -t focus` to test specs tagged "#focus"
# for example: `it "should do something #focus", function()`
just bar::lx-shell # interactive lx shell for package work
# (`lx add <pkg>`, `lx sync`, `lx install`, etc.)Tests run in a separate container with MIX_ENV=test, so they work whether or not services::up is running. The test database is independent from the dev database.
just tei::setup-test-db # initialize/migrate the test database (run once)
just tei::test # run the full test suite
just tei::test test/teiserver/battle_test.exs # run a specific test file
just tei::test-shell # interactive bash shell with MIX_ENV=test
# useful for running mix commands directlyIf you've pulled new teiserver code with migrations, re-run just tei::setup-test-db to apply them.
services::up mounts teiserver/lib and teiserver/assets into the running
container, so editing the web UI (controllers, LiveViews, templates, CSS) takes
effect live -- Phoenix recompiles and the browser at http://localhost:4000
refreshes on save, no rebuild or restart. Changes to mix.exs, config/, or
deps still need just services::build (or services::up, which rebuilds).
just engine::build linux # build Recoil via docker-build-v2
just link::create engine # symlink into game directoryjust bar::launch hands off to bar_debug_launcher with your local Beyond-All-Reason/, BYAR-Chobby/, and RecoilEngine/ checkouts wired in via just link::create. The Tk GUI opens by default; pass flags for headless use:
just bar::launch # GUI
just bar::launch --no-gui --play chobby --source local
just bar::launch --no-gui --play bar --source local --map "Quicksilver"
just bar::launch --print-cmd --play bar --source latestBooting via the AppImage launcher (Linux)
By default the launcher boots the engine directly. Pass --boot launcher to go through the AppImage launcher instead (splash + auto-update, like a real install) and point at the AppImage:
export BAR_APPIMAGE_PATH=~/Applications/Beyond-All-Reason.AppImage
just bar::launch --no-gui --play chobby --source latest --boot launcher~/Applications/ is AppImageLauncher's canonical path; any beyond-all-reason*.AppImage there is auto-discovered when BAR_APPIMAGE_PATH points at a directory.
On WSL2 the engine runs as a native Windows process (WSLg is far too slow for the game), so just bar::launch routes through Windows: setup::init does the one-time wiring (it prompts for a BAR_DATA_DIR -- the Windows-side data dir the engine reads from), and each launch cold-copies your checkouts onto NTFS before starting the engine there.
That copy only happens at launch and engine::build windows -- there's no live edit propagation, so re-sync to pick up Lua/source edits:
just bar::sync # cold-copy sources without launching
just bar::sync-logs # tail the cold-copy logjust docs::server # generate + serve recoil docs locally
just lua::library # regenerate lua library from engine sourcesjust services::up # start PostgreSQL + Teiserver
just services::up lobby spads # ...with bar-lobby and SPADS
just services::down # stop everything
just services::logs teiserver # tail logs
just services::shell teiserver # open bash inside the running container- Linux (Arch, Debian/Ubuntu, Fedora) -- or Windows via WSL2 (see below)
- Docker or Podman with Compose V2
- Git
- Bash 5+
- just -- command runner
- distrobox (recommended) -- dev toolchain container
just setup::deps will detect your distro and install what's missing (except just itself). setup::init only needs to run once; re-run just setup::reconfigure to change your feature/SSH/editor choices later — it re-clones for newly-selected features and prunes deselected ones (in-tree clones move to .backups/, never deleted).
All dev tools (Lua 5.1, Lux, Node.js, Cargo, clangd, StyLua) live in docker/dev.Containerfile, the canonical dependency manifest. setup::init builds it; recipes that need these tools (bar::lint, bar::fmt, bar::units, …) auto-enter the distrobox. Rebuild standalone with just setup::distrobox.
- PostgreSQL 16 -- database for Teiserver, persisted in a Docker volume
- Teiserver -- runs in Elixir dev mode (
mix phx.server). On first boot:- Creates the database and runs migrations
- Seeds fake data (test users, matchmaking data)
- Sets up Tachyon OAuth
- Creates a
spadsbotaccount with Bot/Moderator roles
- SPADS (optional,
services::up spads) -- Perl autohost usingbadosu/spads:latest. Downloads game data viapr-downloaderon first run. Connects to Teiserver via Spring protocol on port 8200. - bar-lobby -- Electron/Vue.js game client, runs natively on the host (not in Docker)
- Distrobox dev environment (
just setup::distrobox) -- Fedora container with Lua 5.1, lux, Node.js, Cargo, clangd, and StyLua for running tests, linting, formatting, and doc generation against the BAR codebase. Seedocker/dev.Containerfilefor the full dependency manifest.
| Port | Service |
|---|---|
| 4000 | Teiserver HTTP |
| 5433 | PostgreSQL (configurable via BAR_POSTGRES_PORT) |
| 8200 | Spring lobby protocol (TCP) |
| 8201 | Spring lobby protocol (TLS) |
| 8888 | Teiserver HTTPS |
SPADS is optional and started separately because it requires downloading ~300MB of game data on first run. The download depends on external rapid repositories that can be unreliable.
just services::up spads # Start with SPADS
just services::logs spads # Check SPADS statusThe SPADS bot account (spadsbot / password) is created automatically during Teiserver initialization.
If something isn't working, start here:
just doctorThis runs a read-only check of your system dependencies, environment, ports, repositories, Docker images, and running services. Every failure includes the command to fix it.
Port 5432/5433 conflict with host PostgreSQL:
Either stop your local PostgreSQL (sudo systemctl stop postgresql) or change the port:
BAR_POSTGRES_PORT=5434 just services::upTeiserver takes forever on first run: The initial database seeding includes generating fake data. Follow progress with:
just services::logs teiserverSPADS fails with "No Spring map/mod found": Game data download may have failed. Check logs and retry:
just services::logs spads
just services::down
just services::up spadsDocker permission denied:
sudo usermod -aG docker $USER
# Then log out and back inNuclear option -- start completely fresh:
just services::reset
just services::up