Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9ed7841
new data
YetheSamartaka Aug 23, 2026
a97a4e9
Server fps support
YetheSamartaka Aug 24, 2026
8aab1d0
improved gear visuals
YetheSamartaka Aug 24, 2026
b9b2bcb
add diffing tool for profiles
YetheSamartaka Aug 24, 2026
2ab900a
add arsenal export buttons
YetheSamartaka Aug 31, 2026
6b244cf
inventory tracking optimizations
YetheSamartaka Sep 3, 2026
db05dcb
new data protobuff support
YetheSamartaka Sep 3, 2026
b8fa6e5
Improved zeus tracking
YetheSamartaka Sep 3, 2026
8643607
Friendly fire and kill fixes due to wrong side attribution
YetheSamartaka Sep 3, 2026
b237d7f
virtual slot visibility fixes
YetheSamartaka Sep 3, 2026
637515b
Fixes for virtual zeus kill counts
YetheSamartaka Sep 3, 2026
6fdd984
Fix for virtual "faction" not being visible when 4 factions on missio…
YetheSamartaka Sep 5, 2026
87e8801
Add virtual side to recording dashboard
YetheSamartaka Sep 5, 2026
3b54a8f
Zeus ping tracking
YetheSamartaka Sep 5, 2026
cd61496
Added tracking of trenches
YetheSamartaka Sep 5, 2026
5ff3dbd
Player stance tracking
YetheSamartaka Sep 5, 2026
b614e47
new tests
YetheSamartaka Sep 5, 2026
2745858
zeus ping fixes and zoom level fixes
YetheSamartaka Sep 5, 2026
7e8a23d
add comms, ORBAT, bookmarks, deep links and support-event overlays
YetheSamartaka Sep 7, 2026
de77fd3
Compression improvements
YetheSamartaka Sep 7, 2026
a6efbcb
orbat improvements
YetheSamartaka Sep 8, 2026
06115ad
Add tracking of Unit traits
YetheSamartaka Sep 14, 2026
2c82c8e
Added weather data
YetheSamartaka Sep 14, 2026
69cfa2d
zeus module tracking
YetheSamartaka Sep 15, 2026
1fe0abb
Added faction filters for events and fixed scale bars
YetheSamartaka Sep 16, 2026
6b0e9a3
zeus improvements + fps
YetheSamartaka Oct 14, 2026
3b33227
Fix Go CI frontend build and positional explosion parsing
YetheSamartaka Oct 17, 2026
e74c27e
Merge branch 'main' into new-data
YetheSamartaka Oct 17, 2026
6216114
Fix web review regressions and stream whole-mission analysis
YetheSamartaka Oct 17, 2026
6743459
Fix chunked combat factions and asynchronous terrain loading
YetheSamartaka Oct 17, 2026
72be422
Keep combat sampling lazy and load radio sidecars for map transmissions
YetheSamartaka Oct 17, 2026
e952a8a
Normalize converted FPS samples and retain transmission factions
YetheSamartaka Oct 17, 2026
289e39d
Refresh system marker labels when zoom changes
YetheSamartaka Oct 17, 2026
9e53433
Apply TFAR range multiplier without terrain data
YetheSamartaka Oct 17, 2026
701fa31
Restore shared playback positions outside saved focus
YetheSamartaka Oct 17, 2026
bf241c9
Prefer current ORBAT slots and georeference heightmap fallback
YetheSamartaka Oct 17, 2026
183a54f
Restore manifest roles in protobuf ORBAT
YetheSamartaka Oct 17, 2026
666d145
Merge branch 'main' into new-data
YetheSamartaka Oct 17, 2026
8d3c301
Fix TFAR coverage bounds and Zeus placeholder replacement
YetheSamartaka Oct 17, 2026
af4c2b5
Center paused follow targets after asynchronous seeks
YetheSamartaka Oct 17, 2026
80fe9ca
Preserve transmission timeout across repeated starts
YetheSamartaka Oct 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ jobs:
key: ${{ runner.os }}-gomod-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-gomod-

- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
cache-dependency-path: ui/package-lock.json

- name: Install frontend dependencies
run: npm ci
working-directory: ui

- name: Build embedded frontend
run: npm run build
working-directory: ui

- name: Install maptool pipeline tools
run: |
sudo apt-get update -qq
Expand Down
164 changes: 161 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,15 +309,173 @@ Convert recordings manually using the CLI:
./ocap-webserver convert --set-format protobuf --id 1
```

Add compressed sidecars to recordings converted by an older build (see [Compression](#compression)):

```bash
# Show what is missing, without writing anything
./ocap-webserver precompress --dry-run

