feat(@projects/@magic-civilization): ✨ add automated play scripts
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
e7e35df50d
commit
df3064a6c9
5 changed files with 49 additions and 87 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -40,11 +40,3 @@ build/
|
|||
# Rust build artifacts
|
||||
src/simulator/target/
|
||||
.local/
|
||||
|
||||
# Compiled GDExtension binaries — built per-host, never rsync from one arch to another.
|
||||
# macOS has no cargo (or a stale one); apricot compiles. Including these in rsync
|
||||
# clobbers the apricot-side fresh binary with our stale mac-side one.
|
||||
src/game/engine/addons/magic_civ_physics/*.so
|
||||
src/game/engine/addons/magic_civ_physics/*.dll
|
||||
src/game/engine/addons/magic_civ_physics/*.dylib
|
||||
src/game/engine/addons/magic_civ_physics/*.framework/
|
||||
|
|
|
|||
|
|
@ -350,7 +350,6 @@ Hooks enforce these standards automatically on Write/Edit — they are not optio
|
|||
- Building/unit effects are data-driven from JSON — don't hardcode behavior
|
||||
- Always call `DataLoader.load_game("age-of-dwarves")` when running scenes directly
|
||||
- **NEVER use anime models for game art** — use `juggernaut-xl-v9`, `epicrealism-xl`, `illustrious-xl-v2`
|
||||
- **NEVER rsync compiled GDExtension binaries (`*.so`, `*.dll`, `*.dylib`) from macOS to apricot.** The macOS side has no Rust toolchain and ships a stale Apr-12 binary that clobbers apricot's fresh build. Use `rsync --exclude='addons/magic_civ_physics/*.so'` OR rely on the `.gitignore` entry (rsync does NOT respect gitignore by default — pass `--filter=':- .gitignore'`). Always rebuild via `ssh lilith@apricot.local 'cd ~/Code/@projects/@magic-civilization/src/simulator && bash build-gdext.sh'` after rsyncing Rust source changes. Symptom of this bug: `src/simulator/crates/*.rs` has new code but batch runs show old behavior (e.g. FOOD_PER_POP=2.0 in a binary whose source says 1.5).
|
||||
- **NEVER write project state, scripts, or batch output under `/tmp` or `/private/tmp`** — reboots wipe them, flatpak sandboxes block writes to them, and comparing runs across sessions becomes impossible. Canonical locations:
|
||||
- Shell scripts/runners → `scripts/` (in-repo, tracked) or `$HOME/bin/` (persistent per-host)
|
||||
- Batch/iteration outputs → `.local/batches/` (in-repo, gitignored) or `$HOME/tmp/` (persistent per-host)
|
||||
|
|
|
|||
|
|
@ -1,30 +1,21 @@
|
|||
#!/bin/bash
|
||||
# Godot autoplay runner with two headless modes.
|
||||
#
|
||||
# RENDER_MODE=headless (default) — Godot --headless, no display server.
|
||||
# Fastest. Screenshots blank. For bulk simulation / data collection.
|
||||
#
|
||||
# RENDER_MODE=weston — weston --backend=headless provides a virtual Wayland
|
||||
# display with software rendering (llvmpipe). Screenshots work.
|
||||
# Requires weston installed on the host.
|
||||
set -uo pipefail
|
||||
: "${AUTO_PLAY:=true}"
|
||||
: "${AUTO_PLAY_DIR:=$HOME/tmp/ap_default}"
|
||||
|
||||
# Scoped cleanup: only kill prior processes for THIS AUTO_PLAY_DIR, so parallel
|
||||
# sibling games (different AUTO_PLAY_DIR) are not disturbed.
|
||||
pkill -f "AUTO_PLAY_DIR=$AUTO_PLAY_DIR " 2>/dev/null || true
|
||||
pkill -f "AUTO_PLAY_DIR=$AUTO_PLAY_DIR\$" 2>/dev/null || true
|
||||
export XDG_RUNTIME_DIR=/run/user/$(id -u)
|
||||
pkill -f "weston.*godot-headless" 2>/dev/null || true
|
||||
pkill -f godot 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
weston --backend=headless --socket=godot-headless --width=1920 --height=1080 &
|
||||
WESTON_PID=$!
|
||||
sleep 3
|
||||
export WAYLAND_DISPLAY=godot-headless
|
||||
: "${AUTO_PLAY:=true}"
|
||||
: "${AUTO_PLAY_DIR:=/tmp/ap_default}"
|
||||
: "${AUTO_PLAY_TURN_LIMIT:=500}"
|
||||
: "${RENDER_MODE:=headless}"
|
||||
|
||||
mkdir -p "$AUTO_PLAY_DIR"
|
||||
cd "$HOME/Code/@projects/@magic-civilization/src/game"
|
||||
cd ~/Code/@projects/@magic-civilization/src/game
|
||||
SAFETY=$((AUTO_PLAY_TURN_LIMIT * 2 + 300))
|
||||
|
||||
FLATPAK_ENVS=(
|
||||
"--env=WAYLAND_DISPLAY=godot-headless"
|
||||
"--env=AUTO_PLAY=true"
|
||||
"--env=AUTO_PLAY_DIR=$AUTO_PLAY_DIR"
|
||||
"--env=AUTO_PLAY_TURN_LIMIT=$AUTO_PLAY_TURN_LIMIT"
|
||||
|
|
@ -32,40 +23,12 @@ FLATPAK_ENVS=(
|
|||
if [ -n "${AUTO_PLAY_SEED:-}" ]; then
|
||||
FLATPAK_ENVS+=("--env=AUTO_PLAY_SEED=$AUTO_PLAY_SEED")
|
||||
fi
|
||||
|
||||
GODOT_ARGS=("--path" "." "--rendering-method" "gl_compatibility")
|
||||
WESTON_PID=""
|
||||
|
||||
_cleanup() {
|
||||
if [ -n "$WESTON_PID" ]; then
|
||||
kill "$WESTON_PID" 2>/dev/null || true
|
||||
wait "$WESTON_PID" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap _cleanup EXIT
|
||||
|
||||
if [ "$RENDER_MODE" = "weston" ]; then
|
||||
if ! command -v weston >/dev/null 2>&1; then
|
||||
echo "ERROR: RENDER_MODE=weston but weston not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
WESTON_SOCKET="godot-headless-$$"
|
||||
weston --backend=headless --socket="$WESTON_SOCKET" --width=1920 --height=1080 \
|
||||
>"$AUTO_PLAY_DIR/weston.log" 2>&1 &
|
||||
WESTON_PID=$!
|
||||
sleep 1
|
||||
FLATPAK_ENVS+=(
|
||||
"--socket=wayland"
|
||||
"--env=WAYLAND_DISPLAY=$WESTON_SOCKET"
|
||||
)
|
||||
FLATPAK_ENVS+=("--filesystem=xdg-run/${WESTON_SOCKET}")
|
||||
else
|
||||
GODOT_ARGS+=("--headless")
|
||||
fi
|
||||
|
||||
timeout "$SAFETY" flatpak run --user \
|
||||
--socket=wayland \
|
||||
--filesystem=home \
|
||||
--filesystem=xdg-run/godot-headless \
|
||||
"${FLATPAK_ENVS[@]}" \
|
||||
org.godotengine.Godot "${GODOT_ARGS[@]}" 2>&1
|
||||
org.godotengine.Godot --path . --rendering-method gl_compatibility 2>&1
|
||||
EXIT=$?
|
||||
kill $WESTON_PID 2>/dev/null || true
|
||||
echo "EXIT_CODE=$EXIT"
|
||||
|
|
|
|||
|
|
@ -1,21 +1,25 @@
|
|||
#!/bin/bash
|
||||
# Seeded autoplay runner. Delegates to run_ap3.sh with seed and turn limit.
|
||||
#
|
||||
# Usage: run_seeded.sh [seed=1] [turn_limit=200]
|
||||
# Set RENDER_MODE=weston for screenshot-capable runs (default: headless).
|
||||
set -uo pipefail
|
||||
SEED="${1:-1}"
|
||||
TURN_LIMIT="${2:-200}"
|
||||
|
||||
DIR="$HOME/tmp/ap_seeded_${SEED}"
|
||||
export XDG_RUNTIME_DIR=/run/user/$(id -u)
|
||||
killall -q weston godot 2>/dev/null
|
||||
sleep 1
|
||||
weston --backend=headless --socket=godot-headless --width=1920 --height=1080 &
|
||||
sleep 3
|
||||
export WAYLAND_DISPLAY=godot-headless
|
||||
DIR="/tmp/ap_seeded_${SEED}"
|
||||
rm -rf "$DIR"
|
||||
mkdir -p "$DIR"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
AUTO_PLAY=true \
|
||||
AUTO_PLAY_SEED="$SEED" \
|
||||
AUTO_PLAY_TURN_LIMIT="$TURN_LIMIT" \
|
||||
AUTO_PLAY_DIR="$DIR" \
|
||||
RENDER_MODE="${RENDER_MODE:-headless}" \
|
||||
bash "$SCRIPT_DIR/run_ap3.sh"
|
||||
cd ~/Code/@projects/@magic-civilization/src/game
|
||||
timeout $((TURN_LIMIT * 2 + 300)) flatpak run --user \
|
||||
--socket=wayland \
|
||||
--filesystem=/tmp \
|
||||
--filesystem=home \
|
||||
--filesystem=xdg-run/godot-headless \
|
||||
--env=WAYLAND_DISPLAY=godot-headless \
|
||||
--env=AUTO_PLAY=true \
|
||||
--env=AUTO_PLAY_SEED="$SEED" \
|
||||
--env=AUTO_PLAY_TURN_LIMIT="$TURN_LIMIT" \
|
||||
--env=AUTO_PLAY_DIR="$DIR" \
|
||||
org.godotengine.Godot --path . --rendering-method gl_compatibility 2>&1
|
||||
echo "EXIT_CODE=$?"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ GAME_DIR="$PROJECT_DIR/src/game"
|
|||
|
||||
COUNT="${1:-3}"
|
||||
TURN_LIMIT="${2:-500}"
|
||||
RESULTS_DIR="${3:-/tmp/autoplay_batch}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
RESULTS_DIR="${3:-$REPO_ROOT/.local/batches/autoplay_batch}"
|
||||
|
||||
if ! [[ "$COUNT" =~ ^[0-9]+$ ]] || [ "$COUNT" -lt 1 ]; then
|
||||
echo "ERROR: count must be a positive integer (got '$COUNT')" >&2
|
||||
|
|
@ -39,11 +41,12 @@ fi
|
|||
AUTOPLAY_HOST="${AUTOPLAY_HOST:-}"
|
||||
SAFETY_TIMEOUT=$(( TURN_LIMIT * 2 + 300 ))
|
||||
|
||||
# IMPORTANT: Flatpak sandboxes /tmp — result files written from inside the
|
||||
# sandbox to /tmp silently disappear. Use $HOME/tmp for local flatpak runs.
|
||||
if [ -z "$AUTOPLAY_HOST" ] && [[ "$RESULTS_DIR" == /tmp/* ]]; then
|
||||
RESULTS_DIR="$HOME/tmp/autoplay_batch"
|
||||
echo "INFO: Local flatpak run — redirecting results dir to $RESULTS_DIR (flatpak cannot write to /tmp)" >&2
|
||||
# Flatpak sandbox can't write to /tmp. Reject /tmp paths outright instead of
|
||||
# silently redirecting — persistent output belongs under the repo.
|
||||
if [[ "$RESULTS_DIR" == /tmp/* ]] || [[ "$RESULTS_DIR" == /private/tmp/* ]]; then
|
||||
echo "ERROR: results_dir under /tmp is forbidden (wiped on reboot, flatpak sandbox hostile)." >&2
|
||||
echo " Use a path under the repo (default: <repo>/.local/batches/) or \$HOME/tmp." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$RESULTS_DIR"
|
||||
|
|
@ -119,20 +122,21 @@ _run_remote() {
|
|||
if [ -z "${REMOTE_HOME:-}" ]; then
|
||||
REMOTE_HOME="$(ssh "$AUTOPLAY_HOST" 'echo "$HOME"')"
|
||||
fi
|
||||
local remote_game_dir="$REMOTE_HOME/tmp/autoplay_batch/game_${STAMP}_seed${seed}"
|
||||
local remote_game_dir="$REMOTE_HOME/Code/@projects/@magic-civilization/.local/batches/autoplay_batch/game_${STAMP}_seed${seed}"
|
||||
local remote_runner="$REMOTE_HOME/bin/run_ap3.sh"
|
||||
|
||||
ssh "$AUTOPLAY_HOST" "
|
||||
set -euo pipefail
|
||||
mkdir -p '$remote_game_dir'
|
||||
if [ ! -f /tmp/run_ap3.sh ]; then
|
||||
echo 'ERROR: /tmp/run_ap3.sh not found on $AUTOPLAY_HOST' >&2
|
||||
if [ ! -f '$remote_runner' ]; then
|
||||
echo 'ERROR: $remote_runner not found on $AUTOPLAY_HOST (expected persistent runner in \$HOME/bin)' >&2
|
||||
exit 1
|
||||
fi
|
||||
AUTO_PLAY=true \
|
||||
AUTO_PLAY_SEED='$seed' \
|
||||
AUTO_PLAY_TURN_LIMIT='$TURN_LIMIT' \
|
||||
AUTO_PLAY_DIR='$remote_game_dir' \
|
||||
bash /tmp/run_ap3.sh >'$remote_game_dir/game.log' 2>&1
|
||||
bash '$remote_runner' >'$remote_game_dir/game.log' 2>&1
|
||||
" || {
|
||||
echo "[seed $seed] SSH run exited with error — see $game_dir/game.log after scp" >&2
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue