Files
TeslaRel410/emulator/CAMERA-REVIEW-NOTES.md
CydandClaude Fable 5 b2f0becbd4 Document Live Cam + Mission Review architecture (CAMERA-REVIEW-NOTES.md)
Researched from the readable RP MUNGA source (BT shares the engine):
host roles are assigned per-IP in the mission egg (hostType= on each
pilot page); a CameraShip/MissionReview host gets a CameraDirector
player that auto-cuts to the top-ranked pilot every 10s, with manual
flight via the same cockpit hardware as pods (CameraRIOMapper) and
fixed clamped trackside cameras authored in-game into MAPS/<map>.cam.
Mission Review records raw NetworkPackets (SpoolFile -> last.spl) and
replays them through a networkless playback app with timestamp bias --
the replay re-simulates the battle from replication traffic.

Same BTL4OPT.EXE serves every role; standing up Live Cam on the
emulated pod LAN is an egg edit, not a build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:21:46 -05:00

10 KiB
Raw Permalink Blame History

Live Cam & Mission Review — how the VWE games did it

Research notes, 2026-07-08. Sources: CODE/RP/MUNGA + CODE/RP/MUNGA_L4 (the readable Red Planet tree; BattleTech shares the MUNGA engine and has the matching CODE/BT/BT_L4/BTL4PB.HPP + CODE/BT/MUNGA/SPOOLER.HPP), the sda4/BTLIVE/MAPS/*.CAM files, and the mission eggs. Everything below is from the code itself, not conjecture, except where marked.

Both features ride the same insight: the game is a replicated-entity network simulation. Every machine on the mission LAN runs the full sim and mirrors the others' entities from network traffic (master/replicant, see HOST.HPP). A spectator view is therefore just one more host that renders without piloting — and a replay is just the same network traffic played back from a file.

1. Host roles come from the mission egg

HOST.HPP:41:

enum HostType {
    GameMachineHostType,     // 0 — a pod
    CameraShipHostType,      // 1 — Live Cam station
    MissionReviewHostType,   // 2 — replay station
    ConsoleHostType          // 3 — the ops console
};

The console assigns a role to every IP on the mission when it authors the egg. Each [pilots] pilot=<ip> page carries a mandatory hostType= key (MISSION.CPP:480 — boot aborts with "ERROR: no host type in egg!" if absent). Our own cavern.egg shows a pod: [200.0.0.113] hostType=0 ....

At player-creation time, Registry::MakePlayer (REGISTRY.CPP:255) switches on the local host's type:

  • GameMachineHostType → an ordinary Player (pod pilot);
  • CameraShipHostType / MissionReviewHostType → a CameraDirector player (the comment notes "Not a CameraShip to run on a POD!").
  • Additionally, if the mission's gamemodel field is "camera", the player's vehicle gets CameraShipPlayerFlag — so a GameMachine host can also fly a camera ("VTV, Mech, CameraShip w/controls" per the comment).

The same game EXE serves every role — pod, camera, review — chosen purely by the egg. This is the actionable heart of it for us (§5).

2. Live Cam = CameraShip + CameraDirector

Classes: CAMSHIP.HPP/CPP (the flying camera vehicle, a Mover entity) and DIRECTOR.HPP/CPP (the auto-director, a Player subclass).

  • CameraDirector (DIRECTOR.CPP:184 BeADirector): every frame it checks timeLeftOnPlayer; when it expires it finds the top-ranked player (FindPlayerByRank(0) over the "Players" entity group) and, if the leader changed, dispatches a CameraShip::DirectionMessage{goalEntity, timeOnCamera, focusOffset} and resets the shot clock to 10 seconds. So the default broadcast is "follow the leader, re-cut every 10 s".
  • HUD (DIRECTOR.CPP:118 UpdateHUD + CameraShipHUDRenderable in L4VIDRND.CPP:2199): the feed overlays the followed player's identity and a ranking window cycling 10 s on / 15 s off — always on during the final 30 s of the mission, hidden once the mission stops.
  • CameraShip (CAMSHIP.CPP:340 FollowGoal): aims at focusOffset × goal->localToWorld, tracks the goal's linear position, and asks the director for a new goal when timeOnCamera runs out. Camera height is operator-adjustable (ApplyControlledCameraHeight).

Manual camera operation

CAMMPPR.HPP (CameraControlsMapper subsystem) + L4MPPR.HPP: the camera station reads the same cockpit hardware as a pod — there are three concrete mappers: CameraL4Mapper, CameraThrustmasterMapper, and CameraRIOMapper (a full RIO cockpit could fly the camera). Attributes: stick / throttle / pedals / reverse-thrust / drive-camera / slide-or-clamp. Buttons: DirectorMode toggle (auto-director vs manual), Increment/DecrementCamera (cut between fixed cameras), and Create/Delete/OutputCameraInstances — the camera-authoring workflow.

Fixed trackside cameras — the .CAM files

CAMINST.HPP/CPP + CAMMGR.CPP (CameraInstanceManager): each map has a notation file MAPS/<map>.cam (e.g. sda4/BTLIVE/MAPS/ARENA1.CAM) holding [cameraN] pages:

[camera0]
cameraID=0
cameraType=DefaultCameraType      ; or AlwaysSeesCameraType
tranx/y/z = position
quatx/y/z/w = orientation
minYawClamp/maxYawClamp           ; pan limits (radians)
minPitchClamp/maxPitchClamp       ; tilt limits

These are broadcast-style fixed cameras with pan/tilt clamps; CameraInstance::CalculateCameraRotation pans them to track the goal within the clamps. They were authored in-game: the operator flies to a spot, presses CreateCameraInstance, and OutputCameraInstances writes the .cam file back out (CameraInstanceManager::WriteNotationFile).

3. Mission Review = record the network, replay the network

Recording (SPOOLER.HPP/CPP, L4SPLR.HPP)

  • SpoolFile is a MemoryStream of raw NetworkPackets (header + payload, back to back). States: Empty → Spooling → Stored → Playing. SpoolPacket() appends (hard-exits if the RAM buffer fills); SaveAs/Read move it to/from disk. The canonical file name is last.spl (SPOOLER.CPP:251,274) — same "last mission" convention as LAST.EGG.
  • MissionReviewApplicationManager owns a pool of spool_count RAM buffers of spool_size each.
  • L4SpoolingApplication (+ L4SpoolingNetworkManager, SpoolingInterestManager) is the recorder: its ReceiveNetworkPacket() override tees every packet arriving over the mission LAN into the active spool while also processing it normally — so the recording host fully participates (renders, directs cameras) as it records. A missionSpooled flag tracks state; the spool is stored (→ last.spl) when the mission stops.

Because the recorder simply captures received replication traffic, the recording is exactly what any spectator host saw: entity creations, state updates, weapon events, kills — everything needed to re-simulate the battle, at a tiny fraction of the size of video.

Playback (BTL4PB.HPP, L4SPLR.HPP)

  • BTL4PlaybackApplication (BT flavor of RPPlaybackApplication) is a full game application whose network manager is L4PlaybackNetworkManager — a NetworkManager with no real network: CheckBuffers() sources packets from the SpoolFile instead of the wire.
  • SpoolerTask (an ApplicationTask) walks the spool with NextPacket() and DispatchPacket()s each one into the app, using timeBias to shift the recorded timestamps onto the playback clock — packets are released on the same relative schedule they were recorded, so the replayed entities move exactly as they did live.
  • The playback host's player is again a CameraDirector (host type switch, §1) — so a review session is the Live Cam presentation (auto-cuts to the leader, ranking overlays, fixed trackside cameras) running over recorded traffic. This is what the debrief room / take-home tapes showed.

What's NOT in the repo

The readable tree is partial: L4SPLR.CPP (SpoolerTask::Execute pacing, exact spool start/stop triggers) and BTL4PB.CPP are missing — only their headers survive. The compiled implementations are in the era binaries. The trigger for "start spooling" (console message vs automatic at mission start) is therefore unconfirmed; the message-handler names (Run/Stop/AbortMissionMessageHandler on the spooling app) suggest it simply records from RunMission to StopMission.

4. The physical setup at a center (period context, inferred)

A "camera ship" machine = one more networked PC with a Division board, its out-the-window video feeding the spectator monitors (and the VHS deck for take-home tapes). The mission-review station = the same machine (or another) replaying last.spl into the debrief room after the mission. Both could be driven hands-off (CameraDirector) or by an operator with a joystick/RIO.

5. What this means for OUR pod project

  1. No new binary needed. BTL4OPT.EXE already contains all of it. A Live Cam station = a second emulated machine on the pod LAN whose egg page says hostType=1. Mission Review = hostType=2 + a last.spl.
  2. The console app owns the roles. When the TeslaSuite console port authors eggs, adding a camera-host page is how you light this up — it's an egg field, not a build variant.
  3. We can record spools ourselves. The spool format is trivial (length- prefixed file of raw NetworkPackets, SPOOLER.CPP:50-95); our emulated NIC path (slirp/pcap) could tee mission traffic into a last.spl independently of any spooling host — giving Mission Review of any pod session after the fact.
  4. Camera authoring works from a cockpit. CameraRIOMapper means the vRIO can fly the camera ship and author .cam files for our arenas.
  5. Renderer note: a camera/review host renders through the same Division wire our bridge already decodes — the bridge should work unchanged for a spectator host, modulo the CameraShipHUDRenderable overlays (2D HUD layer, same 0x29/0x2b dpl2d path as the pod HUD).

Quick reference — the cast

Piece File Role
HostType enum MUNGA/HOST.HPP pod / camera / review / console, set per-IP in the egg (hostType=)
Registry::MakePlayer MUNGA/REGISTRY.CPP host type → Player or CameraDirector
CameraDirector MUNGA/DIRECTOR.* auto-director: cut to rank-0 player every 10 s; ranking-window HUD
CameraShip MUNGA/CAMSHIP.* the flying camera vehicle (a Mover entity)
CameraControlsMapper MUNGA/CAMMPPR.* stick/throttle/pedals camera flight, director toggle, camera authoring buttons
CameraL4/Thrustmaster/RIOMapper MUNGA_L4/L4MPPR.* camera station input bindings (RIO cockpit supported)
CameraInstance(Manager) MUNGA/CAMINST.*, CAMMGR.CPP fixed clamped cameras; reads/writes MAPS/<map>.cam
SpoolFile MUNGA/SPOOLER.* NetworkPacket recording; last.spl on disk
L4SpoolingApplication MUNGA_L4/L4SPLR.HPP records the mission LAN while participating
BTL4PlaybackApplication + SpoolerTask BT_L4/BTL4PB.HPP replays the spool with timeBias re-timing; no real network