Production integration checklist
Use this before shipping a ROCK consumer. The API is designed to fail closed, but a consumer still has to respect extent, capability, lifetime, and thread contracts.
Discovery
- Acquire
ROCKAPI_GetDescriptorV1through the SDK helper and validate its size, version, table pointer, and table bytes before touching optional slots. - Fall back to the legacy export only within the SDK helper's documented safe path.
- Require API version 1; do not assume every V1 provider has the same table extent.
- Check the named table-byte constant for the last function family you use.
- Check the relevant feature bit; use the secondary feature mask when applicable.
- Read provider limits instead of hard-coding runtime capacities.
- Treat initialization errors as actionable dependency/compatibility diagnostics.
Owner registration
- Request only the capabilities the product actually needs.
- Give the registration a stable, null-terminated mod name.
- Store
ownerToken,grantedCapabilities, and provider generation as one invalidatable session. - Check each required capability in
grantedCapabilities; never confuse a feature bit with permission. - Define a graceful read-only or disabled mode for optional capabilities.
Threading and callbacks
- Make live scene/physics reads only from owner frame/animation callbacks.
- Keep callback work bounded: no blocking I/O, unbounded scans, or avoidable allocation.
- Never throw across the SDK callback boundary.
- Keep callback and
userDatastorage alive through an already-copied invocation after unregister. - Do not mutate native graph data during
NativeGraphOutput. - Move durable logging/serialization out of the callback path through your own bounded value queue.
Generations and identity
- Populate world, skeleton, and provider generation guards on stateful publications.
- Pair weapon body/part identity with
weaponGenerationKey. - Pair external body IDs with an addon-defined body generation.
- Pair touch target IDs with a nonzero caller generation and increment it whenever the backing mechanism or match meaning changes.
- Invalidate caches on lifecycle/equipped events and generation changes.
- Never dereference or retain
uintptr_tidentity witnesses. - Never treat a numeric body ID as immortal or globally reusable.
Leases and ownership
- Use the shortest practical nonzero lease and refresh deliberately.
- Remember that a lease made at frame
FforNframes is active whilecurrentFrame < F + N. - Treat
OwnerConflictas an ownership boundary, not a busy retry target. - Make publication replacement semantics explicit in your code; setters replace the owner's whole family state.
- Clear state explicitly in reverse ownership order on normal shutdown.
- Keep lease expiry as abnormal-loss protection, not normal cleanup.
Commands and streams
- Persist command IDs until a terminal state/event arrives.
- Do not report a queued request as completed interaction.
- Cancel only before the command's committed stage; handle
AlreadyCommittedhonestly. - Maintain a cursor per event/contact stream and filter scope.
- Advance to
lastCopiedSequence, not blindly to the latest sequence when a page was truncated by buffer capacity. - Detect gap/overwrite flags and rebuild derived state from snapshots.
- Never infer impulse magnitude from external contact weight.
Input
- Derive button edges locally from fresh level samples and sample sequence.
- Treat stale/unavailable samples according to
availabilityReason. - Suppress only actions the product fully replaces.
- Inspect caller and effective suppression state when behavior is shared.
- Release input and offhand leases before disabling the feature.
- Suppress native VATS release and V.A.N.S. hold independently unless the product intentionally consumes the complete Pause gesture.
Touch targets and world tools
- Publish separate wildcard descriptors when two hands must resolve two different bodies.
- Narrow wildcard layer/motion masks to the intended surfaces.
- Wait for
Yieldedbefore scripted/native motion takes a held mechanism. - Respect the per-owner per-frame raycast budget and maximum distance.
- Treat a successful raycast miss as a valid result, not an API failure.
- Bind exact-hand requests to the current weapon and active handling lease.
- Bind collider focus to the complete current weapon generation/body set.
Structures and values
- Value-initialize every public structure with
{}. - Preserve zero in all reserved fields.
- Honor validity flags before reading optional transforms, vectors, IDs, or fields.
- Use exact-size requirements for setters and getters that document them.
- Use count/copy patterns with bounded storage and returned counts.
- Ensure every published float/transform is finite.
- Bound and null-terminate every public character buffer.
Performance
- Cache table/capability/limit decisions once per provider session.
- Cache weapon metadata by weapon generation and composition signature.
- Use fixed arrays sized to public limits in callbacks.
- Avoid repeated whole-buffer copies when a sequence/signature did not change.
- Rate-limit diagnostics and disable overlay publication when not requested.
- Never poll all optional surfaces every frame by default; read what the active product state needs.
Shutdown order
A typical stateful consumer shuts down in this order:
- Stop product state machines from publishing new work.
- Clear debug overlay, collider focus, touch scopes, and hand visual authority from the valid callback thread.
- Clear weapon drives/targets and animation/equipped-handling authority.
- Release input suppression and offhand reservation.
- Clear external child scopes/bodies.
- Cancel still-cancellable commands or mark committed ones for terminal drain.
- Unregister callbacks while retaining their backing storage long enough.
- Unregister the consumer owner.
- Clear the local API pointer/session cache.
Unregistering the owner revokes its provider resources, but explicit ordering keeps the visible transition controlled.
Validation matrix
At minimum, test these cases in addition to the happy path:
| Case | Expected consumer behavior |
|---|---|
| ROCK DLL absent | Load without crashing; clearly disable dependent features. |
| Shorter V1 table | Never touch a missing slot; downgrade only affected features. |
| Capability denied | Disable that feature without retry spam. |
| Provider not ready | Wait for readiness/lifecycle transition. |
| World or skeleton generation changes | Stop stale writes and rebuild cached identity. |
| Callback unregister during dispatch | No use-after-free. |
| Consumer callback faults | Provider revokes state; consumer records/recovers safely. |
| Lease refresh stops | State expires and ROCK returns to its normal owner. |
| Stream ring overwrites | Gap is surfaced; derived continuous state is rebuilt. |
| Command cancellation races commit | AlreadyCommitted is handled and terminal result is still observed. |
| External body ID is recycled | Old generation is never accepted as the new body. |
| Equip/menu/world transition | No writes while corresponding lifecycle permission is absent. |
| Touch target is yielded | No body motion until Yielded; new acquisition waits for remove/republish. |
| Raycast budget exhausted | Feature degrades for the frame without an unbounded retry loop. |
| Collider focus weapon changes | Focus invalidates and normal overlay returns. |
Troubleshooting
| Symptom | First checks |
|---|---|
| Function pointer is null | Descriptor table bytes, named family extent, feature bit. |
PermissionDenied | Requested capability versus granted mask. |
WrongThread | Move the operation into an owner callback. |
Repeated WorldNotReady / NotReady on guarded writes | Stop reusing old generation identity; rebuild publication from the latest snapshot. |
| Publication appears for one frame | Lease was not refreshed, or generation changed. |
| Input is still blocked after clear | Inspect effective state; another owner may hold a lease. |
| Part target unexpectedly gates all grips | An exclusive target activated whitelist semantics; consider NonExclusive. |
| Drive resolves but does not win | Inspect application result and priority/owner arbitration. |
| No external contacts | Registration policy, row owner/scope token, capability pair, body ID/generation, collision availability. |
| Missing stream transitions | Inspect gap/overwrite flags; increase drain cadence or page capacity within limits. |
| Authored authority active but no animation | Check runtime-provider availability, capture validity, and fault flags. |
| Hand visual call fails | Callback thread, visual-writes flag, hFRIK/skeleton readiness, transform validity, priority/mask. |