Skip to Content
This documentation is provided with the HEAT environment and describes this HEAT instance.

Interface control document

1. Identification

ItemValue
DocumentICD-HCPUNREAL-001 rev 3
ProductHCP Unreal (hcp_unreal_plugin, VersionName 1.0 in its .uplugin)
Product versions coveredcommit aec62a9 on develop (no tagged release exists in this repository)
PeerHEAT Capture
Data modelHEAT ProtoLib C-API, bundled at Plugins/hcp_unreal_plugin/Source/ThirdParty/heat_protolib/capi/. No version marker is readable from this repository; the message and event shapes referenced here were verified directly against HEAT-ProtoLib at main.
Date2026-09-02
OwnerVRAI Simulation

2. Scope

HCP Unreal observes actors registered with it in a running Unreal Engine 5 project and publishes their state and events to HEAT Capture. It starts and stops a HEAT Capture session under Blueprint or C++ control, and keeps both the session and its publish connection up on its own once started - retrying indefinitely with backoff through any HEAT Capture outage - until explicitly stopped.

It does not control Unreal actors and consumes no gameplay input of its own: capture is observational only, driven entirely by what the host project’s own gameplay code does and by which actors and components it has been told to register and attach.

3. Interface inventory

IDNameDirectionTransportPeer
IF-HCPUNREAL-01Session controlProduct to peerHTTP REST, no TLSHEAT Capture
IF-HCPUNREAL-02Frame and event publishProduct to peerWebSocket, no TLSHEAT Capture

Both interfaces are driven by one supervisor state machine (FHEATCaptureTransport), advanced once per subsystem tick, started and stopped together by StartSession()/StopSession():

Stopped --Start()--> EnsuringSession --> Connecting --> Running ^ | |----------- Degraded <------------ (any failure, retried with exponential backoff, forever)

4. IF-HCPUNREAL-01 Session control

4.1 Transport and addressing

Plain HTTP. Base address is http:// + ServerHostname + : + ServerPort from Project Settings (default http://localhost:5005, matching HEAT Capture’s own shipped default). Endpoint paths are fixed in the plugin, not configurable.

4.2 Connection lifecycle

On Start(), the EnsuringSession state first GETs current-session: if a session is already active, it is adopted directly rather than starting a redundant one. Only if none is active does it POST start-session. A 409 response to start-session (another client won the race to start one between the check and this call) is treated as recoverable: the state machine re-checks current-session and adopts whichever session actually exists, rather than failing.

Any failure at this stage - unreachable host, non-2xx response, an unparsable response body - is retried with exponential backoff (RetryInitialDelaySeconds doubling to RetryMaxDelaySeconds, reset to the initial delay on the next success). This retry is indefinite: there is no attempt limit and no permanent failure state short of StopSession().

StopSession() sends one POST to stop-session, not retried - see Section 4.7 and Data produced for what happens to still-buffered frames first.

4.3 Authentication

