#!/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/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