Configuration

The framework reads ibf\ibf.toml in the game folder when the game starts. ibf setup writes it from the default (also shipped as ibf.toml.example). Every key has a default, so a key you leave out keeps it. A key marked live takes effect when you save the file, with the game running; the others need the game restarted. A key with a bad value is reported in the log and keeps its default; a file that is not valid TOML keeps the framework off until it is fixed.

[host]

Key Default
enabled true false starts the game without the framework (so does the environment variable IBF_DISABLE=1)
dev_mode false developer commands (debug.*, overlay.arrange); a copy of the host is loaded, so it can be rebuilt while the game runs
command_pipe false a named pipe that takes console commands from scripts (tools\ibfctl.py)

[input]

Key Default
bot_pads [] (ibf setup: [0, 1]) the player slots bots play, from 0: virtual controllers the game sees from its start. ibf match needs pads 0 to n-1
script_pads [] slots the pad.* console commands drive instead; a slot is a bot pad or a script pad
keep_focus true the game keeps running when its window loses focus (live)
minimize "run" a minimized game keeps playing; "pause" lets it pause as it would (live)
user_kbmouse "focus" your keyboard and mouse reach the game only while its window is in focus; "on", "off" (live)
user_at_start "off" your keyboard and mouse are held back until the toggle key's first press; "on": from the start
toggle_key "F5" the key that lets your keyboard and mouse through, or holds them back
real "allow" real controllers: "allow", "block_pads", or "block_all" (also the keyboard and mouse) (live)
real_pad_map {} puts a real controller in another slot, e.g. { 0 = 2 }. Once it names a controller, the real controllers it does not name are hidden from the game (live)
timeout_ms 250 a bot pad that stops being written reads as neutral after this long (live)
min_press_ticks 2 the shortest press the SDKs make, in ticks (live)
backend "xinput" how virtual controllers reach the game; "rawhid" is reserved and not built yet (the XInput backend answers, with a warning in the log)

[controls] (live)

The pad input each game action becomes: what bots' actions turn into, and what the Navigator presses in menus. Change them if you changed the game's own controls.

Key Default
move "left_stick" (or "right_stick", "dpad")
shoot_up, shoot_down, shoot_left, shoot_right "y", "a", "x", "b"
pause, confirm, back "start", "a", "b"

[mode] (live)

Key Default
run "realtime" "lockstep": the game waits for every bot pad's answer to each tick (Matches)
lockstep_timeout_ms 1000 how long it waits for each tick's answers
lockstep_miss "neutral" a bot that misses the wait: "neutral" until it catches up, and not waited for meanwhile; "hold": it keeps its last input for that tick only, and the next tick waits for it again (Matches)

[overlay]

Key Default
enabled true false: no overlay at all (bots still play and draw nothing)
scale "auto" the overlay's size: "auto" follows the window, or 0.5 to 4.0
hotkeys { hide = "F4", console = "F2", help = "F1" } keys like "F1", "Insert", "Ctrl+Shift+F12"

[bridge]

enabled = true: the shared memory bots connect to. Off, no bot or tool can connect.

[bridge.tcp]

The bridge over TCP: bots on another machine, on Linux, or in containers of their own connect to tcp://host:port (C and C++ bots, ibf run --url). Off by default.

Key Default
enabled false listen
bind "127.0.0.1:47800" where: this machine only, or one of its addresses ("0.0.0.0:47800" for all of them)
token "" a client that sends it (--token) is a full client: commands, any bot pad, the host's log. An address other than loopback needs one

Each connection is one more client of the bridge, so it counts against the 8. On loopback with no token, any program on the machine may connect, as with shared memory. The console's tcp.allow <token> pad=<n> pins a token to one pad: a bot that connects with it has that pad bound, and may play it, draw, declare parameters and log, but not run commands, take another pad or read the host's log. ibf match tcp:<token> and the arena's bot containers use such tokens (the arena). tcp.allow <token> watch makes a watch token: a spectator that reads the snapshots and the state and does nothing else (ibf match --watch-token, the arena's observers). tcp.revoke <token> forgets a token and ends its connection. tcp.token <token> tells, as JSON, a token's kind and pad, whether it is connected, how many connections it has had, and how the last one ended (goodbye, closed, silent, protocol, kicked, broken, slow_reader, revoked, host). tcp shows the listener and its connections. One address may hold 4 connections at a time.

A full client over TCP runs commands as a local one does, with one difference: the files its commands write (record.start, state.dump, state.capture, screenshot, overlay.screenshot, debug.frametrace) go into ibf\remote\ on the game's machine, named by a relative path (no .., drive or root), so nothing it sends reaches IBF's own files or anything else on that machine. Their default names (no file given) are the host's, as for anyone.

[log]

Key Default
level "info" trace, debug, info, warn, error, off (live)
json false also write ibf-*.jsonl
file_max_mb, file_keep 20, 10 each log's size, and how many are kept
console_window false a separate console window with the log
debugger false also send the log to an attached debugger (OutputDebugString)

Logs are in ibf\logs\: ibf-<date>-<time>-<pid>.log for each run, and shim.log.

[state]

Key Default
profile "" force one profile file (relative to ibf\); empty: chosen by Inversus.exe's SHA-256 (live)
tick_source "auto" where ticks come from: "sim_step" (the simulation's step), "present" (each frame)

[plugins]

[plugins]
enabled = ["hello"]          # ibf\plugins\hello.dll

[plugins.hello]              # read by the plugin (live)
greeting = "hi"

See Plugins.