diff --git a/scripts/apricot/run_ap3.sh b/scripts/apricot/run_ap3.sh index 7e8b1cb0..995cb243 100755 --- a/scripts/apricot/run_ap3.sh +++ b/scripts/apricot/run_ap3.sh @@ -1,17 +1,26 @@ #!/bin/bash -# Godot headless autoplay runner. Uses Godot's --headless flag (no weston, -# no Wayland, no X). Screenshots will be blank but turn_stats.jsonl / -# events.jsonl / saves/ are produced. +# 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 pkill -f "flatpak run.*Godot" 2>/dev/null || true pkill -f godot 2>/dev/null || true sleep 1 + : "${AUTO_PLAY:=true}" : "${AUTO_PLAY_DIR:=$HOME/tmp/ap_default}" : "${AUTO_PLAY_TURN_LIMIT:=500}" +: "${RENDER_MODE:=headless}" + mkdir -p "$AUTO_PLAY_DIR" cd "$HOME/Code/@projects/@magic-civilization/src/game" SAFETY=$((AUTO_PLAY_TURN_LIMIT * 2 + 300)) + FLATPAK_ENVS=( "--env=AUTO_PLAY=true" "--env=AUTO_PLAY_DIR=$AUTO_PLAY_DIR" @@ -20,9 +29,40 @@ 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 \ --filesystem=home \ "${FLATPAK_ENVS[@]}" \ - org.godotengine.Godot --path . --headless 2>&1 + org.godotengine.Godot "${GODOT_ARGS[@]}" 2>&1 EXIT=$? echo "EXIT_CODE=$EXIT" diff --git a/scripts/apricot/run_seeded.sh b/scripts/apricot/run_seeded.sh index a503cba4..8ec6f93a 100755 --- a/scripts/apricot/run_seeded.sh +++ b/scripts/apricot/run_seeded.sh @@ -1,25 +1,21 @@ #!/bin/bash -# Headless seeded autoplay runner. No weston/Wayland — uses Godot --headless. +# 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}" -pkill -f "flatpak run.*Godot" 2>/dev/null || true -pkill -f godot 2>/dev/null || true -sleep 1 + DIR="$HOME/tmp/ap_seeded_${SEED}" rm -rf "$DIR" mkdir -p "$DIR" -cd "$HOME/Code/@projects/@magic-civilization/src/game" -SAFETY=$((TURN_LIMIT * 2 + 300)) -FLATPAK_ENVS=( - "--env=AUTO_PLAY=true" - "--env=AUTO_PLAY_SEED=$SEED" - "--env=AUTO_PLAY_TURN_LIMIT=$TURN_LIMIT" - "--env=AUTO_PLAY_DIR=$DIR" -) -timeout "$SAFETY" flatpak run --user \ - --filesystem=home \ - "${FLATPAK_ENVS[@]}" \ - org.godotengine.Godot --path . --headless 2>&1 -EXIT=$? -echo "EXIT_CODE=$EXIT" + +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" diff --git a/tools/autoplay-batch.sh b/tools/autoplay-batch.sh index f5a72610..04d60a28 100755 --- a/tools/autoplay-batch.sh +++ b/tools/autoplay-batch.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # autoplay-batch.sh — Run auto_play N times with different seeds and collect per-game output dirs. # -# Usage: tools/autoplay-batch.sh [count=3] [turn_limit=500] [results_dir=/tmp/autoplay_batch] +# Usage: tools/autoplay-batch.sh [--weston] [count=3] [turn_limit=500] [results_dir] # # Output layout: # /game__seed/ @@ -9,24 +9,39 @@ # turn_stats.jsonl # events.jsonl # game.log -# weston.log (local only) +# weston.log (weston mode only) # *.save (per-turn saves, if configured) # # Environment: -# AUTOPLAY_HOST — If set (e.g. "lilith@apricot.local"), run each game via SSH using -# /tmp/run_ap3.sh on the remote host and scp results back. +# AUTOPLAY_HOST — If set (e.g. "lilith@apricot.local"), run each game via SSH +# using run_ap3.sh on the remote host and scp results back. # If unset, run locally via flatpak (Linux only). +# RENDER_MODE — "headless" (default) or "weston". --weston flag sets this. +# headless: Godot --headless, no display, no screenshots. +# weston: weston headless backend, software rendering, screenshots work. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" GAME_DIR="$PROJECT_DIR/src/game" +REPO_ROOT="$PROJECT_DIR" + +RENDER_MODE="${RENDER_MODE:-headless}" + +# Parse --weston flag before positional args +POSITIONAL=() +for arg in "$@"; do + case "$arg" in + --weston) RENDER_MODE="weston" ;; + --headless) RENDER_MODE="headless" ;; + *) POSITIONAL+=("$arg") ;; + esac +done +set -- "${POSITIONAL[@]+"${POSITIONAL[@]}"}" COUNT="${1:-3}" TURN_LIMIT="${2:-500}" -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 @@ -62,6 +77,7 @@ if [ -n "$AUTOPLAY_HOST" ]; then else echo "Mode: local flatpak" fi +echo "Render: $RENDER_MODE" echo "Safety timeout: ${SAFETY_TIMEOUT}s per game" echo "============================================================" @@ -82,34 +98,50 @@ _run_local() { _kill_stale_procs - echo "[seed $seed] Starting weston (headless)..." - WESTON_SOCKET="godot-headless-$$" - weston --backend=headless --socket="$WESTON_SOCKET" --width=1920 --height=1080 \ - >"$game_dir/weston.log" 2>&1 & - WESTON_PID=$! - sleep 1 + local WESTON_PID="" + local FLATPAK_ENVS=( + "--env=AUTO_PLAY=true" + "--env=AUTO_PLAY_SEED=$seed" + "--env=AUTO_PLAY_TURN_LIMIT=$TURN_LIMIT" + "--env=AUTO_PLAY_DIR=$game_dir" + ) + local GODOT_ARGS=("--path" "$GAME_DIR" "--rendering-method" "gl_compatibility") - echo "[seed $seed] Launching Godot (timeout ${SAFETY_TIMEOUT}s)..." - WAYLAND_DISPLAY="$WESTON_SOCKET" \ + if [ "$RENDER_MODE" = "weston" ]; then + if ! command -v weston >/dev/null 2>&1; then + echo "ERROR: --weston mode but weston not installed" >&2 + exit 1 + fi + WESTON_SOCKET="godot-headless-$$" + echo "[seed $seed] Starting weston (headless)..." + weston --backend=headless --socket="$WESTON_SOCKET" --width=1920 --height=1080 \ + >"$game_dir/weston.log" 2>&1 & + WESTON_PID=$! + sleep 1 + FLATPAK_ENVS+=( + "--socket=wayland" + "--env=WAYLAND_DISPLAY=$WESTON_SOCKET" + "--filesystem=xdg-run/${WESTON_SOCKET}" + ) + else + GODOT_ARGS+=("--headless") + fi + + echo "[seed $seed] Launching Godot ($RENDER_MODE, timeout ${SAFETY_TIMEOUT}s)..." XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" \ timeout "$SAFETY_TIMEOUT" flatpak run --user \ - --socket=wayland \ - --env=WAYLAND_DISPLAY="$WESTON_SOCKET" \ - --env=AUTO_PLAY=true \ - --env=AUTO_PLAY_SEED="$seed" \ - --env=AUTO_PLAY_TURN_LIMIT="$TURN_LIMIT" \ - --env=AUTO_PLAY_DIR="$game_dir" \ - org.godotengine.Godot \ - --path "$GAME_DIR" \ - --rendering-method gl_compatibility \ - --headless \ + --filesystem=home \ + "${FLATPAK_ENVS[@]}" \ + org.godotengine.Godot "${GODOT_ARGS[@]}" \ >"$game_dir/game.log" 2>&1 || { local exit_code=$? echo "[seed $seed] Godot exited with code $exit_code" >&2 } - kill "$WESTON_PID" 2>/dev/null || true - wait "$WESTON_PID" 2>/dev/null || true + if [ -n "$WESTON_PID" ]; then + kill "$WESTON_PID" 2>/dev/null || true + wait "$WESTON_PID" 2>/dev/null || true + fi } _run_remote() { @@ -136,6 +168,7 @@ _run_remote() { AUTO_PLAY_SEED='$seed' \ AUTO_PLAY_TURN_LIMIT='$TURN_LIMIT' \ AUTO_PLAY_DIR='$remote_game_dir' \ + RENDER_MODE='$RENDER_MODE' \ 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