Files
ArmA-3-web-server/PLAN.md
2026-07-06 14:14:58 +02:00

8.2 KiB

Arma 3 Web Server — Architecture Plan

Overview

A web-based control panel to configure, update, install, and execute a single Arma 3 dedicated server instance. Supports both Windows and Linux server binaries via SteamCMD.


Tech Stack

Layer Choice Rationale
Backend Go 1.24+ (gin, gorilla/websocket) Single binary, cross-compiles, goroutines for process streaming
Storage File-based (JSON on disk) No database dependency; settings, configs, and modlists are files
Frontend React 19 + TypeScript + Vite Fast iteration, rich ecosystem
UI Kit Tailwind CSS 4 Dark-theme UI, utility-first styling
Code Editor Monaco (VS Code) Syntax-highlighted config editing
Terminal xterm.js Live log display from server stdout

Project Structure

arma3-web-server/
├── backend/
│   ├── cmd/server/main.go           # Entry point
│   ├── internal/
│   │   ├── api/                     # HTTP handlers (Gin routes)
│   │   │   ├── router.go            # Route registration
│   │   │   ├── settings.go          # Server settings + start/stop/restart
│   │   │   ├── configs.go           # .cfg file CRUD
│   │   │   ├── modlists.go          # Modlist CRUD
│   │   │   └── logs.go              # WebSocket handler for live logs
│   │   ├── models/                  # Data structs
│   │   │   ├── settings.go          # ServerSettings (singleton)
│   │   │   └── modlist.go           # Modlist + ModEntry
│   │   └── services/                # Business logic
│   │       ├── settings.go          # Singleton settings load/save
│   │       ├── config_manager.go    # .cfg file I/O in $CFG_DIR
│   │       ├── modlist_manager.go   # Modlist CRUD (JSON files)
│   │       ├── server_process.go    # Process lifecycle (start/stop/restart)
│   │       └── log_streamer.go      # stdout/stderr → WebSocket fan-out
│   ├── go.mod
│   └── go.sum
├── frontend/
│   ├── src/
│   │   ├── pages/
│   │   │   ├── Dashboard.tsx
│   │   │   ├── Settings.tsx
│   │   │   ├── Configs.tsx
│   │   │   ├── ConfigEditor.tsx
│   │   │   ├── Modlists.tsx
│   │   │   ├── ModlistEditor.tsx
│   │   │   └── Logs.tsx
│   │   ├── components/
│   │   │   ├── ConfigEditor.tsx      # Monaco wrapper
│   │   │   ├── LiveTerminal.tsx      # xterm.js wrapper
│   │   │   └── Layout.tsx            # Sidebar nav shell
│   │   ├── api/client.ts            # Typed fetch wrapper
│   │   └── types/index.ts           # TypeScript types
│   ├── package.json
│   └── vite.config.ts
├── data/                             # Runtime data
│   ├── settings.json                 # Singleton server settings
│   └── modlists/                     # Modlist JSON files (*.json)
├── docker-compose.yml
├── Dockerfile
└── Makefile

Environment Variables

Env Purpose Default
SERVERFILE_DIR Base dir where arma3server_x64 binary + userconfig/ live "" (must be set)
MODS_DIR Base dir where @modname folders are stored "" (must be set)
CFG_DIR Base dir where .cfg config files are stored "" (must be set)
PROFILES_DIR Server profile/save path (ignored in UI) $SERVERFILE_DIR/Profiles
DATA_DIR Internal data (settings.json, modlists/) ./data
LISTEN HTTP listen address :8080
FRONTEND_DIR Path to built frontend files ../frontend/dist

Data Model

Server Settings (singleton) — data/settings.json

{
  "ip_port": "0.0.0.0:2302",
  "server_parameters": "-server -world=empty -loadMissionToMemory -noPause",
  "steam_branch": "stable",
  "steam_user": "anonymous",
  "platform": "linux",
  "cba_settings": "",
  "ai_level_presets": "",
  "difficulty_presets": "",
  "active_config": "server",
  "active_modlist": "uuid-of-modlist",
  "updated_at": "2026-01-01T00:00:00Z"
}

