2026-06-03 00:03:42 -05:00
2026-06-03 00:00:56 -05:00
2026-06-02 23:12:36 -05:00
2026-06-02 23:12:36 -05:00
2026-06-02 23:12:36 -05:00
2026-06-03 04:08:14 +00:00
2026-06-03 00:03:42 -05:00
2026-06-02 23:12:36 -05:00
2026-06-02 23:12:36 -05:00
2026-06-02 23:12:36 -05:00
2026-06-02 23:12:36 -05:00

RocketLeagueBot-Renderer

A web-based 3D Rocket League live viewer and replay system for telemetry streamed from a RocketSimVis-compatible UDP source (e.g., a training bot). Built with Go (Fiber + SQLite + zstd) and Three.js.

   bot ──UDP──▶  ┌──────────────────────┐
                  │  udpListener         │
                  │     │                │
                  │     ├──▶ Hub ───WS──▶│──▶ live viewers (read-only)
                  │     │                │
                  │     └──▶ Recorder ──▶│──▶ SQLite (zstd frames)
                  └──────────┬───────────┘
                             │
                             ▼
                         Player ──WS──▶ playback viewer (seek/pause/speed)

Features

  • Live UDP ingest at any rate; non-blocking hub keeps a slow client from stalling the whole pipeline.
  • 3D Three.js viewer: orbit, zoom, pan, mobile-friendly, low-quality mode for weak GPUs.
  • Rich HUD: ball position/height/speed/distance to each goal, per-car boost, speed, distance to ball, flags (DEMO / AIR / SONIC / TOUCH), packets-per-sec and last-packet age.
  • Automatic recording to SQLite with optional zstd compression (default on). Typical compression ratio of 510× on JSON telemetry.
  • Frame-accurate playback with seek bar, pause, variable speed (0.25× 8×), keyboard shortcuts, and autoplay-to-newer.
  • Server-pushed events: new/stopped/deleted recordings reflect in the UI without polling.
  • Live /api/stats endpoint and an in-panel stats box (uptime, packets, drops, db size, compression ratio).
  • HTTPS via Let's Encrypt autocert with HTTP→HTTPS redirect.
  • Basic Auth on every endpoint (constant-time comparison).
  • Graceful shutdown flushes the recorder before exiting.

Example

Live viewer with ball trail and predicted path

The viewer shows a detailed arena, car models (blue/orange), ball with ring indicator, boost pads with pulsing animation when active, and optional ball-trail/prediction lines. The HUD displays real-time metrics, and the side panel lists recordings and server stats.

Quickstart

git clone <repo>
cd RocketLeagueBot-Renderer
go build -o RocketLeagueBot-Renderer .

# Local HTTP only
./RocketLeagueBot-Renderer -password=secret -http=:8080

# Production with HTTPS (ports 80+443 must be reachable, DNS pointed at host)
sudo ./RocketLeagueBot-Renderer -password=secret -domain=example.com

Open the viewer at http://localhost:8080 (or https://example.com). Log in with any username and the password you set.

Flags

Flag Default Purpose
-password (required) Password for HTTP Basic Auth.
-domain (empty) Domain for Let's Encrypt autocert. Empty → HTTP only.
-http :80 HTTP listen address (must be :80 for ACME).
-tls :443 HTTPS listen address.
-udp :9273 UDP ingest address.
-certdir ./certs Directory for autocert cache.
-db ./recordings.db SQLite database file.
-retention 168h (7 days) How long to keep recordings.
-compress true zstd-compress recorded frames.
-verbose false Log every HTTP request (including WS upgrades).
-log-level info debug / info / warn / error.

UDP payload

Bytes received on the UDP port are treated as opaque — they are recorded verbatim (optionally compressed) and forwarded verbatim to live viewers. The viewer expects each packet to be a JSON object compatible with RocketSimVis, specifically:

{
  "ball_phys":  { "pos": [x,y,z], "vel": [x,y,z], "ang_vel": [...] },
  "cars": [
    {
      "car_id": 0, "team_num": 0,
      "phys": { "pos": [x,y,z], "vel": [...], "forward": [...], "up": [...], "right": [...] },
      "boost_amount": 0.0..1.0, "is_demoed": false,
      "on_ground": true, "ball_touched": false
    }
  ],
  "boost_pad_states": [true, false, ...]   // length 34, same order as PAD_DEFS in viewer.html
}

Coordinates are Unreal Units (1 uu = 1 cm). Field dimensions used by the viewer are the official RL values (4096 × 5120 × 2044 uu).

Quick UDP smoke test (requires nc):

echo '{"ball_phys":{"pos":[0,0,200],"vel":[0,0,0]},"cars":[]}' | nc -u -w0 127.0.0.1 9273

Playback controls

Action Mouse/UI Keyboard
Play / Pause ⏸ / ▶ button Space
Scrub Drag the seek bar / (±5 s)
Step ±1 frame ⏮ / ⏭ buttons Shift+← / Shift+→
Speed down/up Speed dropdown [ / ]
Stop playback ✕ button or LIVE button
Toggle autoplay "Auto: ON/OFF" button

Speed and autoplay preferences are persisted in localStorage.

API

All endpoints require HTTP Basic Auth.

  • GET / — Viewer HTML.

  • GET /ws — Live WebSocket stream (text JSON). Also delivers out-of-band server events: {"type":"event","kind":"recording_started|stopped|deleted","recording":{...}}.

  • GET /api/recordings?since=&limit= — Newest-first JSON list of recordings. since is RFC3339; limit is optional row cap.

  • DELETE /api/recordings/:id — Delete a recording (cascades frames).

  • GET /ws/playback?id=<n> — Playback WebSocket. Client sends:

    • {"cmd":"play"} / {"cmd":"pause"} / {"cmd":"stop"}
    • {"cmd":"seek","ms":12345}
    • {"cmd":"speed","value":2.0}

    Server emits:

    • {"type":"playback_start","name":...,"frames":N,"duration_ms":D}
    • {"type":"playback_frame","offset_ms":T,"frame":i,"data":<state>}
    • {"type":"playback_state","playing":bool,"speed":s,"position_ms":T} (every ~250 ms)
    • {"type":"playback_end"} / {"error":"..."}
  • GET /api/stats — Process & DB observability JSON.

Storage & tuning

The recorder buffers frames in memory and flushes to SQLite either every 64 frames or every 100 ms, whichever comes first, in a single transaction. This collapses ~120 fsyncs/sec down to ~10/sec at typical telemetry rates.

With -compress=true (default), each frame is zstd-encoded individually. Typical RocketSimVis JSON shrinks 510×. Disable with -compress=false if you want raw JSON in the DB.

Idle detection automatically ends the current recording after 5 seconds without packets, finalizing the row (frame count + duration).

Old recordings are pruned every hour according to -retention.

Security

  • Always run behind HTTPS in production; use -domain for free Let's Encrypt certs (ports 80 + 443 must be reachable).
  • Behind a reverse proxy: ensure it forwards Authorization headers and supports WebSocket upgrades on /ws and /ws/playback. Disable any buffering on those paths.

Dependencies

License

MIT.


Note: Not affiliated with Psyonix or Rocket League. For educational and personal use only.

S
Description
No description provided
Readme 253 KiB
v1.0.0 Latest
2026-06-03 05:52:41 +00:00
Languages
HTML 65.6%
Go 34.4%