# Write the missing sidecars
./ocap-webserver precompress
```

### File Structure After Conversion

```
data/
├── mission_name.gz # Original JSON (preserved)
└── mission_name/ # Chunked binary format
├── manifest.pb # Metadata, entities, events
└── chunks/
├── 0000.pb # Frames 0-299
├── 0001.pb # Frames 300-599
├── manifest.pb.zst # Precompressed copies, served when the
├── manifest.pb.gz # client accepts the encoding
├── chunks/
│ ├── 0000.pb # Frames 0-299
│ ├── 0000.pb.zst
│ ├── 0000.pb.gz
│ ├── 0001.pb # Frames 300-599
│ └── ...
└── snapshots/ # Per-player detail, fetched on demand
├── 7.pb # Every snapshot recorded for unit 7
├── 7.pb.zst
└── ...
```

The `.zst` and `.gz` files are optional: the server falls back to the raw `.pb` whenever one is
missing. See [Compression](#compression).

### Compression

A converted recording never changes, so its artifacts are compressed once during conversion rather
than on every request. Each `.pb` gets `.zst` and `.gz` copies written beside it:

| File | Size (3h45m mission, ~950 entities) |
|------|------|
| `manifest.pb` | 2.5 MB |
| `manifest.pb.zst` | 378 KB |
| `chunks/0000.pb` | 2.4 MB |
| `chunks/0000.pb.zst` | 33 KB |
| `chunks/0000.pb.gz` | 55 KB |

The server picks the best sidecar the client accepts — `zstd` first, then `gzip` — and serves it
with the matching `Content-Encoding`. A client that accepts neither, and any recording that has no
sidecars, gets the raw `.pb`. Nothing depends on the sidecars existing.

Compressing offline makes a much higher compression level affordable than a proxy can use on a live
response (roughly 30% smaller chunks), and the server spends no CPU re-compressing bytes that never
change.

Sidecars are written only for artifacts over 1 KB, and only when the result is actually smaller than
the original. Writes go through a temporary file and a rename, so a request either sees a complete
sidecar or none at all.

#### Reverse proxies

If a proxy sits in front of the server, make sure it compresses **`application/x-protobuf`** — the
type `.pb` files are served as. Most proxies only compress a default list of text types and skip
protobuf, which leaves the largest responses on the page uncompressed for any recording without
sidecars.

**Caddy** — the type has to be added to `encode`, because naming `match` replaces the default list:

```caddy
encode {
zstd
gzip
match {
header Content-Type text/*
header Content-Type application/json*
header Content-Type application/javascript*
header Content-Type image/svg+xml*
header Content-Type application/x-protobuf*
}
}
```

**nginx** — `gzip` is `off` by default, and when enabled `gzip_types` still covers only
`text/html`, so protobuf has to be listed explicitly:

```nginx
server {
server_name ocap.example.com;

gzip on;
gzip_vary on; # adds Vary: Accept-Encoding
gzip_comp_level 5;
gzip_min_length 1024;
gzip_proxied any; # only needed if nginx itself sits behind another proxy or CDN
# (nginx keys this off the request's Via header)
gzip_types
text/plain
text/css
application/json
application/javascript
image/svg+xml
application/x-protobuf;

location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```

Do **not** add `proxy_set_header Accept-Encoding "";`. It is a common recipe for making nginx do the
compressing itself, but it hides the client's supported encodings from the server, so the
precompressed sidecars are never used and every response is compressed from scratch instead.

Stock nginx has no zstd support (it needs a third-party module), but that only limits what nginx can
compress *itself*. Sidecars are passed straight through, so a client that accepts zstd still gets
the `.zst` copy — which is a good reason to run `precompress` on an nginx deployment.

Responses served from a sidecar already carry `Content-Encoding`, and both proxies skip those, so
there is no double compression.

Static frontend assets under `/assets/` are content-hashed and are sent with
`Cache-Control: public, max-age=31536000, immutable` plus an `ETag`. Do not override that with
`no-cache` in a proxy — the bundle is around 900 KB raw (~300 KB compressed) and would otherwise be
re-downloaded on every page load.

#### Backfilling existing recordings

Precompression happens during conversion, so recordings converted by an older build have no
sidecars. They still play — the server falls back to the raw file — but their transfers stay larger.
`precompress` adds the sidecars without re-converting anything:

```bash
# Show what is missing, without writing
./ocap-webserver precompress --dry-run

# Write the missing sidecars
./ocap-webserver precompress

# Point at a specific data directory
./ocap-webserver precompress --data /srv/ocap/data

# Rebuild every sidecar, including up-to-date ones
./ocap-webserver precompress --force
```

Notes:

- It reads each `.pb` and writes sidecars beside it. **The artifacts themselves are never rewritten.**
- `.json.gz` recordings are skipped. Their compression is inside the file already and the server
serves them with `Content-Encoding: gzip`, so there is nothing to add.
- It is idempotent — a second run reports everything as current. A sidecar older than its artifact
is treated as stale and rebuilt.
- Safe to run against a live server, and no restart is needed afterwards: the server looks for
sidecars per request.
- It compresses every artifact at a high level, so it is CPU- and I/O-heavy on a large library.
Use `--dry-run` first to see the scale, and prefer off-peak.

Run it once after upgrading. Recordings converted from then on get their sidecars automatically.

### Player detail snapshots

Per-player detail (inventory, medical, stamina, radio) is kept in `snapshots/<unitId>.pb` instead
of the manifest, and fetched only when that player's profile is opened.

JSON recordings are unaffected. Chunked recordings converted before this keep their snapshots in
the manifest and still play; re-run `convert --input` to move them out.
8 changes: 8 additions & 0 deletions assets/markers/ocap/fort_barrier.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions assets/markers/ocap/fort_bunker.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions assets/markers/ocap/fort_sandbag.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions assets/markers/ocap/fort_static.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions assets/markers/ocap/fort_tent.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions assets/markers/ocap/fort_wire.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions assets/markers/ocap/fortification.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions assets/markers/ocap/trench.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions assets/markers/ocap/trench_big.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions assets/markers/ocap/trench_giant.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions assets/markers/ocap/trench_long.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions assets/markers/ocap/trench_short.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions assets/markers/ocap/trench_small.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions assets/markers/ocap/trench_vehicle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading