l4d.tools/README.md
2026-01-18 17:42:32 +01:00

252 lines
6.8 KiB
Markdown

# 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]