Files
SirRoboGarage/DevControlBot_garage/README.md
T
SirStone 59fad00b5c Add DevControlBot garage: skeleton bot with release/debug build script
A minimal Tank Royale bot that boots and stands still. Body, gun and radar
are set to white once at init (team convention: white = not programmed yet);
the tick loop only calls go().

- DevControlBot/ holds the single bot module, its metadata JSON, the
  BotLauncher entry script and the nimble file
- DevControlBot.sh is a thin wrapper around the runBot nimble task:
  release build by default, --debug for the classic diagnostic build
- dependencies resolve at run time from the global nimble store via
  `nimble path`; optional .env (gitignored) supplies env vars such as
  SERVER_URL, with existing environment variables taking precedence
- build happens in a throwaway mktemp dir removed on exit; no artifacts
2026-10-03 16:32:27 +02:00

213 lines
9.6 KiB
Markdown

# DevControlBot
Control skeleton bot: it boots, participates in the battle, and does **nothing**
per tick except `go()` — no movement logic, no scanning, no firing.
Body, gun and radar are plain **white**, which by team convention means "this part
is not programmed yet". Colors are set **once at initialization** (`newDevControlBot`),
never inside the tick loop.
## Layout
```
DevControlBot_garage/
├── AGENTS.md
├── README.md
└── DevControlBot/
├── DevControlBot.nim # bot type + entry point (isMainModule)
├── DevControlBot.json # bot metadata
├── DevControlBot.nimble # the single `runBot` task
├── DevControlBot.sh # BotLauncher entry point
└── .env # OPTIONAL local config (secrets; never commit)
```
There is no `src/`, no `tests/`, no `out/`, no `config.nim`, no `nimble.paths`
and no vendor directory. `DevControlBot.nim` is both the bot implementation and
the program entry point.
## Dependencies
Only two, both installed **globally** with Nimble and resolved from the global
package store — nothing vendored, no `nimble.paths`, no `nimble develop`:
- `robocode_tankroyale_botapi >= 1.0.7` (the renamed `tankroyale_botapi`)
- `jsony >= 1.1.5`
```sh
nimble install robocode_tankroyale_botapi
```
The `runBot` task asks Nimble where each of those packages actually lives
(`nimble path <pkg>`) and passes explicit `--path:` flags to `nim c`. This is
required because the compiler's own `nimblepath="$home/.nimble/pkgs2/"` only
works when `$HOME` is set — with an empty environment (`env -i`, Nix build
sandbox, CI) the packages are otherwise not on the search path. If a package is
not installed, the build stops with `run: nimble install <pkg>` instead of a
compiler error.
## Run
```sh
./DevControlBot/DevControlBot.sh
```
`DevControlBot.sh` is the **BotLauncher entry point**. It is a thin wrapper: it
locates itself, `chdir`s to the directory holding the `.nimble` file, forwards
arguments and signals to `nimble runBot`, and propagates the real exit code.
All build/run/cleanup logic lives in the `runBot` task of `DevControlBot.nimble`,
which creates a throwaway `mktemp -d` build dir, compiles there, runs, and always
removes the dir via an `EXIT`/`INT`/`TERM` trap. **Nothing is left behind** — this
bot never creates an `out/` directory.
`runBot` also accepts a metadata path: **as the first positional argument**. If it
ends in `.json` it is used instead of the bundled `DevControlBot.json`; there is
no `--json` flag (an argument that does not end in `.json` is forwarded to the bot
as-is).
```sh
./DevControlBot/DevControlBot.sh # bundled metadata (default)
./DevControlBot/DevControlBot.sh /path/to/my.json # alt metadata
./DevControlBot/DevControlBot.sh --debug /path/to/my.json # alt metadata, debug build
```
With **no arguments at all** the wrapper does *not* print usage: it performs a
quiet RELEASE build and runs the bot with the bundled `DevControlBot.json`,
exactly as if that path had been passed. Usage is printed **only** for an
explicit `-h` / `--help` (exit 0, nothing is built).
## Configuration comes from the environment — the API's own mechanism
No configuration code exists in `DevControlBot.nim`, and none is needed. The
bot API reads its own environment variables inside `start()`:
| variable | read at | default |
|---|---|---|
| `SERVER_URL` | `robocode_tankroyale_botapi.nim:424` (documented at `:396`) | `ws://localhost:7654` |
| `SERVER_SECRET` | `robocode_tankroyale_botapi.nim:425` | `""` |
| `BOT_NAME`, `BOT_VERSION`, `BOT_AUTHORS`, … | `bot_info.nim:117-130`, used only when no JSON is found | see that file |
This is the supported mechanism the official BotLauncher uses: it puts those
variables in the bot's process environment before starting the binary. So the
only thing the build/run wrapper does is make sure the variables are *present*
in the bot's environment for local runs.
## Optional `.env` (a convenience for local runs only)
`DevControlBot/.env` is **optional**. If it is absent nothing happens: no error,
no warning beyond one informational line, the run behaves exactly as before and
the API defaults apply. It is not required, not generated, and not committed.
When present it is loaded by the **`runBot` task**, not by `DevControlBot.sh`, so
it applies identically to `./DevControlBot.sh` and to a direct `nimble runBot`
and the wrapper stays a thin wrapper. Properties:
- **Format:** plain `NAME=value` and `export NAME=value` lines; blank lines and
`#` comments ignored; anything that is not a simple `NAME=value` identifier
assignment is ignored (a `.env` can never smuggle in a command).
- **CRLF tolerant:** `CR` characters are stripped, so a Windows-edited file does
not turn the value into `bar\r`.
- **Nothing is ever printed.** Only the *names* taken from the file are reported,
never a value, and `set -x` is not used.
- **Overridable path:** `$DEVCONTROLBOT_ENV_FILE` points the loader at a
different file (useful for testing).
### Precedence: the environment always wins
A variable **already present in the environment is never overridden** by the
`.env` file. This matters because the official BotLauncher's values must win:
if a `.env` sitting in the checkout could overwrite them, this convenience would
silently break a real BotLauncher run.
The `.env` file is therefore **not** simply sourced — sourcing it would do the
opposite, because an assignment in a sourced file overwrites an already-exported
variable of the same name. Instead the file is **filtered line by line**: a
`NAME=value` line is dropped when `NAME` is already set in the environment
(tested with `[ -n "${NAME+x}" ]`, i.e. *set but possibly empty*, not merely
non-empty), and only the still-unset names are then exported with `set -a` and
sourced. The filtering is what implements the precedence.
```sh
SERVER_URL=ws://host:1234 ./DevControlBot.sh # shell wins, .env ignored for SERVER_URL
./DevControlBot.sh # .env supplies SERVER_URL
```
`.env` contains secrets, so it must never be committed; see the note in
[AGENTS.md](AGENTS.md) about the root `.gitignore`.
## Build modes: release by default, `--debug` for the classic build
| invocation | build | output |
|---|---|---|
| `DevControlBot.sh …` (default) | `nim c -d:release --hints:off` | quiet: dependency lines, one `build ok (release)`, one `running` line |
| `DevControlBot.sh --debug …` | plain `nim c` | classic diagnostics: `Hint: used config file …`, dot-progress line, `DEBUG BUILD` hint |
`--debug` is stripped by the wrapper wherever it appears in the arguments (a
repeated `--debug` is harmless) and is handed to the `runBot` task via the
exported env var `$DEVCONTROLBOT_DEBUG` (`debug` / `release`) rather than as a
task argument: an env var can never be mistaken for a bot argument, cannot
collide with the `$DEVCONTROLBOT_STATUS_FILE` exit-code mechanism, and leaves the
task's argument parsing untouched. Every other argument is forwarded verbatim.
In release mode the compiler's stdout/stderr is captured to a log and replayed
**only if the build fails**, so a failed build is always loud in both modes while
a successful release build stays short (10 lines including the no-server report,
vs 16 with `--debug`). `--help` / `-h` prints usage and exits 0 **without
building**; no arguments does the opposite — it builds and runs.
The bot needs a running Tank Royale server on `ws://localhost:7654`; without one
the run fails with `[start] Cannot connect to ws://localhost:7654` and exits 1
(expected during local verification).
## Failure reporting and exit codes
Nimble's `exec` raises a NimScript exception — stack trace, escaped copy of the
whole script — for *any* non-zero exit, which made "no game server" look like a
catastrophic build crash. The `runBot` shell therefore always exits 0 and writes
the real code to the status file named by `$DEVCONTROLBOT_STATUS_FILE`, which
`DevControlBot.sh` exports and then re-exits with. The real code is preserved,
not masked.
| exit | meaning |
|---|---|
| `0` | clean run |
| `1` | bot could not connect to the game server on `ws://localhost:7654` (expected locally) |
| `2` | a dependency is not installed — `nimble install <pkg>` |
| `3` | compilation failed — the compiler's own errors are printed above |
| other | the bot's own exit code (runtime crash, bad metadata) |
Each mode prints its own short line, e.g.
```
[start] Cannot connect to ws://localhost:7654: Connection refused
[devcontrolbot] bot exited with code 1.
[devcontrolbot] No game server on ws://localhost:7654.
[devcontrolbot] Start the Tank Royale server, or check it is listening on port 7654.
```
No `Exception raised during nimble script execution`, no stack trace, no source
dump. `nimble runBot` run directly (no status file) keeps the old behaviour and
still raises.
Interrupt handling is unchanged: SIGINT/SIGTERM are forwarded, the build dir is
removed by the `EXIT` trap and the wrapper exits 143 (SIGTERM) / 130 (SIGINT).
## Gotcha: `nimble runBot` must be run from `DevControlBot/`
The `.nimble` file lives in `DevControlBot/`, so the task is only visible from
there:
```sh
cd DevControlBot && nimble runBot # works
cd DevControlBot_garage && nimble runBot
# Error: Could not find a file with a .nimble extension inside the specified
# directory: .../DevControlBot_garage
```
`DevControlBot.sh` handles this for you by `cd`-ing itself. Note also that
`compile`/`build` are reserved Nimble builtin names — hence the `runBot` name.
## Test
There are **no tests** in this garage. The `test` task and the `tests/` directory
were removed along with the old build layout; see
[AGENTS.md](AGENTS.md) for the convention to follow when tests are added.