None presented by the plugin. HEAT Capture requires a signed-in user for every /api/session/* call; a 401 response is recognised specifically (logged as “no user is signed in”) rather than treated as a generic failure, though the underlying retry behaviour is the same either way.

4.4 Messages

EndpointMethodPurposeAuth
/api/session/current-sessionGETAdopt an already-active session before starting a new oneSigned-in user required by HEAT Capture
/api/session/start-sessionPOSTStart a HEAT Capture sessionSigned-in user required by HEAT Capture
/api/session/stop-sessionPOSTStop the active sessionSigned-in user required by HEAT Capture

start-session and stop-session carry an empty body.

4.5 Data elements

FieldTypeUnitRangeOptionality
active (current-session response)booleann/an/arequired
session.userId (current-session response, when active)string (GUID)n/an/apresent when active is true
userId (start-session response)string (GUID)n/an/arequired on a 2xx response

Only the user id is read from either response; nothing else in the session object is parsed or re-published on IF-HCPUNREAL-02.

4.6 Timing

RequestTimeoutSeconds (default 5 s) bounds each individual call. Continuous while not yet Running: a failed attempt is retried after the current backoff delay, not only once.

4.7 Error behaviour

ConditionObservableProduct behaviour
No user signed in, wrong host/port, or any other current-session/start-session failureLogged with the specific reason (401, HTTP status, or “could not reach HEAT Capture”)Retried indefinitely with exponential backoff; capture continues to buffer locally (see IF-HCPUNREAL-02)
start-session returns 409Logged as a session-start conflictRe-checks and adopts the session that won the race; not treated as a failure
stop-session failsLoggedNot retried - the level/session teardown proceeds regardless

5. IF-HCPUNREAL-02 Frame and event publish

5.1 Transport and addressing

WebSocket over plain HTTP (ws://, no TLS). URL is ws:// + ServerHostname + : + ServerPort + /ws/publish, built from the same Project Settings as IF-HCPUNREAL-01.

5.2 Connection lifecycle

Connection is attempted only after IF-HCPUNREAL-01 has a valid session identity (the Connecting state). Once connected (Running), a lost connection - OnConnectionError or an unexpected OnClosed - re-enters Degraded and is retried through the same backoff and the same adopt-or-start session sequence, indefinitely, for as long as StartSession() has been called and StopSession() has not.

On Stop(), if still connected and frames are buffered, every buffered frame is sent immediately and the transport waits up to StopDrainTimeoutSeconds before closing the socket and calling stop-session - see Section 5.7 for why this is a best-effort window, not a confirmed delivery.

5.3 Authentication

None presented by the plugin. HEAT Capture requires a signed-in user for the WebSocket handshake to succeed.

5.4 Messages

One binary WebSocket frame per SimulationFrame, Any-wrapped. A frame is built only while a session is active (see IF-HCPUNREAL-01) and the global UpdateRate gate has elapsed - see Configure. If nothing changed and no event is pending, no frame is built at all for that tick. A built frame is always accepted for transmission regardless of whether the socket happens to be connected at that instant - see Section 5.6. HEAT Capture closes the connection on any frame larger than 10 MiB and ignores text frames.

5.5 Data elements

See Data produced for the entity registration, timing and binding model, and ProtoLib’s message reference for the component and event field catalogue - the plugin’s 53 generated components and 79 generated event emitters are 1:1 wrappers over those same messages.

5.6 Timing

Entity-state frames: gated by the global UpdateRate Project Setting (default 1.0 s; 0 attempts every tick), then further throttled per entity and per component - see Configure and Data produced. A built frame is enqueued into a bounded rolling buffer (MaxBufferedFrames, MaxBufferedMegabytes) rather than requiring an immediately-open socket; the buffer drains as soon as the connection is Running, oldest frame first, and evicts its own oldest entry first if it fills faster than it can drain. Events queue immediately when emitted but are only transmitted on the next frame that actually gets built, so worst-case event latency is bounded by UpdateRate, not sent as an independent, lower-latency message.

5.7 Error behaviour

ConditionObservableProduct behaviour
Connection lostWebSocket connection error / an unexpected close in the console logAutomatic reconnect through the full EnsuringSession -> Connecting sequence, retried indefinitely with exponential backoff; frames built in the meantime buffer instead of being dropped
Buffer fills faster than it can drain (sustained outage)Buffered frame countOldest buffered frame is dropped to make room for the newest - a sustained outage loses the oldest data first, not the newest
Frame exceeds 10 MiBHEAT Capture closes the connectionSame automatic-reconnect path as any other connection loss
Mission/level stops while frames are still buffered-Buffered frames are sent immediately if still connected; the transport waits up to StopDrainTimeoutSeconds for the OS socket buffer to have a chance to flush them before closing - this is a best-effort window, not a confirmed delivery, since Unreal’s IWebSocket::Send has no per-message delivery callback to wait on (unlike the equivalent HCP MACE transport, which can await each send)

6. Data model reference

Entities carry SimulationFrame → Entity → Component messages, populated by whichever of the 53 generated components (Generated/Components/) are attached and bound, plus a plugin-synthesized TransformComponent and LinkedEntityComponent for parent/child relationships. Events carry TrackingEvent with either a generic JsonEventData payload or a typed payload from one of the 79 generated event emitters (Generated/Emitters/). See Data produced for which fields this plugin never populates (for example, no automatic transform on a plainly-registered entity) and ProtoLib’s message reference for the schema itself.

7. Units, frames and conventions

ConceptConvention
Linear unitsUnreal world units, passed through to TransformComponent.position without conversion - confirm your project’s world-unit-to-metre scale matches what a consumer expects
Angular unitsOrientation is always sent as a quaternion (Rotation.X/Y/Z/W from Unreal’s own FQuat), never as raw Euler angles
Coordinate frameUnreal’s own left-handed world frame, as returned by USceneComponent::GetComponentTransform(). There is no conversion to a geodetic or ENU frame anywhere in this plugin
Geodetic datumNot applicable - this plugin has no geodetic position source
Time baseSimulationFrame.capture_time is wall-clock UTC milliseconds; simulation_time_seconds is Unreal’s own World->GetTimeSeconds(), seconds since the level started, not wall clock
EndiannessNot applicable - payloads are protobuf, not a fixed binary layout

8. Configuration affecting the interface

SettingInterface effect
ServerHostname, ServerPortBase address for both IF-HCPUNREAL-01 and IF-HCPUNREAL-02
UpdateRateGlobal gate on IF-HCPUNREAL-02’s entity-state frame cadence
RetryInitialDelaySeconds, RetryMaxDelaySecondsBackoff schedule shared by session start, session adoption, and WebSocket connect
RequestTimeoutSecondsTimeout for each individual current-session/start-session/stop-session call
MaxBufferedFrames, MaxBufferedMegabytesBounds on IF-HCPUNREAL-02’s rolling frame buffer - see Section 5.7
StopDrainTimeoutSecondsHow long Stop() waits for a best-effort flush before closing - see Section 5.7
Per-entity update frequency, per-component CaptureFrequencyHzFurther throttle IF-HCPUNREAL-02’s content, set in code/Blueprint rather than Project Settings - see Data produced

See Configure for the full settings reference.

9. Compatibility and versioning

Documented against hcp-unreal commit aec62a9 on develop (.uplugin VersionName 1.0) and Unreal Engine 5.7. There is no version negotiation on either interface. A SimulationFrame carrying components a given HEAT Capture build does not recognise is expected to be tolerated as unknown protobuf fields, consistent with how HEAT ProtoLib messages evolve elsewhere in this catalogue.

10. Security considerations

Neither interface uses TLS. Both rely entirely on HEAT Capture’s own signed-in-user requirement; the plugin presents no credential of its own. Treat network isolation between the Unreal workstation and the HEAT Capture workstation as the security boundary, consistent with HEAT Capture’s own security guidance - see HEAT Capture’s web API.

11. Conformance

  1. Configure ServerHostname/ServerPort to match a running HEAT Capture instance, per Configure.
  2. Sign in to HEAT Capture, then press Play in the Unreal Editor and call StartSession(). Confirm Started a new HEAT Capture session (or Adopted existing HEAT Capture session) and WebSocket connected in the Output Log.
  3. Register an entity with a HEATTransformComponent attached. Confirm it appears in HEAT Capture within one UpdateRate period.
  4. Stop HEAT Capture (or block the connection) while the level keeps running. Confirm the Output Log shows retry attempts, and that entities keep being registered/updated locally without the plugin crashing or giving up.
  5. Restart HEAT Capture. Confirm the plugin reconnects on its own - without restarting the level
    • and that buffered frames from the outage arrive.
  6. Call StopSession(). Confirm the session stops on the HEAT Capture side.

12. Revision history

RevDateProduct versionChange
12026-09-02commit 18a97e3Initial issue.
22026-09-02commit fd3b65fbScanForNewEntities and bVerboseLogging removed upstream (were undocumented dead settings, never read by the plugin); Section 8 updated to match.
32026-09-02commit aec62a9 (PR #5, merged to develop)Connection lifecycle replaced: automatic reconnect with exponential backoff, adopt-or-start session handling, and a bounded rolling frame buffer, ported from HCP MACE’s transport design. Sections 2 through 9 rewritten; six new Project Settings documented.
Opens your browser's print dialog. Choose "Save as PDF" as the destination.