252 lines
6.8 KiB
Markdown
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]
|