0265afa054
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.
259 lines
7.5 KiB
Bash
Executable File
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
|