
config.toml reference
Every key BattleSpades reads, generated from the server's own configuration loader so nothing is missing or invented. Each entry shows what happens if you leave it out, what the shipped sample sets, and the range the server clamps it to.
How the config is loaded
- The server reads
config.tomlfrom its own folder (not the shell's current directory) once at startup. Restart after editing. --config other.tomlloads another file;--port Noverrides only the port for that run;--checkvalidates everything without opening the port.- A missing file, or a file with a TOML syntax error, falls back to built-in defaults — a syntax error only prints a warning. Always run
--checkafter editing. - Semantic mistakes stop the server with a clear error instead: unknown
RULE_*names or values, an unknowndefault_mode, unsafe map names inmap_rotation, invalidmatch_length_minutes, a Steamapp_idother than 224540, duplicate Steam ports, a non-HTTPSbase_url… - Numbers outside a key's range are silently clamped to the range shown below.
- Relative paths (
maps,bans.json,state/…, log file) resolve against the server folder. - Never save the file with a UTF-8 byte-order mark (BOM); it breaks parsing and the server falls back to defaults.
Which value wins
For match rules the narrowest setting wins: 1) a [modes.<code>] override for the running mode, 2) the [game_rules] Match Lobby rule, 3) [lobby] match_length_minutes (clocks only), 4) the recovered retail default. Old [game] keys such as respawn_time and fall_damage still work when their RULE_* equivalent is not set.
Reading an entry
- Default if omitted — the value the code uses when the key (or its whole table) is missing. It can differ from the sample file; the reference shows both values where they differ.
- Sample config.toml — shown only when the shipped sample file sets a different value.
- Clamped to — the range the loader enforces. Accepted — the only allowed values.
- Not in sample config — a valid key the sample file does not list (advanced or legacy).
Jump to a table:
| Table | What it controls |
|---|---|
| server | name, port, slots, welcome message |
| admin · logging | admin password, bans file, logs |
| revival · steam | AoSPlay and Steam listing |
| game · lobby · teams | mode, map, rotation, votes, balance |
| objectives · world · audio · weapons | objective guards, content paths, music, deprecated damage keys |
| bots | server bots |
| anticheat · conduct | cheat checks, griefing, AFK |
| network · plugins · debug | advanced budgets, plugins, diagnostics |
| map_creator | Map Creator launcher only |
| modes.<code> | per-mode overrides |
| game_rules | the 102 Match Lobby rules (separate page) |
Server, admin and logging
[server]7 keys
Identity, listen port, capacity and the private welcome message. Every other table is optional; a key you leave out uses the default shown here.
namestringName shown in server browsers and the scoreboard. Clients see at most 31 characters (the retail limit); a longer name is cut and a warning is logged.
- Default if omitted
"BattleSpades Server"
portintegerUDP port for game traffic. The same socket answers A2S server-info queries. Defaults to 27015 (also used when the key is missing). Set 32887 only if players must join from the unmodified retail Steam browser, which always connects to that port. --port overrides it for one run.
- Default if omitted
27015
max_playersintegerPlayer slots, 1–255. The stock Match Lobby offered 2–24 and 24 is the retail-tested ceiling. Bots occupy slots too.
- Default if omitted
24- Clamped to
- 1 – 255
tick_rateintegerSimulation rate (clamped to 10–240). Keep 60: client physics and reconciliation are calibrated to it and other values break movement such as jumping.
- Default if omitted
60- Clamped to
- 10 – 240
steam_idintegerStable 64-bit server identity sent to clients and in A2S replies when the server is not registered with Steam. Leave the sample value unless you know you need another one.
- Default if omitted
90087911866072060- Clamped to
- ≥ 0
join_greetingstringnot in sample configPrivate chat line sent once per connection after the player finishes loading. Tokens: {player}, {release}, {build_date}. An empty string disables it.
- Default if omitted
"Welcome, {player}! {release} | Build (UTC): {build_date}"
motdarraynot in sample configMessage of the day: a string or an array of lines sent after the greeting. Setting it replaces the default Revival lines; [] disables it.
- Default if omitted
["Dig in. Build big. Server + client: an open AoS Revival project | https://aosplay.net", "Server: https://github.com/KikoTs/BattleSpades", "Client: https://github.com/KikoTs/BattleSpadesClient"]
[admin]3 keys
The in-game /admin password, command logging and the ban list file.
passwordstringPassword for /admin <password>. Login is disabled while it is changeme, empty, or shorter than 12 characters. Containers take it from BATTLESPADES_ADMIN_PASSWORD.
- Default if omitted
"changeme"
log_commandsbooleanLog every command use (the /admin password is always redacted).
- Default if omitted
true
bans_pathstringBan list file (JSON, keyed by IP address). Survives restarts.
- Default if omitted
"bans.json"
[logging]8 keys
Log level, file, rotation and packet-trace diagnostics. Files live in the logs/ folder next to the server.
levelstringPython log level: DEBUG, INFO, WARNING, ERROR.
- Default if omitted
"INFO"
filestringLog file name, relative to the logs/ folder (absolute paths are used as given).
- Default if omitted
"server.log"
consolebooleanAlso print logs to the console.
- Default if omitted
true
packet_tracebooleanFull packet parsing and hex dumps. Reverse-engineering only; very large logs.
- Default if omitted
false
queue_capacityintegerLog records buffered for the background writer; when full, records are dropped instead of stalling the game.
- Default if omitted
8192- Clamped to
- ≥ 256
max_bytesintegerRotate the log at this size (1 MB–1 GB).
- Default if omitted
16777216- Clamped to
- 1048576 – 1073741824
backup_countintegerRotated log files kept (1–10).
- Default if omitted
3- Clamped to
- 1 – 10
suppress_packetsarrayPacket IDs hidden from debug packet logs (2 = WorldUpdate, 4 = ClientData, 11 = SetColor).
- Default if omitted
[2, 4, 11]- Sample config.toml
[2, 4, 11, 56, 57]
AoSPlay and Steam listing
Step-by-step setup is in Ports and public listing.
[revival]10 keys
The AoSPlay (Revival) server list, player identity tickets, and round results for account progression. The write credential is never read from this file: set AOS_MASTER_WRITE_TOKEN in the server's environment.
enabledbooleanPublish to the AoSPlay server list, accept Revival join tickets and upload round results. It stays off (with a warning) until AOS_MASTER_WRITE_TOKEN is set in the environment.
- Default if omitted
true
base_urlstringAoSPlay API origin. Must be HTTPS (plain HTTP only for localhost). AOS_MASTER_URL overrides it.
- Default if omitted
"https://www.aosplay.net"
public_hoststringPublic IPv4 address or DNS name players use to reach you; used in the listing and the server ID. Replace 127.0.0.1 before going public. AOS_PUBLIC_HOST overrides it.
- Default if omitted
"127.0.0.1"
server_idstringListing identifier. Blank derives public_host:port; if you set it, it must equal exactly that. AOS_SERVER_ID overrides it.
- Default if omitted
""
regionstringRegion shown in the AoSPlay list (ASCII token, up to 31 characters).
- Default if omitted
"europe"
results_pathstringSQLite outbox of round results waiting to be uploaded. Keep it across upgrades; unsent results retry with their original IDs.
- Default if omitted
"state/round-results.sqlite3"
officialbooleanOfficial-listing flag in heartbeats. Community-registered servers are always shown as community regardless of this value.
- Default if omitted
false
require_identitybooleanReject players who connect without a valid Revival or Steam identity ticket (legacy/unranked clients).
- Default if omitted
false
heartbeat_interval_secondsnumberHeartbeat period (15–60 s). Population changes are also published within seconds.
- Default if omitted
30- Clamped to
- 15 – 60
request_timeout_secondsnumberHTTP timeout for AoSPlay requests (1–15 s).
- Default if omitted
5- Clamped to
- 1 – 15
[steam]18 keys
Optional registration in the original retail Steam server browser through an isolated 32-bit helper. Off by default; direct connections and the AoSPlay list work without it. Valve DLLs are never bundled.
enabledbooleanStart the isolated Steam browser registrar. Needs the original 32-bit steam_api.dll in steam-runtime/ (Windows). The Linux container always forces this off.
- Default if omitted
false
app_idintegerMust stay 224540 (Ace of Spades). Startup rejects any other value, including Spacewar's 480.
- Default if omitted
224540
runtime_dirstringFolder containing the original x86 steam_api.dll. Blank uses ./steam-runtime or the BATTLESPADES_STEAM_RUNTIME environment variable.
- Default if omitted
""
steamclient_dirstringFolder with a compatible x86 steamclient.dll, tier0_s.dll and vstdlib_s.dll. Blank auto-discovers an installed desktop Steam.
- Default if omitted
""
helper_pathstringOverride for the bundled x86 bridge helper. Blank uses the bundled one.
- Default if omitted
""
use_supplied_steamclientbooleanCopy a supplied steamclient.dll into the runtime. Keep false: the small legacy DLL from old client folders was measured hanging during Steam initialisation.
- Default if omitted
false
steam_portintegerSteam updater/master UDP port (retail used 8766). Forward it when Steam listing is on.
- Default if omitted
8766
query_portintegerSteam A2S query UDP port; 0 means game port + 1. The game, Steam and query ports must all be different. Forward it when Steam listing is on.
- Default if omitted
0
publicbooleantrue lists publicly; false selects LAN / no-master mode.
- Default if omitted
true
securebooleanRequests VAC mode from the registrar. Leave false: the legacy ticket handshake is not VAC validation. Requires public = true.
- Default if omitted
false
regionstringOptional retail region tag (ASCII letters, digits, _ . -, up to 31 characters).
- Default if omitted
""
game_versionstringAdvertised game version; the original value is 1.0.0.0.
- Default if omitted
"1.0.0.0"
protocol_versionintegerAdvertised protocol tag; retail clients speak 168.
- Default if omitted
168- Clamped to
- ≥ 0
playlist_idintegerPlaylist tag in the retail browser filter string.
- Default if omitted
8- Clamped to
- ≥ 0
texture_skinstringSkin tag; blank auto-selects the mafia skin for VIP and Territory Control.
- Default if omitted
""
require_registrationbooleanWhen true, startup fails unless Steam logon completes within startup_timeout_seconds. False (recommended) keeps the game running during a Steam outage.
- Default if omitted
false
startup_timeout_secondsnumberHow long startup waits for Steam logon (1–60 s).
- Default if omitted
8- Clamped to
- 1 – 60
publish_interval_secondsnumberHow often name/map/population changes are pushed to Steam (0.25–10 s). Runs outside the simulation.
- Default if omitted
1- Clamped to
- 0.25 – 10
Game, lobby and teams
Modes, rotation and votes are explained in Game modes and maps.
[game]12 keys
Startup mode and map plus core simulation switches. Several older keys here are compatibility aliases for [game_rules] entries.
default_modestringMode at startup. Accepts the short codes listed here or their aliases (zombie, occupation, classic_ctf, …). An unknown value stops the server with the list of valid names.
- Default if omitted
"tdm"- Accepted
arenacctfctfdemdiamhoctctdmvipzom
default_mapstringStartup map: a .vxl file name from the maps folder, without the extension. If omitted the server starts on MayanJungle.
- Default if omitted
"MayanJungle"
respawn_timenumbernot in sample configLegacy alias for RULE_RESPAWN_TIMES. Used only when that rule is not set in [game_rules].
- Default if omitted
5
friendly_firebooleanAllow damage to teammates.
- Default if omitted
false
fall_damagebooleanLand fall damage. Water landings are controlled separately by RULE_ENABLE_FALL_ON_WATER_DAMAGE.
- Default if omitted
true
build_damagebooleanAllow weapons and explosions to damage and destroy blocks.
- Default if omitted
true
score_limitintegernot in sample configLegacy generic score limit. Each mode uses its own rule (RULE_*_SCORE_TARGET); this value is only an A2S fallback.
- Default if omitted
10- Clamped to
- ≥ 0
radar_station_lifetime_secondsnumberHow long a deployed radar station lasts (retail 45 s; 1–250).
- Default if omitted
45- Clamped to
- 1 – 250
same_team_collisionbooleanWhether teammates physically block each other. Sent to clients so prediction matches.
- Default if omitted
false
bot_countintegerLegacy fixed bot count, used only when the config has no [bots] table.
- Default if omitted
0
movement_authoritystringserver (production): the server simulates every player. client echoes client positions and exists only for diagnostics.
- Default if omitted
"server"- Accepted
serverclient
map_sync_modestringKeep full. auto (delta sync) is experimental and leaves the client world incomplete.
- Default if omitted
"full"- Accepted
autofull
[lobby]16 keys
Match length, map rotation and voting, end-of-round presentation, and kick-vote limits.
match_length_minutesunknownnot in sample configOptional clock for every mode, in minutes. Only the stock lobby choices are accepted: 5, 10, 15 … 60 (steps of 5) or 90. A [modes.<code>].time_limit still wins. Leave it commented out to use each mode's retail clock.
- Default if omitted
unset
map_rotationarrayMaps offered in the end-of-round vote. Empty uses the mode's retail playlist; if none of those maps is installed, every .vxl in the maps folder. Missing files are skipped with a log line and maps retail marks invalid for the mode are dropped.
- Default if omitted
[]
map_rotation_shufflebooleanShuffle the map list once at startup, like the retail lobby did.
- Default if omitted
true
map_vote_retail_max_playersbooleanOffer maps whose retail player cap is below the current human count only after maps that fit.
- Default if omitted
true
end_screen_secondsnumberHow long the scores screen stays up after the vote before the next map loads (0–120 s).
- Default if omitted
12- Clamped to
- 0 – 120
end_round_scoreboardbooleanHold the scoreboard open at round end.
- Default if omitted
true
end_round_headlinebooleanShow the retail round-result headline (“{team} wins!”, “Draw!”) on the scoreboard.
- Default if omitted
true
map_vote_size_fitbooleanOffer maps sized for the current player count first.
- Default if omitted
true
map_vote_area_per_playernumberPlayable columns wanted per player when ranking maps by size.
- Default if omitted
8000- Clamped to
- 1 – 262144
map_vote_min_areanumberMinimum ideal map area regardless of player count.
- Default if omitted
24000- Clamped to
- 0 – 262144
map_vote_recent_excludeintegerThe last N played maps are offered last.
- Default if omitted
2- Clamped to
- 0 – 32
map_vote_bot_weightnumberHow much a bot counts as a player for map sizing (0–1).
- Default if omitted
1- Clamped to
- 0 – 1
votekick_cooldown_secondsnumberHow long a player (by address) must wait before starting another kick vote. Retail: 300 s.
- Default if omitted
300- Clamped to
- 0 – 3600
votekick_cancelled_cooldown_secondsnumberShorter wait after the starter cancels their own vote. Retail: 45 s.
- Default if omitted
45- Clamped to
- 0 – 3600
votekick_min_team_playersintegerPlayers (bots included) the starter's team needs before a kick vote can start. Retail: 3; 0 disables the check.
- Default if omitted
3- Clamped to
- 0 – 32
map_size_overridestablePin a map's size used by the vote ranking: an area (columns) or small / medium / large, e.g. { WW1 = "large", DragonIsland = 27000 }.
- Default if omitted
{}
[teams]11 keys
Team names, colours, and automatic team balancing at join time and during a match.
team1_namestringTeam name as a client string-table ID. Keep TEAM1_COLOR (shown as “Blue”); literal text shows as “Missing string …” in game.
- Default if omitted
"TEAM1_COLOR"
team2_namestringKeep TEAM2_COLOR (shown as “Green”).
- Default if omitted
"TEAM2_COLOR"
team1_colorarrayRGB colour of team 1 (retail blue). Pure RGB values make HUD and blocks glow.
- Default if omitted
[44, 117, 179]
team2_colorarrayRGB colour of team 2 (retail green).
- Default if omitted
[137, 179, 44]
auto_balancebooleanA joiner who picks a team already balance_threshold players larger is placed on the smaller team, and team switches that would unbalance are refused.
- Default if omitted
true
balance_thresholdintegerPlayer difference that counts as unbalanced.
- Default if omitted
2
balance_mid_matchbooleanAlso repair imbalance during a match (for example after players leave).
- Default if omitted
true
balance_grace_secondsnumberHow long an imbalance must last before anyone is moved.
- Default if omitted
5- Clamped to
- 0 – 600
balance_player_cooldownnumberA human is never moved twice within this many seconds.
- Default if omitted
600- Clamped to
- 0 – 86400
balance_bot_wait_secondsnumberAfter a dead bot fails to fix the imbalance, wait this long before retiring and replacing a bot.
- Default if omitted
10- Clamped to
- 0 – 600
balance_check_intervalnumberHow often the balancer checks, in seconds.
- Default if omitted
1- Clamped to
- 0.1 – 60
[objectives]10 keys
Escape watch (marking players who leave the playable map) and guards that keep intel, diamonds and bombs obtainable.
escape_watch_enabledbooleanMark players who leave the playable map on the enemy minimap so they can be hunted.
- Default if omitted
true
objective_pickup_ends_spawn_protectionbooleanPicking up an objective ends spawn protection.
- Default if omitted
true
objective_pickup_requires_losbooleanObjectives cannot be picked up through walls or floors.
- Default if omitted
true
escape_watch_intervalnumberCheck period in seconds.
- Default if omitted
1- Clamped to
- 0.1 – 60
escape_watch_embedded_secondsnumberTime inside solid terrain before a player is marked.
- Default if omitted
3- Clamped to
- 0 – 600
escape_watch_entomb_secondsnumberTime an objective carrier, VIP, survivor or zone holder may stay sealed in a tiny pocket before being marked.
- Default if omitted
5- Clamped to
- 0 – 600
escape_watch_sky_secondsnumberTime above the map top before a player is marked.
- Default if omitted
5- Clamped to
- 0 – 600
objective_entomb_secondsnumberAn intel, diamond or bomb that is buried, sealed or floating this long is returned or resettled.
- Default if omitted
5- Clamped to
- 0 – 600
objective_afk_secondsnumberPlayers without input this long stop counting towards holding a Territory Control or Multi-Hill zone. 0 disables.
- Default if omitted
60- Clamped to
- 0 – 3600
ctf_base_pit_depthnumberA CTF carrier in an open pit dug up to this many blocks below its base still scores.
- Default if omitted
24- Clamped to
- 0 – 240
[world]10 keys
Content folders and fallback atmosphere. Map sidecar metadata (fog, skybox) overrides the fallbacks for maps that have it.
map_size_xintegerInformational only. Maps are always 512 × 512 × 240; a different value is ignored with a warning.
- Default if omitted
512
map_size_yintegerInformational only. Maps are always 512 × 512 × 240; a different value is ignored with a warning.
- Default if omitted
512
map_size_zintegerInformational only. Maps are always 512 × 512 × 240; a different value is ignored with a warning.
- Default if omitted
240
water_levelintegernot in sample configIgnored (with a warning). The water plane is fixed by the map format.
- Default if omitted
238
water_damagebooleannot in sample configIgnored (with a warning). Retail has no water damage.
- Default if omitted
true
default_skyboxstringSkybox for maps without their own sidecar. Must name a stock client environment file.
- Default if omitted
"User_Grassland.txt"
maps_pathstringFolder containing .vxl maps (relative to the server folder).
- Default if omitted
"maps"
prefabs_pathstringFolder containing .kv6 prefabs.
- Default if omitted
"prefabs"
entities_wire_readybooleanSend map entities (crates, intel) to clients. Keep true.
- Default if omitted
true
fog_color_rgbarrayFallback fog colour; a map's own metadata wins.
- Default if omitted
[12, 13, 11]
[audio]1 keys
Server-driven music.
mode_start_musicbooleanPlay a looping in-round music bed at round start and for joiners. A deliberate deviation: retail rounds were silent (map ambience only) until the final minute. Set false for retail behaviour.
- Default if omitted
true
[weapons]5 keys
Legacy damage keys. They are still parsed but no gameplay code reads them: weapon damage comes from the recovered retail weapon tables, scaled by RULE_WEAPON_DAMAGE.
rifle_damageintegernot in sample configDeprecated and ignored: damage comes from the retail per-weapon tables. Setting it logs a warning; remove the [weapons] table.
- Default if omitted
49
smg_damageintegernot in sample configDeprecated and ignored: damage comes from the retail per-weapon tables. Setting it logs a warning; remove the [weapons] table.
- Default if omitted
29
shotgun_damageintegernot in sample configDeprecated and ignored: damage comes from the retail per-weapon tables. Setting it logs a warning; remove the [weapons] table.
- Default if omitted
27
spade_damageintegernot in sample configDeprecated and ignored: damage comes from the retail per-weapon tables. Setting it logs a warning; remove the [weapons] table.
- Default if omitted
50
grenade_damageintegernot in sample configDeprecated and ignored: damage comes from the retail per-weapon tables. Setting it logs a warning; remove the [weapons] table.
- Default if omitted
100
Bots
Practical recipes are in the bots guide.
[bots]22 keys
Server-owned bots. Their decisions and pathfinding run in a worker thread or process outside the 60 Hz simulation.
enabledbooleanTurn server bots on. Note that a [bots] table without this key means disabled.
- Default if omitted
false- Sample config.toml
true
population_modestringbackfill: keep humans + bots near fill_target. fixed: always run max_bots. admin: only what admins add with /bots.
- Default if omitted
"backfill"- Accepted
backfillfixedadmin
fill_targetintegerBackfill target: bots are added until playing humans (spectators do not count) plus bots reach this number.
- Default if omitted
12- Clamped to
- ≥ 0
max_botsintegerHard cap on bots (and the count used by fixed mode).
- Default if omitted
12- Clamped to
- ≥ 0
reserve_human_slotsintegerSlots always kept free for joining humans; at least 1 is always reserved in every mode. When a human joins a full server a bot leaves.
- Default if omitted
2- Clamped to
- ≥ 0
difficultystringSkill of newly created bot profiles: casual, normal, hard, or mixed.
- Default if omitted
"mixed"- Accepted
casualnormalhardmixed
workerstringthread uses less memory for one casual instance. process isolates a worker crash and is what every official fleet profile uses.
- Default if omitted
"thread"- Accepted
threadprocess
perception_hznumberHow often bots perceive the world (1–30 Hz).
- Default if omitted
10- Clamped to
- 1 – 30
decision_hznumberHow often bots decide (1 Hz up to perception_hz).
- Default if omitted
8- Clamped to
- 1 – bots.perception_hz
path_requests_per_secondintegerPathfinding request budget.
- Default if omitted
24- Clamped to
- ≥ 1
main_thread_budget_msnumberMain-thread time allowed per tick for applying bot intentions.
- Default if omitted
0.75- Clamped to
- ≥ 0.1
seedintegerRandom seed for bot behaviour; 0 is the default.
- Default if omitted
0
behavior_versionstringcooperative tactical squads (default) or classic for the older behaviour layer.
- Default if omitted
"cooperative"- Accepted
classiccooperative
friendly_mischiefbooleanAllow harmless playful behaviour towards teammates.
- Default if omitted
true
chatterbooleanOccasional, rate-limited bot chat after kills, deaths and round events.
- Default if omitted
true
debug_visualizationbooleanEnable /bots debug snapshots. Off in production.
- Default if omitted
false
clean_slate_gamesintegerRecycle the bot planner after this many games/map changes to keep memory flat. 0 disables recycling; per-round state is always reset.
- Default if omitted
3- Clamped to
- ≥ 0
skill_balancebooleanWhen one team's humans clearly out-kill the other side, ease that team's bots and sharpen the other team's.
- Default if omitted
true
skill_balance_max_shiftnumberLargest change to any bot profile field (0–0.9).
- Default if omitted
0.35- Clamped to
- 0 – 0.9
skill_balance_ratenumberChange per second while balancing.
- Default if omitted
0.03- Clamped to
- 0 – 1
skill_balance_deadbandnumberKill-ratio edge ignored as noise (0–0.95).
- Default if omitted
0.15- Clamped to
- 0 – 0.95
skill_balance_min_eventsintegerKills + deaths a team needs before balancing starts.
- Default if omitted
6- Clamped to
- 1 – 1000
Anti-cheat and conduct
How to use these in practice: Admin and moderation.
[anticheat]44 keys
Server-side validation switches and the statistical suspicion report. Checks that could punish honest players on bad connections ship log-only (enforce_* = false).
accuracy_min_marginnumber…and must also beat the median by at least this much.
- Default if omitted
0.15- Clamped to
- 0 – 1
accuracy_min_populationintegerOther players with data needed for the comparison.
- Default if omitted
20- Clamped to
- ≥ 0
accuracy_min_shotsintegerShots with a weapon before its accuracy is compared.
- Default if omitted
100- Clamped to
- ≥ 0
accuracy_percentilenumberAccuracy above this percentile of the population is suspicious…
- Default if omitted
99- Clamped to
- 0 – 100
admin_login_attemptsintegerWrong /admin passwords from one address before the player is kicked and the address banned for 10 minutes.
- Default if omitted
3- Clamped to
- ≥ 0
aim_direction_tolerance_degnumberAllowed aim difference in degrees.
- Default if omitted
10- Clamped to
- ≥ 0
backlog_max_framesintegerQueued input frames tolerated.
- Default if omitted
6- Clamped to
- ≥ 0
enforce_aim_directionbooleanReject shots whose direction disagrees with that frame's aim. Log-only when false.
- Default if omitted
false
enforce_block_intervalbooleannot in sample configEnforce the retail minimum of 0.1 s between one player's block builds. Log-only when false. Not in the sample config.
- Default if omitted
false
enforce_input_backlogbooleanCatch up players whose input queue stays too long (fake lag). Log-only when false.
- Default if omitted
false
enforce_input_starvationbooleanMake airborne players fall and time out completely silent clients when input stops arriving. Log-only when false.
- Default if omitted
false
enforce_shot_originbooleanReject shots, throws and builds whose origin is too far from the server's eye position for that frame. Log-only when false.
- Default if omitted
false
engage_cone_degnumberEngagement cone used by the aim signals.
- Default if omitted
4- Clamped to
- ≥ 0
flag_min_scorenumberSuspicion score at which a player is flagged.
- Default if omitted
1- Clamped to
- ≥ 0
headshot_hit_min_hitsinteger…after at least this many hits.
- Default if omitted
60- Clamped to
- ≥ 0
headshot_hit_rationumberFlag when headshots exceed this share of single-ray hits…
- Default if omitted
0.55- Clamped to
- 0 – 1
headshot_kill_min_killsinteger…after at least this many hitscan kills.
- Default if omitted
30- Clamped to
- ≥ 0
headshot_kill_rationumberFlag when headshots exceed this share of hitscan kills…
- Default if omitted
0.6- Clamped to
- 0 – 1
kick_on_protocol_violationbooleanKick immediately for packets the stock client never sends (NaN values, forged tools, impossible packets).
- Default if omitted
true
pellet_seed_min_shotsintegerShotgun shots needed for the pellet-seed signal.
- Default if omitted
64- Clamped to
- ≥ 0
reaction_acquire_degnumberAngle that counts as having acquired a target.
- Default if omitted
10- Clamped to
- ≥ 0
reaction_history_framesintegerFrames of aim history used to measure reaction time.
- Default if omitted
60- Clamped to
- 1 – 600
reaction_median_msnumberMedian reaction time below this is suspicious.
- Default if omitted
90- Clamped to
- ≥ 0
reaction_min_samplesintegerReaction samples needed.
- Default if omitted
15- Clamped to
- ≥ 0
reaction_reengage_secondsnumberA target counts as new again after this long.
- Default if omitted
3- Clamped to
- ≥ 0
report_backupsintegerRotated report files kept.
- Default if omitted
3- Clamped to
- 0 – 20
report_enabledbooleanWrite the statistical suspicion report. It only flags players, never kicks.
- Default if omitted
true
report_max_bytesintegerRotate the report at this size.
- Default if omitted
5000000- Clamped to
- 4096 – 1073741824
report_pathstringReport file (JSON lines). Review it in game with /acreport and /acstats.
- Default if omitted
"logs/anticheat.jsonl"
shot_origin_tolerancenumberAllowed origin error in blocks.
- Default if omitted
1.5- Clamped to
- ≥ 0
snap_framesinteger…within this many frames…
- Default if omitted
2- Clamped to
- 1 – 30
snap_head_radiusnumber…ending within this radius of a head…
- Default if omitted
0.35- Clamped to
- ≥ 0
snap_margin_degnumber…with this angular margin.
- Default if omitted
0.75- Clamped to
- ≥ 0
snap_min_degnumberAn aim snap is a turn of at least this many degrees…
- Default if omitted
20- Clamped to
- ≥ 0
snap_min_engagedintegerEngagements needed before the snap ratio counts.
- Default if omitted
20- Clamped to
- ≥ 0
snap_min_eventsintegerSnaps needed before the signal counts.
- Default if omitted
4- Clamped to
- ≥ 0
snap_rationumberShare of engagements that are snaps before flagging.
- Default if omitted
0.1- Clamped to
- 0 – 1
starvation_airborne_ticksintegerStarved ticks before an airborne player falls.
- Default if omitted
24- Clamped to
- ≥ 0
starvation_timeout_secondsnumberSilence before a client times out.
- Default if omitted
8- Clamped to
- ≥ 0
summary_interval_secondsnumberPer-player summary log period, and the suspicion report's evaluation period.
- Default if omitted
60- Clamped to
- ≥ 0
sustained_min_countinteger…minimum violations in that window…
- Default if omitted
20- Clamped to
- ≥ 0
sustained_min_minutesnumberWindow for sustained log-only violations…
- Default if omitted
5- Clamped to
- ≥ 0
sustained_per_minutenumber…and minimum rate per minute.
- Default if omitted
3- Clamped to
- ≥ 0
[conduct]15 keys
Team-griefing and AFK kicks, and reserved player names.
afk_exempt_adminsbooleanLogged-in admins are never AFK-kicked.
- Default if omitted
true
afk_kick_secondsnumberKick after this long without real input (movement keys, fire, aim). The clock pauses while loading, dead, or between rounds. 0 disables.
- Default if omitted
600- Clamped to
- ≥ 0
afk_spectator_kick_secondsnumberAFK limit for spectators; 0 exempts them.
- Default if omitted
1800- Clamped to
- ≥ 0
afk_warn_secondsnumberPrivate warning before the AFK kick.
- Default if omitted
540- Clamped to
- ≥ 0
announce_kicksbooleanAnnounce grief/AFK kicks in system chat.
- Default if omitted
true
grief_decay_secondsnumberOne grief point decays every this many seconds.
- Default if omitted
60- Clamped to
- ≥ 0
grief_exempt_adminsbooleanLogged-in admins never earn grief points (bots never do).
- Default if omitted
true
grief_incident_max_pointsnumber…capped at this many points, so one accident cannot kick alone.
- Default if omitted
6- Clamped to
- ≥ 0
grief_incident_secondsnumberEverything within this window counts as one incident…
- Default if omitted
3- Clamped to
- ≥ 0
grief_kick_enabledbooleanKick team griefers. When false, points and warnings still happen but nobody is kicked.
- Default if omitted
true
grief_kick_pointsnumberGrief points that trigger the kick (retail reason “kicked for griefing”).
- Default if omitted
10- Clamped to
- ≥ 0
grief_team_damage_pointsnumberPoints per 100 HP of damage to teammates.
- Default if omitted
1- Clamped to
- ≥ 0
grief_team_kill_pointsnumberPoints per team kill, including deaths caused by setting off a teammate's landmine.
- Default if omitted
3- Clamped to
- ≥ 0
grief_warn_pointsnumberGrief points that trigger a private warning.
- Default if omitted
5- Clamped to
- ≥ 0
reserved_namesarrayExtra names nobody may use, on top of the built-in Server, Admin, Console, System, Moderator, BattleSpades and similar. Look-alike names are caught too.
- Default if omitted
[]
Network, plugins and debug
[network]36 keys
ENet admission and per-tick work budgets, lag compensation, and terrain repair. The shipped values are measured production defaults; most operators never change this table.
timeout_msintegernot in sample configIgnored. Accepted for old configs; a non-default value logs a warning. Retail and this server keep ENet's default peer timeout.
- Default if omitted
10000
max_connectionsintegerENet peer limit for the listen socket.
- Default if omitted
64
bandwidth_limitintegernot in sample configIgnored, like timeout_ms. ENet keeps unlimited (0/0) bandwidth.
- Default if omitted
0
require_protocol_versionbooleanRefuse clients that do not connect with protocol 168, with the retail client-out-of-date or server-out-of-date message.
- Default if omitted
true
event_budgetintegerMaximum ENet events handled per network pump.
- Default if omitted
512- Clamped to
- ≥ 32
max_pending_packetsintegerReceive queue bound before packets are dropped.
- Default if omitted
4096- Clamped to
- ≥ 256
packet_drain_budgetintegerGameplay packets processed per tick.
- Default if omitted
4096- Clamped to
- ≥ 64
plugin_event_budget_msnumberTotal time plugin callbacks may take per event; remaining callbacks are skipped so plugins cannot stall a tick.
- Default if omitted
2- Clamped to
- ≥ 0.1
max_map_mutation_journalintegerTerrain changes remembered for a player who is still downloading the map. Overflow makes that join unsafe and it is refused instead of desynced.
- Default if omitted
8192- Clamped to
- ≥ 64
map_air_catchup_batch_limitintegerBlocks re-sent per input frame when catch-up is enabled.
- Default if omitted
256- Clamped to
- ≥ 1
map_air_catchup_enabledbooleanRe-send every destroyed block to joiners after they spawn. Off: the map download already contains all edits, and this made joiners watch terrain change after spawning.
- Default if omitted
false
unreliable_throttle_decelerationintegernot in sample configENet's unreliable-packet throttle step. 0 keeps every 30 Hz world update flowing to players on jittery links.
- Default if omitted
0- Clamped to
- 0 – 32
entity_tick_batch_limitintegerUpper bound of entity behaviour updates per tick.
- Default if omitted
8192- Clamped to
- ≥ 64
mode_event_queue_limitintegerQueued game-mode events before new ones are dropped (and counted).
- Default if omitted
8192- Clamped to
- ≥ 64
mode_event_drain_budgetintegerGame-mode events processed per tick.
- Default if omitted
512- Clamped to
- ≥ 1
world_mutation_queue_limitintegerPending block edits kept per server.
- Default if omitted
2048- Clamped to
- ≥ 64
world_mutation_batch_limitintegerBlock-edit requests committed per tick.
- Default if omitted
256- Clamped to
- ≥ 1
world_mutation_cell_budgetintegerVoxel cells changed per tick by block edits.
- Default if omitted
4096- Clamped to
- ≥ 64
world_mutation_timeout_ticksintegerTicks a block edit may wait for its movement frame before it is discarded.
- Default if omitted
180- Clamped to
- ≥ 30
prefab_queue_limitintegerPrefab placements waiting to commit.
- Default if omitted
32- Clamped to
- 1 – 128
prefab_cell_batch_limitintegerMap Creator (UGC) model cells committed per tick.
- Default if omitted
16- Clamped to
- 1 – 128
prefab_competitive_cell_budgetintegerCells of competitive prefabs committed per tick across all players. A prefab always commits whole; extra prefabs wait a tick.
- Default if omitted
2048- Clamped to
- 64 – 8192
prefab_validation_batch_limitintegerPrepared Map Creator prefab cells checked against the world per tick.
- Default if omitted
1024- Clamped to
- 64 – 4096
prefab_health_state_batchintegerDamaged block/prefab health rows per packet sent to a joining player.
- Default if omitted
128- Clamped to
- 1 – 4096
lag_compensation_enabledbooleanCheck hitscan and melee hits against where the shooter saw the target (one round trip ago). Only hitboxes are rewound.
- Default if omitted
true
lag_compensation_max_msnumberHard cap on rewind time.
- Default if omitted
250- Clamped to
- 0 – 1000
lag_compensation_extra_msnumberRewind never exceeds the shooter's round trip plus this margin.
- Default if omitted
50- Clamped to
- 0 – 250
lag_compensation_view_delay_msnumberExtra view delay added to the rewind. Keep 0: the retail client extrapolates remote players.
- Default if omitted
0- Clamped to
- 0 – 250
terrain_repair_enabledbooleanLater re-send the true state of blocks whose client-side prediction was rejected, so clients converge.
- Default if omitted
true
terrain_repair_queue_limitintegerPending terrain repairs.
- Default if omitted
8192- Clamped to
- ≥ 64
terrain_repair_batch_limitintegerCells repaired per repair pass.
- Default if omitted
8- Clamped to
- ≥ 1
terrain_repair_interval_ticksintegerTicks between repair passes.
- Default if omitted
3- Clamped to
- ≥ 1
terrain_repair_delay_ticksintegerQuiet period before a repair is sent.
- Default if omitted
120- Clamped to
- ≥ 1
terrain_collapse_repair_batch_limitintegerCells confirmed per pass after a structure collapse.
- Default if omitted
8- Clamped to
- ≥ 1
terrain_collapse_repair_delay_ticksintegerDelay before collapse confirmations start (18 ticks = 0.3 s).
- Default if omitted
18- Clamped to
- ≥ 1
transition_grace_secondsnumberTime between the map-ended packet and closing old sessions on a map change, so retail clients reach the reconnect screen.
- Default if omitted
1.25- Clamped to
- 0 – 5
[plugins]4 keys
Trusted local Python plugins loaded at startup. Plugins are not sandboxed.
enabledbooleanLoad plugins from the plugin folder at startup.
- Default if omitted
true
pathstringPlugin folder. Public top-level .py files containing a BasePlugin subclass are loaded; files starting with _ are skipped.
- Default if omitted
"plugins"
allowlistarrayIf not empty, only these plugins load (file stem or plugin name).
- Default if omitted
[]
denylistarrayPlugins that never load. Always wins over the allowlist.
- Default if omitted
[]
[debug]22 keys
Reverse-engineering and netcode calibration controls. Keep every shipped value in production.
debug_paritybooleanPhysics parity capture over a local UDP socket. Reverse-engineering only.
- Default if omitted
false
debug_parity_hoststringParity capture address.
- Default if omitted
"127.0.0.1"
debug_parity_portintegerParity capture port.
- Default if omitted
32895
debug_parity_queue_capacityintegerParity capture queue bound.
- Default if omitted
256- Clamped to
- ≥ 64
debug_parity_sample_hznumberParity sample rate.
- Default if omitted
10- Clamped to
- 0.1 – 10
debug_parity_flush_intervalnumberParity capture flush period.
- Default if omitted
1- Clamped to
- ≥ 0.1
debug_parity_flush_batchintegerParity records per flush.
- Default if omitted
128- Clamped to
- ≥ 1
broadcast_world_updatesbooleanA/B switch; false stops all position updates. Keep true.
- Default if omitted
true
worldupdate_broadcast_intervalintegerTicks between world updates (2 = retail 30 Hz).
- Default if omitted
2- Clamped to
- ≥ 1
worldupdate_include_selfbooleanSend each player their own correction row. Required by the stock client.
- Default if omitted
true
worldupdate_loop_offsetintegerSelf-row stamp offset. Keep 0.
- Default if omitted
0
worldupdate_self_row_intervalintegerTicks between grounded self rows.
- Default if omitted
2- Clamped to
- ≥ 1
worldupdate_airborne_self_row_intervalintegerTicks between airborne self rows.
- Default if omitted
6- Clamped to
- ≥ 1
jetpack_owner_handoff_input_framesintegerJetpack correction hand-off window.
- Default if omitted
30- Clamped to
- 0 – 120
jetpack_owner_release_handoff_input_framesintegerJetpack release hand-off cap.
- Default if omitted
600- Clamped to
- 0 – 1200
jetpack_activation_defer_framesintegerFrames thrust starts after activation is announced.
- Default if omitted
2- Clamped to
- 0 – 30
jetpack_exhaustion_tail_framesintegerFrames thrust continues after exhaustion is announced.
- Default if omitted
3- Clamped to
- 0 – 30
debug_selfrowbooleanWrite self-row samples for offline calibration. Debug only.
- Default if omitted
false
movement_debug_capturebooleanMovement capture. Debug only.
- Default if omitted
false
movement_input_latch_framesintegerRetail input latch (0 or 1). Keep 1.
- Default if omitted
1- Clamped to
- 0 – 1
input_gap_fill_limitintegerRefill up to this many lost client input frames so a dropped packet does not leave the server a frame behind (0–9).
- Default if omitted
8- Clamped to
- 0 – 9
clock_sync_loop_biasintegerClock-sync bias. Keep 0.
- Default if omitted
0
Map Creator launcher
[map_creator]10 keys
Read only by the separate Map Creator launcher (run_map_creator.py / BattleSpadesMapCreator). The normal game server ignores this table. Command-line flags override these values.
authornot in sample configAuthor credit for a new project.
- Default if omitted
—
descriptionnot in sample configDescription for a new project.
- Default if omitted
—
output_dirstringFolder where standalone projects are saved (relative paths resolve beside the executable). Mutually exclusive with publish_root.
- Default if omitted
—- Sample config.toml
"ugc-projects"
prefab_setnot in sample configPrefab set index for a new project, 0–5.
- Default if omitted
—
projectstringProject name to open or create. An existing .ugc/.vxl/.txt triplet with this name is reopened.
- Default if omitted
"MyUGCMap"
publish_rootstringInstead of output_dir: the client's hosted_ugc folder, so saved maps appear in the stock Publish Map screen.
- Default if omitted
—- Sample config.toml
""
retail_rootstringYour legally installed Ace of Spades folder containing ugc/maps and ugc/kv6. Blank auto-discovers it. BattleSpades does not ship these retail assets.
- Default if omitted
—- Sample config.toml
""
target_modestringMode the map is validated for: tdm, ctf, dem, mh, oc, tc, vip, zom or dia (blank keeps the project's mode, or tdm for a new one).
- Default if omitted
—- Sample config.toml
""
terrainstringBaseplate for a new project: desert, lunar, mountain, grassland, temple, urban, marsh, snowy or water (blank = grassland). Never replaces an existing map.
- Default if omitted
—- Sample config.toml
""
titlenot in sample configDisplay title for a new project.
- Default if omitted
—
Per-mode overrides
Add a [modes.<code>] table to change one mode without touching the others. Alias table names work too ([modes.zombie] applies to zom; if both exist the short-code table wins). Keys a mode does not read are ignored.
[modes.tdm]
time_limit = 1200 # 20 minutes
score_limit = 100
[modes.zom]
infection_delay = 30.0
minimum_players = 4[modes.tdm]Team Deathmatch
| Key | When not set | Meaning |
|---|---|---|
time_limit | [lobby] match_length_minutes, else 900 s | Round/match clock in seconds (0 = no clock). Wins over [lobby].match_length_minutes. |
score_limit | uses RULE_TDM_SCORE_TARGET | Win threshold for the mode (kills, captures, rounds, points…). |
kill_points | 1 | Team points per enemy kill. |
headshot_bonus | 0 | Extra team points for a headshot kill (a custom-server option; retail gives none). |
[modes.ctf]Capture the Flag
| Key | When not set | Meaning |
|---|---|---|
time_limit | [lobby] match_length_minutes, else 1800 s | Round/match clock in seconds (0 = no clock). Wins over [lobby].match_length_minutes. |
score_limit | uses RULE_CTF_SCORE_TARGET | Win threshold for the mode (kills, captures, rounds, points…). |
intel_auto_return | uses RULE_CTF_ENABLE_INTEL_AUTO_RETURN (mode default true) | Dropped intel returns home by itself after a while. |
intel_return_on_touch | uses RULE_CTF_ENABLE_INTEL_RETURN_ON_TOUCH | A defender touching dropped intel returns it immediately. |
intel_in_own_base_to_score | uses RULE_CTF_ENABLE_INTEL_IN_OWN_BASE_TO_SCORE | Your own intel must be at home for a capture to count. |
shoot_with_intel | uses RULE_CTF_ENABLE_SHOOT_WITH_INTEL (mode default false) | Carriers may shoot while holding the intel. |
[modes.cctf]Classic CTF
| Key | When not set | Meaning |
|---|---|---|
time_limit | [lobby] match_length_minutes, else 5400 s | Round/match clock in seconds (0 = no clock). Wins over [lobby].match_length_minutes. |
score_limit | uses RULE_CTF_SCORE_TARGET (mode default 5) | Win threshold for the mode (kills, captures, rounds, points…). |
intel_auto_return | uses RULE_CTF_ENABLE_INTEL_AUTO_RETURN (mode default false) | Dropped intel returns home by itself after a while. |
intel_return_on_touch | uses RULE_CTF_ENABLE_INTEL_RETURN_ON_TOUCH | A defender touching dropped intel returns it immediately. |
intel_in_own_base_to_score | uses RULE_CTF_ENABLE_INTEL_IN_OWN_BASE_TO_SCORE | Your own intel must be at home for a capture to count. |
shoot_with_intel | uses RULE_CTF_ENABLE_SHOOT_WITH_INTEL (mode default true) | Carriers may shoot while holding the intel. |
[modes.zom]Zombie
| Key | When not set | Meaning |
|---|---|---|
time_limit | [lobby] match_length_minutes, else 600 s | Round/match clock in seconds (0 = no clock). Wins over [lobby].match_length_minutes. |
score_limit | uses RULE_ZOMBIE_NOOF_ROUNDS | Win threshold for the mode (kills, captures, rounds, points…). |
first_infected | uses RULE_NOOF_FIRST_INFECTED_ZOMBIES | Players infected when the Zombie round starts. |
infection_delay | 60 | Seconds before the first infection. |
minimum_players | 2 | Players needed before a Zombie round starts (never below 2). |
zombie_respawn_time | 0 | Respawn delay for zombies. |
round_intermission | 5 | Pause between rounds, in seconds. |
round_respawns_per_tick | 4 | Players respawned per tick at a round restart (spreads the load). |
[modes.vip]VIP
| Key | When not set | Meaning |
|---|---|---|
time_limit | [lobby] match_length_minutes, else 900 s | Round/match clock in seconds (0 = no clock). Wins over [lobby].match_length_minutes. |
score_limit | uses RULE_VIP_NOOF_ROUNDS | Win threshold for the mode (kills, captures, rounds, points…). |
selection_delay | 10 | Seconds before one VIP per team is chosen. |
round_intermission | 7 | Pause between rounds, in seconds. |
round_respawns_per_tick | 4 | Players respawned per tick at a round restart (spreads the load). |
minimum_team_size | 1 | Players per team needed before a VIP round starts. |
sudden_death | uses RULE_ENABLE_SUDDEN_DEATH | VIP sudden death at the end of the clock. |
vip_health_multiplier | uses RULE_VIP_HEALTH | VIP health multiplier. |
[modes.mh]Multi-Hill
| Key | When not set | Meaning |
|---|---|---|
time_limit | [lobby] match_length_minutes, else 1500 s | Round/match clock in seconds (0 = no clock). Wins over [lobby].match_length_minutes. |
score_limit | 100 | Win threshold for the mode (kills, captures, rounds, points…). |
max_active_bases | uses RULE_MULTIHILL_MAX_ACTIVE_BASES | Zones/bases active at the same time. |
base_active_time | uses RULE_BASE_ACTIVE_TIME | Seconds a Multi-Hill zone stays active before moving. |
capture_point_resupply | true | Standing in a held capture point refills ammo and blocks. |
capture_point_refill_time | 10 | Seconds between capture-point refills. |
[modes.tc]Territory Control
| Key | When not set | Meaning |
|---|---|---|
time_limit | [lobby] match_length_minutes, else 1500 s | Round/match clock in seconds (0 = no clock). Wins over [lobby].match_length_minutes. |
max_active_bases | uses RULE_TC_MAX_ACTIVE_BASES | Zones/bases active at the same time. |
capture_rate | uses RULE_CAPTURE_RATE | Capture speed multiplier. |
capture_point_resupply | true | Standing in a held capture point refills ammo and blocks. |
capture_point_refill_time | 10 | Seconds between capture-point refills. |
[modes.dia]Diamond Mine
| Key | When not set | Meaning |
|---|---|---|
time_limit | [lobby] match_length_minutes, else 900 s | Round/match clock in seconds (0 = no clock). Wins over [lobby].match_length_minutes. |
score_limit | uses RULE_DIA_SCORE_TARGET | Win threshold for the mode (kills, captures, rounds, points…). |
max_active_bases | uses RULE_DIAMOND_MAX_ACTIVE_BASES | Zones/bases active at the same time. |
max_active_diamonds | uses RULE_MAX_ACTIVE_DIAMONDS | Diamonds on the map at once. |
diamond_lifetime | uses RULE_DIAMOND_LIFETIME | Seconds a dropped diamond lasts. |
[modes.dem]Demolition
| Key | When not set | Meaning |
|---|---|---|
time_limit | [lobby] match_length_minutes, else 900 s | Round/match clock in seconds (0 = no clock). Wins over [lobby].match_length_minutes. |
build_state_length | uses RULE_BUILD_STATE_LENGTH | Demolition build phase in seconds (0 = no build phase). |
[modes.oc]Occupation
| Key | When not set | Meaning |
|---|---|---|
time_limit | [lobby] match_length_minutes, else 900 s | Round/match clock in seconds (0 = no clock). Wins over [lobby].match_length_minutes. |
score_limit | uses RULE_OCC_SCORE_TARGET | Win threshold for the mode (kills, captures, rounds, points…). |
max_active_bombs | uses RULE_MAX_ACTIVE_BOMBS | Bombs active at once (1–3). |
bomb_fuse_time | uses RULE_BOMB_FUSE_TIME | Bomb fuse in seconds. |
[modes.arena]Arena (BattleSpades extension)
| Key | When not set | Meaning |
|---|---|---|
rounds_to_win | 5 | Arena rounds needed to win (alias: score_limit). |
round_time_limit | 180 | Seconds per Arena round. |
time_limit | 0 | Round/match clock in seconds (0 = no clock). Wins over [lobby].match_length_minutes. |
Environment variables
Secrets and deployment-specific values can come from the environment. Variables marked “Docker image” are applied by the container entrypoint only.
| Variable | Applies to | Effect |
|---|---|---|
BATTLESPADES_SERVER_NAME | Docker image | Sets [server] name (up to 64 characters accepted; clients show 31). |
BATTLESPADES_PORT | Docker image | Sets [server] port, the UDP game and A2S port (1–65535). |
BATTLESPADES_MAX_PLAYERS | Docker image | Sets [server] max_players (1–255). |
BATTLESPADES_MODE | Docker image | Sets [game] default_mode (a mode code or alias). |
BATTLESPADES_MAP | Docker image | Sets [game] default_map. A map name, never a path. |
BATTLESPADES_BOT_COUNT | Docker image | Fixed bot population (0–254): sets [bots] to fixed with this many bots, or disables bots at 0. |
BATTLESPADES_REGION | Docker image | Sets [revival] region. |
BATTLESPADES_OFFICIAL | Docker image | Sets [revival] official (true/false, yes/no, on/off, 1/0). |
BATTLESPADES_REQUIRE_IDENTITY | Docker image | Sets [revival] require_identity. |
BATTLESPADES_REVIVAL_ENABLED | Docker image | Sets [revival] enabled. Use false for a private test container. |
BATTLESPADES_ADMIN_PASSWORD | Docker image | Sets [admin] password; must be at least 12 characters. The container refuses to start while the password is still changeme. |
BATTLESPADES_ALLOW_INSECURE_DEFAULTS | Docker image | Set to true only to start a throwaway container with the sample changeme password. |
BATTLESPADES_DATA_DIR | Docker image | Writable data directory, /data or below (default /data). Holds the generated config, logs and bans.json. |
BATTLESPADES_CONFIG_TEMPLATE | Docker image | TOML file used as the template (default: the image's config.toml). Mount your own complete config here to change keys the other variables do not cover. |
BATTLESPADES_RUNTIME_CONFIG | Docker image | Where the effective config is written (default /data/runtime/config.toml). |
AOS_MASTER_WRITE_TOKEN | any launch | The server-scoped aos_srv_… token AoSPlay returns when you register. Required for listing, identity tickets and results; never put it in config.toml. |
AOS_MASTER_URL | any launch | Overrides [revival] base_url. |
AOS_PUBLIC_HOST | any launch | Overrides [revival] public_host: the public IPv4 players connect to. |
AOS_PUBLIC_PORT | any launch | Public UDP game port when it differs from the listen port (router mapping or a relay such as Playit). |
AOS_PUBLIC_QUERY_PORT | any launch | Public A2S port. Defaults to the public game port when AOS_PUBLIC_PORT is set. |
AOS_SERVER_ID | any launch | Overrides [revival] server_id; must equal public_host:public_port. |
BATTLESPADES_STEAM_RUNTIME | any launch | Fallback for [steam] runtime_dir. |
BATTLESPADES_STEAMCLIENT_RUNTIME | any launch | Fallback for [steam] steamclient_dir. |
BATTLESPADES_STEAM_HELPER | any launch | Fallback for [steam] helper_path. |
Common questions
I changed config.toml but nothing happened.+
Restart the server; the file is read once at startup. If it still uses defaults, run --check and look for a TOML parse warning (often a BOM or a missing quote).
Is this list complete?+
Yes for the version shown at the top: it is generated from BattleSpades' server/config.py, server/game_rules.py and the mode sources by scripts/generate-server-reference.py in the website repository.
Can I change settings while the server runs?+
Only a few, through admin commands (/map, /mode, /time, /fog, /bots …, /lockscore, /infiniteblocks). Those changes last until the next restart.

