Files
sysadmin-chronicles/start-game.sh
T
44r0n7 0265afa054 chore: bootstrap lean sysadmin-chronicles repo
Import the runnable game code, content, docs, scripts, and repo guidance while leaving local agent state, dependency installs, build output, and backup copies out of the published tree.
2026-05-02 11:49:07 -04:00

259 lines
7.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Sysadmin Chronicles — Launcher
# Usage: bash start-game.sh [--stop] [--manage-saves] [--reset-save]
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$SCRIPT_DIR"
SERVER_DIR="$PROJECT_ROOT/server"
FRONTEND_DIR="$PROJECT_ROOT/frontend"
DOMAIN="${SC_WORKSTATION_DOMAIN:-sc-workstation}"
NETWORK="${SC_NETWORK:-sc-internal}"
PORT="${PORT:-3000}"
export PORT
export LIBVIRT_DEFAULT_URI="${LIBVIRT_DEFAULT_URI:-qemu:///system}"
source "$PROJECT_ROOT/tools/lib/ui.sh"
source "$PROJECT_ROOT/tools/lib/config.sh"
source "$PROJECT_ROOT/tools/lib/save.sh"
source "$PROJECT_ROOT/tools/lib/internal-https.sh"
config_read || true
sc_ensure_internal_certs "$PROJECT_ROOT"
sc_export_internal_https_env
SERVER_PID=""
VIEWER_PID=""
_cleanup() {
[ -n "$SERVER_PID" ] && kill "$SERVER_PID" 2>/dev/null || true
[ -n "$VIEWER_PID" ] && kill "$VIEWER_PID" 2>/dev/null || true
}
trap '_cleanup' EXIT INT TERM
# ---------------------------------------------------------------------------
# Flag handling
# ---------------------------------------------------------------------------
for arg in "$@"; do
case "$arg" in
--stop)
stopped=false
for pid in $(sc_listen_pids "$PORT" || true); do
if sc_pid_is_repo_server "$pid" "$PROJECT_ROOT"; then
sc_stop_pid "$pid"
stopped=true
fi
done
if [ "$stopped" = true ]; then
echo " ✓ Game server stopped."
else
echo " (no running game server found)"
fi
exit 0
;;
--manage-saves)
exec bash "$PROJECT_ROOT/tools/save/manage-saves.sh"
;;
--reset-save)
echo ""
echo " This will reset your current save to a new game."
printf " Type RESET to confirm: " >/dev/tty
read -r _c </dev/tty
echo ""
if [ "$_c" = "RESET" ]; then
save_reset
else
echo " Cancelled."
fi
exit 0
;;
esac
done
# ---------------------------------------------------------------------------
# Pre-flight checks
# ---------------------------------------------------------------------------
if ! command -v virsh >/dev/null 2>&1; then
echo ""
echo " Your system is missing the virtual machine tools."
echo " Run install.sh to set up the game."
echo ""
exit 1
fi
if ! command -v node >/dev/null 2>&1; then
echo ""
echo " Node.js is required but wasn't found."
echo " Run install.sh to set up the game."
echo ""
exit 1
fi
# Check images directory is accessible
SC_IMAGES_DIR="${SC_IMAGES_DIR:-}"
if [ -n "$SC_IMAGES_DIR" ] && [ ! -d "$SC_IMAGES_DIR" ]; then
echo ""
echo " Can't find your game world."
echo " The VM images are stored at $SC_IMAGES_DIR"
echo " but that location isn't available right now."
echo ""
echo " Is your game drive plugged in and mounted?"
echo " Once it's mounted, run start-game.sh again."
echo ""
exit 1
fi
# Check workstation domain exists
if ! virsh dominfo "$DOMAIN" >/dev/null 2>&1; then
echo ""
echo " Your game world hasn't been built yet."
echo " Run install.sh to finish setup."
echo ""
exit 1
fi
# Check frontend has been built
if [ ! -f "$FRONTEND_DIR/dist/index.html" ]; then
echo ""
echo " The game interface hasn't been built yet."
echo " Run install.sh to finish setup."
echo ""
exit 1
fi
# ---------------------------------------------------------------------------
# Ensure server dependencies are installed
# ---------------------------------------------------------------------------
if [ ! -d "$SERVER_DIR/node_modules" ]; then
sc_info "Installing server dependencies..."
(cd "$SERVER_DIR" && npm install --silent)
fi
# ---------------------------------------------------------------------------
# Start server if not already running
# ---------------------------------------------------------------------------
_server_ready() {
ss -tlnp 2>/dev/null | grep -q ":${PORT} "
}
_ensure_server_port() {
local pids=""
pids="$(sc_listen_pids "$PORT" || true)"
if [ -z "$pids" ]; then
return 0
fi
local pid
for pid in $pids; do
if ! sc_pid_is_repo_server "$pid" "$PROJECT_ROOT"; then
echo ""
echo " Port $PORT is already in use by another process (pid $pid)."
echo " Stop that process or set PORT to a free port before launching."
echo ""
exit 1
fi
if sc_pid_has_internal_tls "$pid"; then
return 1
fi
done
sc_info "Restarting game server with HTTPS enabled..."
for pid in $pids; do
sc_stop_pid "$pid"
done
return 0
}
_wait_for_server() {
local timeout=15
local i=0
while ! _server_ready; do
sleep 0.3
(( i++ )) || true
if [ "$i" -ge $(( timeout * 10 / 3 )) ]; then
return 1
fi
done
}
if _ensure_server_port; then
(
cd "$SERVER_DIR"
exec node src/index.js
) &
SERVER_PID=$!
fi
# ---------------------------------------------------------------------------
# Ensure network is active
# ---------------------------------------------------------------------------
if virsh net-list --all 2>/dev/null | grep -q "\\b${NETWORK}\\b"; then
if ! virsh net-info "$NETWORK" 2>/dev/null | grep -q "Active:.*yes"; then
sc_info "Starting game network..."
virsh net-start "$NETWORK" >/dev/null 2>&1 || true
fi
fi
# ---------------------------------------------------------------------------
# Start workstation VM if not running
# ---------------------------------------------------------------------------
if [ "$(virsh domstate "$DOMAIN" 2>/dev/null | tr -d ' \n')" != "running" ]; then
virsh start "$DOMAIN" >/dev/null 2>&1 || true
fi
# ---------------------------------------------------------------------------
# Wait for server, show startup status
# ---------------------------------------------------------------------------
if [ -n "$SERVER_PID" ] && ! _wait_for_server; then
echo ""
echo " The game server didn't start in time."
echo " Check that port $PORT is available and try again."
echo ""
exit 1
fi
echo ""
sc_ok "Game server running"
sc_ok "Workstation online"
echo ""
echo " Opening your desk..."
echo ""
# ---------------------------------------------------------------------------
# Open SPICE viewer (do not exec — we need to wait and then clean up)
# ---------------------------------------------------------------------------
_launch_viewer() {
local spice_uri
spice_uri="$(virsh domdisplay "$DOMAIN" 2>/dev/null | grep -i spice | head -n1 || true)"
if [ -n "$spice_uri" ] && command -v remote-viewer >/dev/null 2>&1; then
remote-viewer "$spice_uri" &
VIEWER_PID=$!
elif command -v virt-viewer >/dev/null 2>&1; then
virt-viewer --connect "$LIBVIRT_DEFAULT_URI" "$DOMAIN" &
VIEWER_PID=$!
elif [ -n "$spice_uri" ] && command -v spicy >/dev/null 2>&1; then
local port
port="$(printf '%s' "$spice_uri" | grep -oE ':[0-9]+' | head -1 | tr -d ':')"
spicy -h 127.0.0.1 -p "${port:-5900}" &
VIEWER_PID=$!
else
echo " No SPICE viewer found (virt-viewer or remote-viewer)."
echo " Run install.sh to set up the game."
exit 1
fi
}
_launch_viewer
wait "$VIEWER_PID" 2>/dev/null || true