# L4D Tools A Rails 8.1 application for managing Left4Dead 2 game servers with a web interface. ## Features - **Steam Authentication**: Login via Steam OpenID - **Server Templates**: Define reusable server configurations with overlays, config options, and startup parameters - **Overlays Management**: Support for both system and custom overlays with file uploads - **Server Lifecycle**: Spawn, start, stop, restart, and delete L4D2 servers - **Live Logs**: Stream server logs in real-time via WebSocket (ActionCable) - **Health Checks**: Periodic monitoring of server status via systemd and RCON - **Activity Logging**: Track user actions (server creation, deletion, start/stop) - **User-Level Systemd**: Servers run as systemd user units (steam user) ## System Requirements - Ruby 4.0+ - Rails 8.1+ - SQLite 3.8.0+ - Linux (for L4D2 server and systemd support) - Left4Dead 2 base installation at `/opt/l4d2/installation/` - Overlays in `/opt/l4d2/overlays/` - `fuse-overlayfs` installed and available ## Setup Instructions ### 1. Install Dependencies ```bash bundle install ``` ### 2. Database Setup ```bash bin/rails db:migrate ``` ### 3. Enable Systemd User Services (One-Time Setup) Allow the `steam` user to run persistent systemd services: ```bash loginctl enable-linger steam ``` Without this, user-level systemd services will stop when the user logs out. ### 4. Create Systemd Helper Scripts Create `/opt/l4d2/bin/` directory and add helper scripts for server lifecycle: ```bash mkdir -p /opt/l4d2/bin ``` Create `/opt/l4d2/bin/start-server` (referenced by systemd units): ```bash #!/bin/bash SERVER_ID=$1 exec /opt/l4d2/servers/${SERVER_ID}/merged/srcds_run -norestart -pidfile /opt/l4d2/servers/${SERVER_ID}/pid -game left4dead2 -ip 0.0.0.0 -port $(grep "^Port:" /opt/l4d2/servers/${SERVER_ID}/server.cfg | cut -d' ' -f2) +hostname "Server" +map c1m1_hotel ``` Create `/opt/l4d2/bin/stop-server`: ```bash #!/bin/bash SERVER_ID=$1 # Systemd handles killing the process exit 0 ``` Make scripts executable: ```bash chmod +x /opt/l4d2/bin/start-server /opt/l4d2/bin/stop-server ``` ### 5. Configure Steam API Key (Optional for Development) For Steam authentication in production, set the Steam API key: ```bash export STEAM_API_KEY="your-steam-api-key" ``` In development, a test key is used by default. ### 6. Run the Application Development: ```bash bin/dev ``` This starts: - Rails server on `http://localhost:3000` - Solid Queue job processor (in Puma) - Asset pipeline watcher ## Architecture ### Domain Models - **User** — Steam-authenticated users - **Overlay** — Filesystem directories layered via overlayfs (system or custom) - **ServerTemplate** — Reusable server configuration (overlays + config + params) - **Server** — Active instance of a ServerTemplate - **ConfigOption** — Key-value server configuration (server.cfg) - **StartupParam** — Command-line arguments passed to srcds_run - **Activity** — Audit log of user actions ### Workflow 1. **Create ServerTemplate** - Select overlays (in priority order) - Define config options (key=value for server.cfg) - Define startup parameters 2. **Spawn Server from Template** - User specifies: server name, port, optional parameter overrides - Rails generates `/opt/l4d2/servers/{id}/server.cfg` - Mounts overlayfs stack using fuse-overlayfs - Creates systemd user unit at `~/.config/systemd/user/left4dead2-{id}.service` - Starts service via `systemctl --user start` 3. **Monitor Server** - StatusUpdateJob polls every 30 seconds - Checks systemd status + RCON health - Updates Server.status and last_health_check_at 4. **Manage Server** - Start/Stop/Restart via UI buttons - Stream live logs via ActionCable + journalctl - Delete server (stops service, removes config, unmounts overlayfs) ### Directory Structure ``` /opt/l4d2/ ├── installation/ # L4D2 base game files (read-only lower layer) ├── overlays/ # Overlay directories │ ├── system_overlay_1/ # System overlays (created by setup.sh) │ ├── custom_{id}/ # Custom overlays (user-created) ├── servers/ # Active server instances │ └── {server_id}/ │ ├── server.cfg # Generated config │ ├── upper/ # Overlayfs writable layer │ ├── work/ # Overlayfs work directory │ ├── merged/ # Overlayfs mount point (active game filesystem) │ └── pid # Process ID file ├── configs/ # (Legacy - configs now live in /opt/l4d2/servers/{id}/) └── bin/ ├── start-server # Systemd ExecStart script └── stop-server # Systemd ExecStop script ``` ### Key Libraries - **L4dServer::ConfigGenerator** — Renders server.cfg from ConfigOptions - **L4dServer::Launcher** — Mounts overlayfs, generates systemd unit - **L4dServer::SystemdManager** — Wraps systemctl commands - **L4dServer::HealthChecker** — Monitors server health via systemd + RCON ### Background Jobs - **SpawnServerJob** — Executed when user spawns server (mounts, starts) - **StatusUpdateJob** — Recurring job (every 30s) polling server status ### WebSocket - **LogChannel** — Streams `journalctl` output to connected clients in real-time ## Development Notes ### Running Tests ```bash bin/rails test ``` ### Console Access ```bash bin/rails console ``` ### Viewing Logs ```bash journalctl --user -u left4dead2-{server_id}.service -f ``` ### Database Migrations ```bash bin/rails db:migrate # Apply pending migrations bin/rails db:rollback # Revert last migration ``` ## Configuration ### Environment Variables - `STEAM_API_KEY` — Steam OpenID API key (optional, test key used in dev) - `RAILS_ENV` — Rails environment (development/production) - `RAILS_MASTER_KEY` — Encryption key for credentials (production only) ### Rails Credentials ```bash bin/rails credentials:edit ``` Store sensitive data here (encrypted in Git). ## Deployment (Kamal) Docker deployment via Kamal: ```bash kamal deploy ``` See `config/deploy.yml` for Kamal configuration. **Important**: Ensure `RAILS_MASTER_KEY` is set in production environment. ## Known Limitations / Future Work 1. **Single-Machine Only** — Servers must run on same machine as Rails app 2. **No Backup/Restore** — Server state is ephemeral; no state snapshots yet 3. **Basic Health Checks** — RCON health check is simple; doesn't parse game state 4. **Manual Setup Scripts** — `setup.sh` and wrapper scripts must be installed manually 5. **No API** — Web UI only; no REST/GraphQL API yet ## Contributing Follow Rails conventions and the style guide: ```bash bundle exec rubocop -a # Auto-fix style issues bundle exec brakeman # Security scanning bundle exec bundler-audit # Gem vulnerability check ``` ## License [Add your license here]