Context: Nicely rendered view of the doc for coffeetocode/local-play-bootstrap#1
Okay, let's see if we can both unblock people on apple silicon, and put this issue to bed. This is the distilled output of a bunch of testing sessions, intended to that anyone else can test and replicate in a structured way, and quickly see if they get the same results. As much as possible this is structured to have an exact command, the exact error it produces, and the exact command to view that error.
Note: Heavy Claude assistance on this, but I've fully reviewed and heavily edited.
- Goal 1 — Validate that bot developed on OSX can run in the ladder environment. Let an OSX/Apple-Silicon bot author run a local
local-play-bootstrapmatch to completion, so they can confirm their bot boots, plays, and produces a result the same way the AI Arena ladder would run it. Speed does not matter for this goal. - Goal 2 — Do Goal 1 at faster-than-realtime. Same, but fast enough for iterative local testing (sustained ≥ realtime through a full game).
Result up front: Goal 1 is achieved by exactly one configuration (below). Goal 2 is not achieved by any configuration we found, and Section "Goal 2" documents every configuration we tried and precisely why each fails or falls short.
- Confirm this approach to unblock Apple silicon: Follow the steps in "Goal 1 — Validate a bot runs: the one working configuration" and confirm they work for you.
- If you want to dig deeper: We still don't fully know what's happening on the VM when we get the crash described in Blocker C. I don't wan't to call that route fully dead until I know why.
- MacBook Pro, Apple M4, macOS 15.7.1 (build 24G231)
- Docker Desktop 4.49.0 (Docker Engine 28.5.1)
- arenaclient images pinned to tag
2026.05.18-06.43(the post-#172 "new architecture"; see below)
Your Mac model / macOS / Docker version may differ — that is exactly the kind of variation this doc exists to let others test.
- All arenaclient and SC2 images are
linux/amd64only. On Apple Silicon they must run under emulation. There are two emulation paths for Docker on OSX, and they are selected by the Docker backend:- qemu — used by the Docker VMM backend, and by the Apple Virtualization framework backend when Rosetta is off. Full CPU emulation. Slow, but complete.
- Rosetta — used only by the Apple Virtualization framework backend with Rosetta on. Near-native CPU translation. Fast.
- There are two arenaclient architectures:
- Stock v0.8.0 (the default
docker-compose.ymlin this repo). Its bot controller detects a started bot's TCP port via a Linux netlink API. That API is unavailable under qemu, so the stock compose can never complete a match under qemu (Blocker A). - New architecture (sc2-ai-match-controller PR #172; images tagged by date, e.g.
2026.05.18-06.43). Port detection was removed entirely (fixed player seats + shared network namespace). This is whatdocker-compose-apple-silicon.yml(the PR) uses.
- Stock v0.8.0 (the default
Do not trust the Docker Desktop settings file to tell you the running state. In our testing the settings file reported one backend/store while the daemon ran another until a full restart. To be certain, after making a change in Docker Desktop, then click "Apply + Restart", then fully quit Docker Desktop, and relaunch it. Then the config files will reliably match the running configuration.
Verify at runtime:
docker run --rm --platform linux/amd64 python:3.12-slim \
python -c "import socket; socket.socket(socket.AF_NETLINK, socket.SOCK_DGRAM, 4); print('ROSETTA (native syscalls)')"- Prints
ROSETTA (native syscalls)→ you are on Rosetta (Apple VF + Rosetta on). - Raises
OSError: [Errno 93] Protocol not supported→ you are on qemu (Docker VMM, or Apple VF with Rosetta off).
This works because qemu-user does not implement the NETLINK_INET_DIAG protocol (errno 93),
while Rosetta executes the syscall natively. It is the same mechanism as Blocker A below.
docker info --format '{{.Driver}}' # or: docker info | grep -i "Storage Driver"overlay2→ classic image store (what you want).overlayfs+io.containerd.snapshotter.v1→ containerd image store, which breaksdocker composeon this stack (see "Infra gotcha" below). Turn it off.
Relative to the repo root, after a run of docker-compose-apple-silicon.yml:
match/results.json— final result (winner +game_steps), orresult: Erroron failure.logs/game_controller/sc2_controller.log— the controller's view of the game (create-game, websocket errors, timeouts).logs/game_controller/stderr-10001.log/stderr-10002.log— SC2's own stderr for each player seat (this is where you see how far SC2 itself got).logs/bot-controller-1/,logs/bot-controller-2/— bot controller logs; bot stdout/stderr also land inbots/<name>/logs/.
This is the only configuration we found that completes a match on Apple Silicon.
- Docker Desktop → Settings → General:
- Virtual Machine Manager → "Docker VMM" (this is the backend).
- Uncheck "Use containerd for pulling and storing images."
- Apply & Restart. If you have toggled these before, fully quit and relaunch Docker Desktop — an engine-only "Apply & Restart" does not reliably switch either setting.
- Verify the running state (per Diagnostic tools):
docker info | grep -i "Storage Driver" # expect: overlay2 docker run --rm --platform linux/amd64 python:3.12-slim \ python -c "import socket; socket.socket(socket.AF_NETLINK,socket.SOCK_DGRAM,4)" ; echo "exit=$?" # expect: OSError [Errno 93] (i.e. qemu) — Docker VMM is always qemu
- Put your bot(s) and map(s) in place. Bots go in
bots/<name>/with a top-levelrun.py. Maps (.SC2Map) go inmaps/. This repo already shipsbasic_bot,loser_bot, andAcropolisAIE.SC2Map, which the default match uses — so you can validate with zero edits. - Set the match. One line in
match/match-request.csv, 10 fields (ladder CSV + a trailing expected-result), e.g. the shipped default:1,basic_bot,T,python,2,loser_bot,T,python,AcropolisAIE,Player1Win - Run it:
docker compose -f docker-compose-apple-silicon.yml up --abort-on-container-exit
It took about a minute on my machine. The docker compose console will report the combined logs. Here are relevant lines that show it working (full dump at the end of this doc):
match_is_loaded-1 | 2026-08-02T05:01:30.763269Z INFO match_controller/src/match_scheduler/mod.rs:52: Match prepared successfully
...
bot_controller2-1 | 2026-08-02T05:01:33.972095Z INFO bot_controller/src/main.rs:67: Starting bot with command cd "/bot" && "python" "run.py" "--GamePort" "10002" "--LadderServer" "127.0.0.1" "--StartPort" "10002" "--OpponentId" "1"
bot_controller1-1 | 2026-08-02T05:01:33.974658Z INFO bot_controller/src/main.rs:122: Constructing bot command...
bot_controller1-1 | 2026-08-02T05:01:33.976561Z INFO bot_controller/src/main.rs:67: Starting bot with command cd "/bot" && "python" "run.py" "--GamePort" "10001" "--LadderServer" "127.0.0.1" "--StartPort" "10001" "--OpponentId" "2"
match_controller-1 | 2026-08-02T05:01:34.022037Z INFO match_controller/src/match_scheduler/mod.rs:57: Starting match - basic_bot vs loser_bot
match_controller-1 | 2026-08-02T05:01:36.026519Z INFO match_controller/src/match_scheduler/mod.rs:67: Match is running...
...
match_controller-1 | 2026-08-02T05:02:27.072435Z INFO match_controller/src/match_scheduler/mod.rs:82: Match result: AiArenaGameResult { match_id: 1, bot1_avg_step_time: Some(0.0010101066), bot1_tags: Some([]), bot2_avg_step_time: Some(0.0009854488), bot2_tags: Some([]), result: Player1Win, game_steps: 2144 }
match_controller-1 | 2026-08-02T05:02:27.073008Z INFO match_controller/src/match_scheduler/mod.rs:83: Match finished in 53.050445317s
For a real bot and a more substantial game, it can take a long time. The best progress monitor is your bot's own output. The runner writes each bot's stdout/stderr into its folder; follow them:
tail -f bots/<name>/logs/stdout.log bots/<name>/logs/stderr.logThis shows real in-game progress only if your bot prints periodically (e.g. a bot that emits
telemetry every N game-seconds). Heads-up: the bundled basic_bot and loser_bot print
essentially nothing, so their logs stay empty — this heartbeat is for a real bot under test, not
the sample bots.
cat match/results.jsonYou should see a real result with a non-zero game_steps, e.g. Player1Win.
What we tested: 2 different games; basic_bot vs loser_bot and our custom bot Procyon
vs loser_bot.
To re-run, rewrite the (now commented-out) match line and clear stale state:
rm -f match/match-request.tomlExit code 2 from the run is not a crash — it means the actual result differed from the
ExpectedResult field; the real result is still written to match/results.json.
- It is slow. Under Docker VMM/qemu, early/mid game runs roughly around realtime (we measured ~1.1–1.3× realtime early/mid on one bot), but it is not sustained: heavy late-game states (e.g. ~95 workers / 7 bases) drop to ~0.2× realtime because CPU is emulated. A full game is effectively sub-realtime overall. This is why it satisfies Goal 1 but not Goal 2.
- Long games can crash late under qemu. We observed a healthy 20-minute game then
qemu: uncaught target signal 11 (Segmentation fault) - core dumpedatgame_steps: 27465, producingresult: Player1Crash. This is a local-emulation artifact only — the real ladder runs native amd64 — but it means very long local games are not guaranteed to finish. For "does my bot boot, macro, and fight" validation it is sufficient; for reliably playing a full 20-min game to a natural result it is not. - No replays are saved — the new-architecture compose does not mount a replays directory.
- Emulation is unavoidable — the images are amd64-only (see "Why native arm64 isn't an option").
The only path to near-native CPU speed is Rosetta. Rosetta requires the Apple Virtualization framework backend. The blocker is that SC2 itself crashes under Rosetta (Config 2D). Every other configuration is qemu-based and therefore cannot exceed realtime through a full game.
| # | Backend | Rosetta | Architecture | amd64 via | Outcome |
|---|---|---|---|---|---|
| 1 (Goal 1) | Docker VMM | n/a (inert) | new (PR) | qemu | ✅ Completes; slow (see Goal 1) |
| 2A | any qemu backend | off | stock v0.8.0 | qemu | ❌ Blocker A — no match ever completes |
| 2B | Apple VF | off | new (PR) | qemu | |
| 2C | Docker VMM | — | new (PR) | qemu | = Config 1. VMM cannot use Rosetta, so never faster than realtime |
| 2D | Apple VF | on | new (PR) | Rosetta | ❌ Blocker C — SC2 crashes at create-game (this was the only fast candidate) |
| 2E | Apple VF | on | stock v0.8.0 | Rosetta | ❌ Blocker C — SC2 crashes (netlink works under Rosetta, but SC2 still dies) |
Below, each blocker in full.
Applies to: Configs 2A (and the repo's default docker-compose.yml) on any qemu backend.
Command:
docker compose up # the default docker-compose.yml (stock v0.8.0 images)What breaks: every bot fails ~32s after start; no match ever completes; replays/ stays
empty.
Exact error (console, and logs/ proxy log):
ERROR ... api_error_message: "Could not find port for started process"
ERROR ... Failed to start bot 1: ... 400 Bad Request
How to see it directly (root cause, reproducible in one command):
docker run --rm --platform linux/amd64 python:3.12-slim \
python -c "import socket; socket.socket(socket.AF_NETLINK, socket.SOCK_DGRAM, 4)"
# OSError: [Errno 93] Protocol not supportedWhat it means: the v0.8.0 bot controller discovers a bot's TCP port via
socket(AF_NETLINK, SOCK_DGRAM, NETLINK_INET_DIAG). qemu-user does not implement that netlink
protocol → errno 93. The controller swallows the error (empty socket list) and concludes the bot
has no port — for every bot, always.
Dead end? For the stock architecture under qemu, yes — no compose/host-network/platform-pin
change fixes it (we tested docker-compose-host-network.yml and platform: linux/amd64; both
fail identically). This is exactly why the PR exists: the new architecture removed port
detection, so this blocker disappears. Not a Goal-2 avenue on its own (it's qemu → slow), but it
is why "just use the default compose" is not an option on Apple Silicon.
Applies to: Config 2B.
Why it's not a Goal-2 answer: with Rosetta off, Apple VF still emulates amd64 via qemu — so it is not faster than realtime. In our data Apple VF/qemu was actually slower than Docker VMM/qemu (the original ~2.5–3× slower-than-realtime runs were on Apple VF; Docker VMM's faster I/O is what improved this). So Config 2B is strictly worse than Config 1 for speed.
Additional failure on this backend: cold-cache create-game under Apple-VF/qemu exceeds the controller's hardcoded 60s websocket timeout.
Exact error (logs/game_controller/sc2_controller.log):
ERROR ... ws_routes.rs: Sc2Timeout(60s)
SC2's own log (logs/game_controller/stderr-10001.log) shows it was mid create-game
("Create game with map path: ..."), not crashed — it just wasn't finished in 60s.
Workaround (documented, not needed on Docker VMM): a timeout-patched sc2 image
(60s→600s) — see docs/apple-silicon/ in the PR. But since this path is slower than Config 1
anyway, there is no reason to use it for Goal 2.
Dead end for Goal 2? Yes — it's qemu, so it cannot beat realtime; Docker VMM is the better qemu host.
Applies to: Configs 2D (new arch) and 2E (stock). This was the only candidate that could have delivered Goal 2 (Rosetta = near-native CPU).
Setup and gate (this is how to test it credibly):
- Docker Desktop → Settings → General → Apple Virtualization framework, Use Rosetta ON, containerd store OFF, then fully quit & relaunch Docker.
- Verify Rosetta is actually active at runtime before trusting anything (settings file is
not proof):
docker run --rm --platform linux/amd64 python:3.12-slim \ python -c "import socket; socket.socket(socket.AF_NETLINK, socket.SOCK_DGRAM, 4); print('ROSETTA ACTIVE')" # must print: ROSETTA ACTIVE (if it errors with errno 93, you are still on qemu — stop)
- Run the match:
docker compose -f docker-compose-apple-silicon.yml up --abort-on-container-exit
What breaks: SC2 boots fully, the game is created, then SC2 dies ~1 second later. No result
is written (match_controller hangs on "Waiting for results from both players" until it times
out; final result: Error, game_steps: 0).
Exact error (logs/game_controller/sc2_controller.log):
INFO ... websocket/player.rs:204: Game created successfully
ERROR ... ws_routes.rs:131: Sc2Websocket(Protocol(ResetWithoutClosingHandshake))
How to see what actually died — read SC2's own stderr:
tail logs/game_controller/stderr-10001.logIt ends exactly at:
Startup Phase 3 complete. Ready for commands.
... Requesting to join a multiplayer game ...
Create game with map path: AcropolisAIE.SC2Map <-- last line; no error, no core dump
What it means: SC2 runs fine under Rosetta through boot, join, and interface configuration —
so this is not "Rosetta can't run SC2." The host seat's SC2_x64 process is killed by a
fatal signal during map load / simulation init (the ResetWithoutClosingHandshake is just the
controller seeing the dead process's TCP socket reset). The death is silent (no SC2 error, no
core dumped line), which is consistent with a SIGILL/SIGSEGV from Rosetta mistranslating an
instruction in that heavy code path. Crucially, qemu runs the identical binary and map fine
(20-minute games), so the fault is specific to Rosetta's translation, not to the data.
Note on the AVX2 hypothesis: macOS 15 added Rosetta AVX2 support for Linux binaries under the Virtualization framework, so the simple "Rosetta lacks AVX2" explanation does not cleanly hold on 15.7.1 — it may be an AVX2 translation edge case, a different unsupported instruction, or a memory/mmap interaction. We did not pin it down.
Dead end? For running matches: yes, verified. We reproduced this on two runs; the second was runtime-gated (Rosetta confirmed active via the netlink probe immediately before launch) and crashed identically. Rosetta cannot run this SC2 build's create-game path on this machine.
What is left to try / open questions (for anyone who wants to push further):
- Capture the exact signal/opcode. Run
SC2_x64standalone inside the container under Rosetta withlldb(orstrace, or inspect the exit signal /dmesg) while loading a map, to turn "a mistranslated instruction" into a named signal and instruction. If it is a specific Rosetta translation bug, that is worth filing with Apple. - Other macOS / chip versions. Our result is macOS 15.7.1 / M4. A newer macOS point release or different silicon could translate the offending instruction correctly. This is the single most valuable thing for others to test.
- OrbStack as an alternative Docker runtime is untested here. It also uses Rosetta for amd64, so it would likely share this crash — but confirming that (or not) would close the question.
- A newer SC2 Linux build is not an option — the ladder pins the build shipped in the image (4.10 / B75689).
Not one of the two goals, but it cost us hours and will bite testers, so it is documented here.
Trigger: switching Docker Desktop to the Apple Virtualization framework backend silently
enabled Docker's containerd image store. (Check with docker info | grep "Storage Driver":
overlayfs + io.containerd.snapshotter.v1 = on; overlay2 = off.)
What breaks: docker compose on this compose fails to resolve the multi-arch busybox:1.36
image (the only multi-arch image in the stack), even though docker run busybox works fine:
Error response from daemon: {"message":"No such image: busybox:1.36"}
and, once past that, containers vanish on creation:
Error response from daemon: {"message":"No such container: <id>"}
Fix: Docker Desktop → Settings → General → uncheck "Use containerd for pulling and storing
images", then fully quit and relaunch Docker Desktop (an engine-only restart does not switch
the store; confirm docker info shows overlay2).
Two follow-on gotchas:
- The classic and containerd stores are separate: switching empties the visible image list,
so you must re-pull (
docker compose -f docker-compose-apple-silicon.yml pull). The sc2 and bot images are ~8–9 GB each. - Re-pulling on top of the old store's leftover data can fill the Docker VM disk, which
surfaces as
write ... input/output error. Keep several tens of GB free on the host.
- The arenaclient and SC2 images are published amd64-only; there is no arm64 variant to run natively.
- ares-sc2 based bots additionally need the x86-64
sc2_helperbinary (no arm64 Linux build exists upstream). So even a bot that could otherwise run arm64-native is blocked.
Emulation (qemu or Rosetta) is therefore unavoidable on Apple Silicon, which is what makes Goal 2 hard.
| Goal | Status | Configuration |
|---|---|---|
| Goal 1 — validate a bot runs | ✅ Achieved | Docker VMM + docker-compose-apple-silicon.yml (new arch) + stock 2026.05.18-06.43 images + containerd store off. Slow; long games may crash late under qemu; no replays. |
| Goal 2 — faster than realtime | ❌ Not achieved | The only near-native path (Apple VF + Rosetta) crashes SC2 at create-game (Blocker C, verified). All qemu configs are ≤ realtime; Apple-VF/qemu is slower than VMM. |
The single most useful thing another tester can contribute: run the Blocker C setup on different Apple-Silicon hardware / macOS versions, verify Rosetta is active with the netlink probe, and report whether SC2 survives create-game. If it does anywhere, Goal 2 becomes solvable; if it consistently crashes, that strengthens the case that Rosetta + this SC2 build is a hard dead end and worth an upstream (Apple) report.
Full output of a successful run:
$ # Confirm qemu emulation is active by showing that socket.AF_NETLINK isn't supported
$ docker run --rm --platform linux/amd64 python:3.12-slim \
python -c "import socket; socket.socket(socket.AF_NETLINK, socket.SOCK_DGRAM, 4); print('ROSETTA (native syscalls)')"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/usr/local/lib/python3.12/socket.py", line 233, in __init__
_socket.socket.__init__(self, family, type, proto, fileno)
OSError: [Errno 93] Protocol not supported
$ docker compose -f docker-compose-apple-silicon.yml up --abort-on-container-exit
[+] Running 1/1
✔ game_is_ready Pulled 1.0s
[+] Running 12/12
✔ Network local-play-bootstrap_default Created 0.0s
✔ Container local-play-bootstrap-match_is_loaded-1 Created 0.2s
! match_is_loaded The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 0.0s
✔ Container local-play-bootstrap-game_controller-1 Created 0.0s
! game_controller The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 0.0s
✔ Container local-play-bootstrap-game_is_ready-1 Created 0.0s
✔ Container local-play-bootstrap-bot_controller1-1 Created 0.0s
✔ Container local-play-bootstrap-bot_controller2-1 Created 0.0s
✔ Container local-play-bootstrap-match_controller-1 Created 0.0s
! match_controller The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 0.0s
! bot_controller2 The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 0.0s
! bot_controller1 The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 0.0s
Attaching to bot_controller1-1, bot_controller2-1, game_controller-1, game_is_ready-1, match_controller-1, match_is_loaded-1
match_is_loaded-1 | 2026-08-02T05:01:30.758072Z INFO match_controller/src/match_scheduler/mod.rs:29: Preparing match - basic_bot vs loser_bot
match_is_loaded-1 | 2026-08-02T05:01:30.762734Z DEBUG common/src/models/aiarena/aiarena_match.rs:178: Writing match request to file: match_id = 1
match_is_loaded-1 | player_1_id = "1"
match_is_loaded-1 | player_1_name = "basic_bot"
match_is_loaded-1 | player_2_id = "2"
match_is_loaded-1 | player_2_name = "loser_bot"
match_is_loaded-1 | map_name = "AcropolisAIE.SC2Map"
match_is_loaded-1 | player_1_race = 1
match_is_loaded-1 | player_2_race = 1
match_is_loaded-1 |
match_is_loaded-1 | 2026-08-02T05:01:30.763269Z INFO match_controller/src/match_scheduler/mod.rs:52: Match prepared successfully
match_is_loaded-1 | Keep-alive mode enabled. Waiting for termination signal...
game_controller-1 | 2026-08-02T05:01:32.357066Z INFO sc2_controller/src/logging.rs:52: Controller logs initialized.
game_controller-1 | 2026-08-02T05:01:32.368744Z INFO sc2_controller/src/routes.rs:63: SC2 process for player seat 10001 started at port 9390
game_controller-1 | 2026-08-02T05:01:32.376673Z INFO sc2_controller/src/routes.rs:106: WebSocket server for player seat 10001 opened on 0.0.0.0:10001
game_controller-1 | 2026-08-02T05:01:32.377700Z INFO sc2_controller/src/routes.rs:87: WebSocket server for player seat 10001 starting on 0.0.0.0:10001
game_controller-1 | 2026-08-02T05:01:32.379270Z INFO sc2_controller/src/routes.rs:63: SC2 process for player seat 10002 started at port 9645
game_controller-1 | 2026-08-02T05:01:32.379718Z INFO sc2_controller/src/routes.rs:87: WebSocket server for player seat 10002 starting on 0.0.0.0:10002
game_controller-1 | 2026-08-02T05:01:32.379762Z INFO sc2_controller/src/routes.rs:106: WebSocket server for player seat 10002 opened on 0.0.0.0:10002
game_controller-1 | 2026-08-02T05:01:32.380129Z INFO sc2_controller/src/main.rs:21: Player seats opened successfully.
bot_controller2-1 | 2026-08-02T05:01:33.963430Z INFO bot_controller/src/main.rs:108: Controller logs initialized.
bot_controller2-1 | 2026-08-02T05:01:33.968788Z INFO bot_controller/src/main.rs:122: Constructing bot command...
bot_controller1-1 | 2026-08-02T05:01:33.968079Z INFO bot_controller/src/main.rs:108: Controller logs initialized.
bot_controller2-1 | 2026-08-02T05:01:33.972095Z INFO bot_controller/src/main.rs:67: Starting bot with command cd "/bot" && "python" "run.py" "--GamePort" "10002" "--LadderServer" "127.0.0.1" "--StartPort" "10002" "--OpponentId" "1"
bot_controller1-1 | 2026-08-02T05:01:33.974658Z INFO bot_controller/src/main.rs:122: Constructing bot command...
bot_controller1-1 | 2026-08-02T05:01:33.976561Z INFO bot_controller/src/main.rs:67: Starting bot with command cd "/bot" && "python" "run.py" "--GamePort" "10001" "--LadderServer" "127.0.0.1" "--StartPort" "10001" "--OpponentId" "2"
match_controller-1 | 2026-08-02T05:01:34.022037Z INFO match_controller/src/match_scheduler/mod.rs:57: Starting match - basic_bot vs loser_bot
match_controller-1 | 2026-08-02T05:01:36.026519Z INFO match_controller/src/match_scheduler/mod.rs:67: Match is running...
game_controller-1 | 2026-08-02T05:01:40.359649Z INFO websocket{addr=127.0.0.1:55652}: sc2_controller/src/websocket/player.rs:204: Game created successfully
game_controller-1 | 2026-08-02T05:02:26.322960Z INFO websocket{addr=127.0.0.1:55652}: sc2_controller/src/websocket/player.rs:292: Replay saved to "/root/StarCraftII/maps/1_basic_bot_vs_loser_bot.SC2Replay"
game_controller-1 | 2026-08-02T05:02:26.322961Z INFO websocket{addr=127.0.0.1:59108}: sc2_controller/src/websocket/player.rs:292: Replay saved to "/root/StarCraftII/maps/1_basic_bot_vs_loser_bot.SC2Replay"
game_controller-1 | 2026-08-02T05:02:26.326030Z INFO websocket{addr=127.0.0.1:55652}: sc2_controller/src/ws_routes.rs:206: Done
game_controller-1 | 2026-08-02T05:02:26.326088Z INFO websocket{addr=127.0.0.1:59108}: sc2_controller/src/ws_routes.rs:206: Done
game_controller-1 | 2026-08-02T05:02:26.330501Z INFO websocket{addr=127.0.0.1:59108}: sc2_controller/src/ws_routes.rs:258: Game result: AiArenaGameResult { match_id: 1, bot1_avg_step_time: Some(0.0010101066), bot1_tags: Some([]), bot2_avg_step_time: Some(0.0009854488), bot2_tags: Some([]), result: Player1Win, game_steps: 2144 }
game_controller-1 | 2026-08-02T05:02:26.330535Z INFO websocket{addr=127.0.0.1:55652}: sc2_controller/src/ws_routes.rs:258: Game result: AiArenaGameResult { match_id: 1, bot1_avg_step_time: Some(0.0010101066), bot1_tags: Some([]), bot2_avg_step_time: Some(0.0009854488), bot2_tags: Some([]), result: Player1Win, game_steps: 2144 }
game_controller-1 | 2026-08-02T05:02:26.334626Z INFO websocket{addr=127.0.0.1:59108}: sc2_controller/src/ws_routes.rs:262: Game result stored successfully
game_controller-1 | 2026-08-02T05:02:26.334672Z INFO websocket{addr=127.0.0.1:55652}: sc2_controller/src/ws_routes.rs:262: Game result stored successfully
bot_controller1-1 | 2026-08-02T05:02:26.551089Z INFO bot_controller/src/main.rs:70: Bot process exited with status: exit status: 0
bot_controller2-1 | 2026-08-02T05:02:26.551296Z INFO bot_controller/src/main.rs:70: Bot process exited with status: exit status: 0
match_controller-1 | 2026-08-02T05:02:27.072435Z INFO match_controller/src/match_scheduler/mod.rs:82: Match result: AiArenaGameResult { match_id: 1, bot1_avg_step_time: Some(0.0010101066), bot1_tags: Some([]), bot2_avg_step_time: Some(0.0009854488), bot2_tags: Some([]), result: Player1Win, game_steps: 2144 }
match_controller-1 | 2026-08-02T05:02:27.073008Z INFO match_controller/src/match_scheduler/mod.rs:83: Match finished in 53.050445317s
match_controller-1 | 2026-08-02T05:02:27.074111Z DEBUG match_controller/src/matches/sources/test_source/mod.rs:63: Error("EOF while parsing a value", line: 1, column: 0)
match_controller-1 | 2026-08-02T05:02:27.077319Z INFO match_controller/src/match_scheduler/mod.rs:109: Match result submitted
match_controller-1 | Match controller exits
match_controller-1 exited with code 0
Aborting on container exit...
Container local-play-bootstrap-bot_controller2-1 Stopping
Container local-play-bootstrap-match_controller-1 Stopping
Container local-play-bootstrap-bot_controller1-1 Stopping
Container local-play-bootstrap-match_controller-1 Stopped
bot_controller2-1 | 2026-08-02T05:02:27.198731Z INFO bot_controller/src/main.rs:178: Received SIGTERM, shutting down gracefully
bot_controller1-1 | 2026-08-02T05:02:27.198691Z INFO bot_controller/src/main.rs:178: Received SIGTERM, shutting down gracefully
bot_controller2-1 | 2026-08-02T05:02:27.199050Z INFO bot_controller/src/main.rs:32: Bot controller exits
bot_controller1-1 | 2026-08-02T05:02:27.199168Z INFO bot_controller/src/main.rs:32: Bot controller exits
Container local-play-bootstrap-bot_controller2-1 Stopped
Container local-play-bootstrap-bot_controller1-1 Stopped
Container local-play-bootstrap-game_is_ready-1 Stopping
bot_controller2-1 exited with code 0
bot_controller1-1 exited with code 0
Container local-play-bootstrap-game_is_ready-1 Stopped
Container local-play-bootstrap-game_controller-1 Stopping
game_is_ready-1 exited with code 0
Container local-play-bootstrap-game_controller-1 Stopped
Container local-play-bootstrap-match_is_loaded-1 Stopping
game_controller-1 exited with code 143
match_is_loaded-1 | Received SIGTERM, shutting down gracefully
match_is_loaded-1 | Match controller exits
Container local-play-bootstrap-match_is_loaded-1 Stopped
match_is_loaded-1 exited with code 0