Configs — $CFG_DIR/*.cfg

Each config is a plain .cfg file stored in the env-defined CFG_DIR. CRUD operations create/rename/delete these files directly on disk.

Modlists — data/modlists/{uuid}.json

{
  "id": "uuid",
  "name": "My Modlist",
  "mods": [
    { "id": "450814997", "name": "CBA_A3", "enabled": true },
    { "id": "463939057", "name": "ACE", "enabled": false }
  ],
  "created_at": "...",
  "updated_at": "..."
}

Server Configuration

The three userconfig files are singletons — only one version can exist on disk at a time:

File Disk location Managed in
cba_settings.sqf $SERVERFILE_DIR/userconfig/cba_settings.sqf Settings page (textarea)
CfgAILevelPresets.sqf $SERVERFILE_DIR/userconfig/CfgAILevelPresets.sqf Settings page (textarea)
CfgDifficultyPresets.sqf $SERVERFILE_DIR/userconfig/CfgDifficultyPresets.sqf Settings page (textarea)

These are written to disk immediately when settings are saved.


API Routes

# Server Settings (singleton)
GET    /api/server/settings
PUT    /api/server/settings          # also writes userconfig files to disk
POST   /api/server/start
POST   /api/server/stop
POST   /api/server/restart
GET    /api/server/status
GET    /api/server/steamcmd          # returns current branch/user/platform
POST   /api/server/steamcmd/update-game
POST   /api/server/steamcmd/download-mod
GET    /api/server/steamcmd/status

# Configs (.cfg files in $CFG_DIR)
GET    /api/configs
POST   /api/configs
GET    /api/configs/:name
PUT    /api/configs/:name
DELETE /api/configs/:name
POST   /api/configs/:name/duplicate

# Modlists
GET    /api/modlists
POST   /api/modlists
GET    /api/modlists/:id
PUT    /api/modlists/:id
DELETE /api/modlists/:id
POST   /api/modlists/:id/duplicate
POST   /api/modlists/import          (multipart form: file=*.html — parses Arma Launcher HTML preset)
GET    /api/modlists/:id/check       (returns mod entries with downloaded: bool)
POST   /api/modlists/:id/download-missing
POST   /api/modlists/:id/update-all

# Logs
GET    /ws/server/logs          (WebSocket — live server stream)
GET    /ws/steamcmd/logs        (WebSocket — live steamcmd stream)
GET    /api/server/logs         (list log files)
GET    /api/server/logs/:file   (read log file)

Process Start Flow

User clicks Start
  → Settings loaded from data/settings.json
  → Args built from server_parameters + -config=<active_config>.cfg
  → -profiles=$PROFILES_DIR auto-appended (unless already in server_parameters)
  → -port= derived from ip_port field (unless -port= already in server_parameters)
  → Mod path built from active modlist (enabled mods → @modname paths)
  → arma3server_x64 spawned via exec.CommandContext
  → stdout/stderr piped to LogStreamer fan-out
  → WebSocket clients receive live output
  → On exit, process reference cleaned up

Key Design Decisions

  1. File-based storage — No database. Settings, configs, and modlists are files on disk. Portable, backup-able with simple file copy.
  2. Env-defined pathsSERVERFILE_DIR, MODS_DIR, CFG_DIR, PROFILES_DIR are environment variables, not stored in the UI.
  3. Configs are real .cfg files — Stored in $CFG_DIR, directly usable by the Arma 3 server's -config= parameter.
  4. Modlist = mod references + enabled state — The -mod= parameter is built at start time by resolving enabled mods. For each mod, $MODS_DIR/@name is tried first (manually placed or symlinked); if not found and a Steam Workshop id exists, $SERVERFILE_DIR/steamapps/workshop/content/107410/<id> is used as fallback.
  5. Single server instance — Only one server at a time. No multi-instance support.
  6. No auth for v1 — JWT auth can be added later without breaking the API design.
  7. Docker-first deployment — Single docker-compose.yml bundles SteamCMD, backend, and frontend serving.