Skip to content

Extension that bridges the addon and web components.

License

Notifications You must be signed in to change notification settings

OCAP2/extension

Repository files navigation

OCAP Recorder (Go)

Coverage

About

A Go implementation of an ArmA 3 native extension that records gameplay to PostgreSQL, SQLite, or in-memory JSON. Captures unit positions, combat events, markers, and more for mission replay and analytics.

Architecture

Overview

ArmA 3 Game
    ↓ callExtension("ocap_recorder", [":COMMAND:", args])
RVExtensionArgs() [CGo boundary]
    ↓
Dispatcher.Dispatch(Event)
    ↓
    ├─ [Sync]     → Handler directly
    └─ [Buffered] → Channel → Goroutine → Handler
                                              ↓
                                   Parser (args → core types)
                                              ↓
                                   EntityCache (validate/enrich)
                                              ↓
                                   Storage Backend
                                     ├─ Memory → in-memory append → JSON export on save
                                     ├─ Postgres → Queue → DB writer (batch insert every 2s) → PostgreSQL
                                     └─ SQLite → Queue → DB writer (batch insert every 2s) → SQLite

Buffered handlers are gated on :STORAGE:INIT: — events queue in channels until the storage backend is ready.

DLL Entry Points

The extension exposes CGo-exported functions that ArmA 3 calls:

Function Purpose
RVExtensionArgs() Main entry point; receives command and arguments
RVExtension() Legacy simple command handler
RVExtensionVersion() Returns version string

Event Dispatcher

The dispatcher routes commands to handlers with optional buffering:

  • Sync handlers: Execute immediately (entity creation)
  • Buffered handlers: Queue events in channels for async processing (high-volume state updates)
  • Metrics: OpenTelemetry integration for queue sizes, events processed/dropped

Project Structure

cmd/ocap_recorder/main.go    Entry point, initialization, lifecycle commands
pkg/a3interface/             CGo exports (RVExtension*)
internal/
├── dispatcher/              Event routing with async buffering
├── parser/                  Command parsing (args → core types)
├── worker/                  Handler registration and DB writer loop
├── queue/                   Thread-safe queues for batch writes
├── cache/                   Entity lookup caching (ObjectID → model)
├── model/                   Database models + converters
├── storage/                 Storage backends (memory, postgres, sqlite)
└── geo/                     Coordinate/geometry utilities

Design Principles

  1. Low latency: Async buffered handlers don't block ArmA's game loop
  2. High throughput: Batch writes every 2 seconds instead of per-event
  3. Entity caching: Sync entity creation → cache → async state updates use cached FK
  4. Pluggable storage: Memory (JSON export), PostgreSQL, or SQLite (in-memory with periodic disk dump)
  5. Observability: OpenTelemetry metrics and structured logging (slog)

Building

Requires Docker with Linux containers.

Windows DLL

docker pull x1unix/go-mingw:1.24

docker run --rm -v ${PWD}:/go/work -w /go/work x1unix/go-mingw:1.24 \
  go build -buildvcs=false -o dist/ocap_recorder_x64.dll -buildmode=c-shared ./cmd/ocap_recorder

Linux .so

docker run --rm -v ${PWD}:/go/work -w /go/work golang:1.24-bullseye \
  go build -buildvcs=false -o dist/ocap_recorder_x64.so -buildmode=c-shared ./cmd/ocap_recorder

Uses Debian Bullseye (glibc 2.31) for broad compatibility with Linux game servers.

Configuration

Copy ocap_recorder.cfg.json.example to ocap_recorder.cfg.json alongside the DLL and edit as needed.

Supported Commands

All commands follow a :RESOURCE:ACTION: naming convention. New commands must use this pattern — resource noun first, then verb/qualifier (e.g., :SOLDIER:CREATE:, :EVENT:KILL:, :SYS:INIT:).

Entity Commands

Command Buffer Purpose
:SOLDIER:CREATE: Sync Register new unit
:SOLDIER:STATE: 10,000 Update unit position/state
:SOLDIER:DELETE: 500 Mark unit as removed (disconnect, respawn corpse)
:VEHICLE:CREATE: Sync Register new vehicle
:VEHICLE:STATE: 10,000 Update vehicle position/state
:VEHICLE:DELETE: 500 Mark vehicle as removed

Combat Commands

Command Buffer Purpose
:EVENT:PROJECTILE: 5,000 Projectile tracking (positions + hits)
:EVENT:KILL: 2,000 Kill event

General Commands

Command Buffer Purpose
:EVENT:GENERAL: 1,000 General gameplay event
:EVENT:CHAT: 1,000 Chat message
:EVENT:RADIO: 1,000 Radio transmission
:TELEMETRY:FRAME: 100 Server telemetry (FPS, entity counts, weather, player network stats)
:TIME:STATE: 100 Mission time/date tracking

Marker Commands

Command Buffer Purpose
:MARKER:CREATE: Sync Create map marker (needs immediate DB ID)
:MARKER:STATE: 1,000 Update marker position/appearance (pos, dir, alpha, text, color, size, type, brush, shape)
:MARKER:DELETE: 500 Delete marker

Placed Object Commands

Command Buffer Purpose
:PLACED:CREATE: Sync Register placed object (mine, explosive)
:PLACED:EVENT: 1,000 Placed object lifecycle event (detonated/deleted)

ACE3 Integration

Command Buffer Purpose
:ACE3:DEATH: 1,000 ACE3 death event
:ACE3:UNCONSCIOUS: 1,000 ACE3 unconscious event

Lifecycle Commands

Command Purpose
:SYS:INIT: Initialize extension, send :SYS:READY: callback
:STORAGE:INIT: Initialize storage backend, ungate buffered handlers
:MISSION:START: Start recording mission
:MISSION:SAVE: End recording, flush data, upload if configured
:SYS:VERSION: Get extension version

About

Extension that bridges the addon and web components.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors