Skip to main content

Frames, hands, and events

Use this family when your addon needs coherent timing and state rather than polling unrelated globals. Owner frame callbacks are the preferred boundary for nearly every integration.

Callback signature

using RockProviderFrameCallback = void(ROCK_PROVIDER_CALL*)(
const RockProviderFrameSnapshot* snapshot,
void* userData);

The snapshot pointer is borrowed for the invocation. Copy fields or the whole POD value if they are needed later; never retain the pointer.

registerFrameCallbackForOwnerV1 · slot 56

RockProviderResultV1 registerFrameCallbackForOwnerV1(
std::uint64_t ownerToken,
RockProviderFrameCallback callback,
void* userData,
std::uint64_t* outCallbackToken);

Registers a callback under a consumer with FrameSnapshots. Returns Ok, InvalidArgument, OwnerNotRegistered, PermissionDenied, or CapacityFull. The aggregate V1 callback capacity is 16.

ROCK calls it on the game-thread frame boundary with the same coherent snapshot it stores for polling. A callback fault revokes all stateful resources and callbacks owned by that consumer.

unregisterFrameCallbackForOwnerV1 · slot 57

RockProviderResultV1 unregisterFrameCallbackForOwnerV1(
std::uint64_t ownerToken,
std::uint64_t callbackToken);

Removes one callback belonging to the owner. Returns OwnerConflict if the token belongs to another owner and TargetUnavailable if it does not exist. Removal does not wait for an already copied invocation.

registerFrameCallback · slot 3 · legacy

std::uint64_t registerFrameCallback(
RockProviderFrameCallback callback,
void* userData);

Registers an unowned callback and returns its token, or 0 for a null callback or full capacity. It cannot be tied to consumer teardown and a fault only removes that callback. Retained for old V1 consumers; use slot 56 in new code.

unregisterFrameCallback · slot 4 · legacy

bool unregisterFrameCallback(std::uint64_t callbackToken);

Returns true when the legacy token was removed. Returns false for 0 or an unknown token. It has the same non-quiescent removal rule.

getFrameSnapshot · slot 5

bool getFrameSnapshot(RockProviderFrameSnapshot* outSnapshot);

Copies the latest coherent aggregate snapshot. The caller-provided size must be at least ROCK_PROVIDER_FRAME_SNAPSHOT_V1_SIZE (256 bytes). ROCK copies the smaller of the caller size and current structure size, then writes the copied size back.

Returns false for invalid size/pointer or before the first snapshot exists.

Important field groups:

GroupFields
Frame/timeframeIndex, deltaSeconds, enrichmentFlags
LifecycleproviderReady, menuBlocking, configBlocking, lifecycleFlags, lastLifecycleReason, stableFrameCount
GenerationsworldGeneration, skeletonGeneration, providerGeneration, collisionGeneration
ScalegameToHavokScale, havokToGameScale, physicsScaleRevision
Handstransforms, body IDs, state masks, primaryHand, offhandHand
Weaponform ID, body IDs/count, weaponGenerationKey, transition sequence
HMDhmdTransform, hmdForwardWorld when enrichment flags say valid
Change detectionstateSequence, stateChangeMask

The legacy bhkWorld, hknpWorld, and weaponNode values are temporary identity witnesses only. Do not dereference or retain them.

getPrimaryHandV1 · slot 15

RockProviderHand getPrimaryHandV1();

Returns ROCK's current primary/firing role (Left or Right). ROCK's role is independent of changing physical controller identity or mutating FO4VR's native handedness setting.

getOffhandHandV1 · slot 16

RockProviderHand getOffhandHandV1();

Returns the opposite role. If both roles must belong to the same frame, read primaryHand and offhandHand from one snapshot rather than polling these two calls separately across a possible handoff.

getHandFrameV1 · slot 17

bool getHandFrameV1(
RockProviderHand hand,
RockProviderHandFrameV1* outFrame);

Copies ROCK's root-flattened physics authority for Left or Right. This is not necessarily the final hFRIK-presented visual hand.

The caller size must be at least 112 bytes; ROCK prefix-copies up to the current 144-byte structure. Returns false for an invalid hand, missing snapshot, or unready provider.

Output flags identify validity, physical side, current primary/offhand role, and RootFlattenedAuthority. Appended fields carry frame and all relevant generations/sequences.

getPresentedHandFrameV1 · slot 53 · callback-only

bool getPresentedHandFrameV1(
RockProviderHand hand,
RockProviderHandFrameV1* outFrame);

Reads the final hFRIK-presented hand-root world transform after visual authority writers. It contains no scene node and sets PresentedVisual rather than RootFlattenedAuthority.

Call only from ROCK's game-thread callback boundary. Returns false for wrong thread, invalid hand/size, unavailable visual bridge/skeleton, unready provider, or non-finite output.

Use getPresentedHandPoseV1 when exact final finger-local transforms are also required.

getHandInteractionStateV1 · slot 58

RockProviderResultV1 getHandInteractionStateV1(
std::uint64_t ownerToken,
RockProviderHand hand,
RockProviderHandInteractionStateV1* outState);

Requires HandInteractionState. Returns a pointer-free value describing:

  • phase: idle, touching, selecting, pulling, catching, holding, releasing, stash candidate, or consume candidate;
  • target kind and form/body identity;
  • up to eight held body IDs;
  • primary/offhand, loose object/weapon, firing grip, part grip/carry, suppression, collision, touch-grab, fixed/global surface latch, mesh surface authority, mesh finger pose, collision fallback, and truncation flags;
  • effective input suppression and collision availability masks;
  • state, target, grip, and release sequences;
  • frame and generation identities.

For a touch grab, reservedTargetIdentity carries the value referenceNativeHandle when one exists, while primaryBodyId always identifies the resolved target—including wildcard global world surfaces. Interpret the touch-specific target/phase together with the owner-scoped state from slot 84.

Fixed-surface anchor reporting

When FixedSurfaceLatch is set, inspect the RockProviderSurfaceGripModeV1 value in surfaceGripMode instead of assuming that primaryBodyId's Havok contact is also the visible grab point:

surfaceGripModeMeaning
CollisionAnchorThe established Havok collision-shell contact is authoritative. This is also the mode when the experimental mesh feature is disabled.
MeshAnchorROCK resolved the contacted object's visible mesh and uses that point for the presented hand and API anchor.
CollisionFallbackMesh acquisition was enabled but could not safely resolve inside its projection or extraction limits, so ROCK retained the collision-shell latch.

SurfaceAnchorValid makes surfaceAnchorGame valid. For MeshAnchor, that point is on the resolved visible mesh and follows the target body's live transform while held, including animated statics such as doors. For the other two modes it reports the collision-shell anchor.

The related flags are deliberately independent:

  • MeshSurfaceAnchor means the mesh point is authoritative;
  • MeshFingerPose means ROCK's mesh-derived finger pose is actually active in the final hand presentation, not merely that a solve was attempted;
  • MeshCollisionFallback means the enabled experiment fell back to the collision anchor.

Treat an unknown future surfaceGripMode conservatively. Gate every anchor read on SurfaceAnchorValid, and use collision-compatible behavior unless MeshSurfaceAnchor is explicitly present.

The output size must be at least the current structure size. Returns NotReady before a snapshot exists and the standard owner/capability errors.

copyProviderEventsSinceV1 · slot 59

RockProviderResultV1 copyProviderEventsSinceV1(
std::uint64_t ownerToken,
std::uint64_t afterSequence,
RockProviderEventV1* outEvents,
std::uint32_t maxEvents,
RockProviderEventStreamStateV1* outStreamState);

Requires ProviderEvents. Copies global events plus events addressed to this owner from the fixed 256-row ring. Events owned by another consumer are hidden.

maxEvents == 0 permits outEvents == nullptr and still returns stream state. The stream state reports oldest/latest sequence, first/last copied, count, overwrite count, and gap flags. Advance your cursor only to lastCopiedSequence when copiedCount != 0.

Event payloads:

KindresultsubjectSequenceExtra data
LifecycleChangedRockProviderLifecycleReasonframe state sequencedata[0] lifecycle flags; data[1] ready; data[2] change mask
EquippedWeaponTransitionTerminaltransition resultterminal sequenceform/generation key; data[0] source; data[1] state flags
AuthorityLostsuppression invalidation reasonowner; data[0] authority kind
InteractionCommandTerminalcommand statecommand IDowner/hand/form; data[0] kind; data[1] failure
GrabStateChangedhand interaction phasehand state sequencehand/form; data[0] target kind; data[1] body ID; data[2] flags

Treat data[] as kind-specific and ignore unused elements.

getEquippedWeaponStateV1 · slot 60

RockProviderResultV1 getEquippedWeaponStateV1(
std::uint64_t ownerToken,
RockProviderEquippedWeaponStateV1* outState);

Requires FrameSnapshots. Returns a coherent value snapshot of current weapon form/generation, transition and terminal sequences, transition source, terminal result, state flags, frame, and broader generations.

Flags distinguish a valid weapon, identity/draw pending states, temporary bridge presentation, native renderability, hand-pose handoff completion, recovery exhaustion, and active transition.

Use transitionSequence to notice a new transition and terminalSequence to notice its final result. Do not infer completion merely because a weapon form ID appeared.

Frame enrichment helpers

Use hasLifecycleFlag for lifecycle masks. For other output masks, perform the same explicit typed cast pattern shown in Data model and units.

RockProviderFrameEnrichmentFlagV1 tells you whether delta time, HMD values, coherent hand roles, sequences, collision generation, and equipped transition sequence are present. RockProviderFrameStateChangeFlagV1 identifies the domains behind the newest stateSequence change.