docs: update PLAN, CODEBASE, README with env vars, stubs, tests, atomic state

PLAN.md:
- Add SERVER_BINARY, SERVER_PARAMS, STEAMCMD_PATH to env vars table
- Add testdata/ to project structure
- Add testing section (backend tests, stubs, frontend tests, make targets)
- Update process start flow with env var overrides

CODEBASE.md:
- Add testdata/ stubs to directory map
- Replace zustand with vitest/testing-library/jsdom in frontend deps
- Update CI to include test steps
- Update Makefile targets description
- Add atomic state machine to ProcessManager description
- Add env overrides and atomic state to design decisions
- Update data flow with env var overrides

README.md:
- Add SERVER_BINARY, SERVER_PARAMS, STEAMCMD_PATH to env vars table
- Remove FRONTEND_DIR (embedded frontend)
- Update Development section with make targets
This commit is contained in:
MrFastwind
2026-07-23 22:29:22 +02:00
parent 930b6cbca6
commit 41c1390972
3 changed files with 93 additions and 16 deletions
+16 -7
View File
@@ -40,10 +40,13 @@ arma3-web-server/
│ │ ├── modlist_manager.go # Modlist CRUD against data/modlists/*.json
│ │ ├── modlist_parser.go # HTML Arma Launcher preset parser + renderer
│ │ ├── mod_manager.go # Workshop + local mod discovery + usage map
│ │ ├── server_process.go # Process lifecycle, arg builder, mod path resolver
│ │ ├── server_process.go # Process lifecycle, arg builder, mod path resolver (atomic state machine)
│ │ ├── steamcmd.go # SteamCMD manager (UpdateGame, DownloadMod, DownloadMods)
│ │ ├── scheduler.go # Cron-based scheduled updates
│ │ ── log_streamer.go # Pub/sub fan-out for stdout/stderr via channels
│ │ ── log_streamer.go # Pub/sub fan-out for stdout/stderr via channels
│ │ └── testdata/ # Stub binaries for testing
│ │ ├── arma3server_x64 # Server stub: logs args, heartbeat, -t N exit, RPT writes
│ │ └── steamcmd # SteamCMD stub: fake downloads, creates mod dirs
│ ├── go.mod / go.sum
│ └── serverfiles/ # Default SERVERFILE_DIR (created at startup)
├── frontend/ # React SPA
@@ -107,11 +110,14 @@ arma3-web-server/
| `tailwindcss` | styling | ^4.3.2 | Utility-first CSS |
| `@monaco-editor/react` | editor | ^4.7.0 | Monaco code editor for .cfg files |
| `@xterm/xterm` / `@xterm/addon-fit` | terminal | ^6.0.0 / ^0.11.0 | In-browser xterm.js terminal |
| `zustand` | state | ^5.0.14 | Declared but **unused** (no stores exist) |
| `vite` | bundler | ^8.1.1 | Dev server + production build |
| `@vitejs/plugin-react` | tooling | ^6.0.3 | React fast-refresh / JSX transform |
| `typescript` | language | ~6.0.2 | Type checking |
| `oxlint` | linter | ^1.71.0 | Linting |
| `vitest` | test runner | ^4.1.10 | Frontend unit/integration tests |
| `@testing-library/react` | test utility | ^16.3.0 | React component testing |
| `@testing-library/jest-dom` | test matcher | ^6.6.3 | DOM assertion matchers |
| `jsdom` | test env | ^26.1.0 | Browser environment for tests |
## Architecture
@@ -133,7 +139,7 @@ Browser -- REST/WS --> api.Handler --> SettingsManager --> data/settings.j
- **Handler** (api package) -- stateless; receives service pointers via constructor injection. All handlers are methods on Handler.
- ***Manager** (services package) -- stateful structs for each concern. Hold their own `sync.Mutex` for thread safety.
- **LogStreamer** -- pub/sub per named stream (`"server"`, `"steamcmd"`). Subscribe returns a `chan string`; Stream reads from an `io.Reader` (process stdout/stderr) and broadcasts to all subscribers of that key.
- **ProcessManager** -- manages the Arma 3 server child process lifecycle. Builds the command line from settings, resolves mod paths, monitors process exit.
- **ProcessManager** -- manages the Arma 3 server child process lifecycle. Uses `atomic.Int32` state machine (idle/starting/running/stopping) with `CompareAndSwap` for lock-free state transitions. Builds the command line from settings, resolves mod paths, monitors process exit.
### Notable design decisions
1. **No database** -- everything is files: `settings.json`, `*.cfg` in `$CFG_DIR`, modlist JSON files. Backup means copying directories.
@@ -141,7 +147,8 @@ Browser -- REST/WS --> api.Handler --> SettingsManager --> data/settings.j
3. **`%command%` substitution** -- if ServerParameters contains `%command%`, the entire textarea is treated as a shell template, with `%command%` replaced by the binary path and auto-args (`-config=`, `-mod=`, `-profiles=`, `-port=`) appended. Enables Wine wrappers.
4. **Two-tier mod resolution** -- resolveModPath checks `$MODS_DIR/@name` first (manual/symlinked), then falls back to `$SERVERFILE_DIR/steamapps/workshop/content/107410/<id>` for workshop downloads.
5. **SteamCMD async with live streaming** -- all steamcmd operations run in background goroutines, output broadcast to `"steamcmd"` key.
6. **Env-defined paths** -- all dirs set via env vars with hardcoded defaults, resolved to absolute paths at startup.
6. **Env-defined paths** -- all dirs set via env vars with hardcoded defaults, resolved to absolute paths at startup. `SERVER_BINARY`, `SERVER_PARAMS`, `STEAMCMD_PATH` override defaults at runtime.
7. **Atomic state machine** -- ProcessManager and SteamCmdManager use `atomic.Int32`/`atomic.Bool` with `CompareAndSwap` for lock-free state transitions, eliminating race conditions between concurrent Start/Stop calls.
## Data flow (example: Start server)
@@ -152,6 +159,8 @@ User clicks "Start" (Settings.tsx)
--> Handler.StartServer() (settings.go)
--> process.Start() (server_process.go)
--> Load settings.json
--> SERVER_PARAMS env overrides ServerParameters if set
--> SERVER_BINARY env overrides platform default binary name if set
--> If %command% in ServerParameters:
--> Replace %command% with binPath
--> Split into command + args
@@ -174,7 +183,7 @@ User clicks "Start" (Settings.tsx)
### Gitea Actions (`/.gitea/workflows/`)
- **`ci.yml`** — Triggered on push to any branch or PR. Runs `go build ./...` and `npm ci && npm run build`. Build-only; no tests.
- **`ci.yml`** — Triggered on push to any branch or PR. Runs `go build ./...`, `npm ci && npm run build`, `go test ./...`, and `npm test`. Build + test CI.
- **`release.yml`** — Triggered on `v*` tag push. Runs GoReleaser v2 with `release --clean`, which builds Go binaries (linux/amd64 + windows/amd64), builds frontend via `before.hooks`, creates archives, builds + pushes a Docker image using `Dockerfile.goreleaser`, and publishes a Gitea release.
### Docker
@@ -184,4 +193,4 @@ User clicks "Start" (Settings.tsx)
### Local dev
- **Makefile targets** — `make run` starts backend, `make dev` starts both backend + Vite dev server with hot reload.
- **Makefile targets** — `make run` starts backend, `make dev` starts both backend + Vite dev server with hot reload (copies test stubs to dev-deploy, passes `SERVER_BINARY`/`SERVER_PARAMS`/`STEAMCMD_PATH` env vars). `make test` runs both backend and frontend tests.