Skip to content

Instantly share code, notes, and snippets.

@coffeetocode
Created August 2, 2026 06:18
Show Gist options
  • Select an option

  • Save coffeetocode/2b5e108d8bd2d8eb1066df3dca2c9d92 to your computer and use it in GitHub Desktop.

Select an option

Save coffeetocode/2b5e108d8bd2d8eb1066df3dca2c9d92 to your computer and use it in GitHub Desktop.
Running local-play-bootstrap on Apple Silicon — tested configurations & replication guide

Context: Nicely rendered view of the doc for coffeetocode/local-play-bootstrap#1

Running local-play-bootstrap on Apple Silicon — tested configurations & replication guide

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.

My two goals

  1. 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-bootstrap match 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.
  2. 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.

What others can do

  • 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.

Environment these results were produced on

  • 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.

Essential background (why this is hard)

  • All arenaclient and SC2 images are linux/amd64 only. 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.yml in 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 what docker-compose-apple-silicon.yml (the PR) uses.

Diagnostic tools — run these first to know what your machine is actually doing

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:

1. Which emulation is active right now? (qemu vs Rosetta)

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.

2. Which image store is active? (matters for docker compose)

docker info --format '{{.Driver}}'         # or: docker info | grep -i "Storage Driver"
  • overlay2 → classic image store (what you want).
  • overlayfs + io.containerd.snapshotter.v1containerd image store, which breaks docker compose on this stack (see "Infra gotcha" below). Turn it off.

3. Where the logs are (new-architecture compose)

Relative to the repo root, after a run of docker-compose-apple-silicon.yml:

  • match/results.json — final result (winner + game_steps), or result: Error on 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.logSC2'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 in bots/<name>/logs/.

Goal 1 — Validate a bot runs: the one working configuration

Configuration: Docker VMM + new-architecture compose + stock images

This is the only configuration we found that completes a match on Apple Silicon.

Exact setup

  1. 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.
  2. 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
  3. Put your bot(s) and map(s) in place. Bots go in bots/<name>/ with a top-level run.py. Maps (.SC2Map) go in maps/. This repo already ships basic_bot, loser_bot, and AcropolisAIE.SC2Map, which the default match uses — so you can validate with zero edits.
  4. 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
    
  5. 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

Watching progress while it runs (it's slow — is it alive or stuck?)

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.log

This 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.

How to confirm it worked

cat match/results.json

You 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.toml

Exit 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.

Limits / caveats of this configuration (important, be honest about these)

  • 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 dumped at game_steps: 27465, producing result: 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").

Goal 2 — Faster than realtime: not achieved; every configuration tried

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.

Configuration space (summary)

# 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 ⚠️ Completes but slower than VMM + needs timeout workaround (Blocker B)
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.

Blocker A — stock v0.8.0 compose under qemu: "Could not find port for started process"

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 supported

What 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.

Blocker B — Apple VF + Rosetta OFF (new arch, qemu): slow, and Sc2Timeout(60s)

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.

Blocker C — Apple VF + Rosetta ON: SC2 crashes at create-game (the promising path, dead)

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):

  1. Docker Desktop → Settings → General → Apple Virtualization framework, Use Rosetta ON, containerd store OFF, then fully quit & relaunch Docker.
  2. 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)
  3. 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.log

It 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_x64 standalone inside the container under Rosetta with lldb (or strace, 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).

Infra gotcha — the containerd image store breaks docker compose on this stack

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.

Why native arm64 isn't an option (so "just don't emulate" is off the table)

  • 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_helper binary (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.


Summary

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.

Appendix

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment