Skip to content

VR: tracking with weapons drawn (other players' VRIK body pose) - #11

Draft
cec339 wants to merge 5 commits into
vr-supportfrom
svmp-body-pr
Draft

cec339 wants to merge 5 commits into
vr-supportfrom
svmp-body-pr

Conversation

@cec339

@cec339 cec339 commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

For humans

Other VR players show their VRIK pose (head, hands, arms, body, legs, held weapon), also with weapons drawn. It comes from headset and controllers only. On by default, VR only.

New since your 10-02 notes: with a menu open (inventory, magic, map...), the other player sees the game's idle, not a twisted body. The attack animation on a melee hit is removed. A body that cannot be posed with a weapon drawn shows the game's animation, not collapsed arms.

Turn off: F10 (only this client), or bUseLegacyHandPose=true / /ToggleLegacyHandPose on the server (everyone). Needs: client and server from this build; VRIK and HIGGS 1.10.10 on the sender.

Not done yet: handshake height, arrow on the bow, sheathed weapon at the hip. Missing armor after a door is fixed separately in #14.


For LLMs / agents (detailed)

Commits (base 5ab99be4, head 80a04c67; build token 0.0.1-37-g80a04c67)

  • 60d1e996 feat: server setting bUseLegacyHandPose for VR pose sync. Adds ServerSettings field Gameplay:bUseLegacyHandPose (default false) and the console command ToggleLegacyHandPose (typed /ToggleLegacyHandPose), which rebroadcasts settings.
  • edb057ee feat: vr full-body tracking for other players. The feature. The commit title says "full-body"; it means VRIK's IK body, not tracker-based FBT.
  • 91c87fb9 fix: remove synthesised vr melee attack action (maintainer request, 10-02). Removes only the Actor.cpp hook from 984bf95 that replayed an attack animation when the local player landed melee damage; the rest of that commit stays.
  • aba2981f fix: vr menus do not send the menu pose as the player's body (maintainer report "inventory breaks both poses"). Since fix: vr unpaused menus (skills, map, input and melee block) #10's UI.cpp kAllowList, the inventory, magic, skills, favorites, map, container, tween, message box, tutorial and console menus no longer pause the game, so VRIK's in-menu pose went out as the body. While one is open the body stream sends an explicit loss (SourceStream::Build aSuppressed, fenced so only a capture taken after the update that sees the close resumes), and the hand/head lane sends no hands and no head (a menu change forces the send). Also logs, VR only, health/menu/position when the player goes down and around the bleedout respawn (PlayerService, ActorValueService).
  • 80a04c67 fix: body-mode fallback leaves a drawn weapon to the animation. When the body lane cannot write a puppet (refused, not yet acquired, or stopped), the legacy hand lane used to pose drawn arms and collapsed them into the torso. In body mode that fallback now leaves a drawn weapon to the game's animation, decided from the SENDER's drawn state: new Drawn field in RequestHandPose/NotifyHandPose, relayed by the server, sent at once on change. After a sheathe nothing is resolved until the receiver's copy has read sheathed for 4.5 s, then the arms are re-resolved with a fresh rest. Legacy mode unchanged. Diagnostics: BODY remote_worn (puppet's worn armor when a refusal streak starts) and BODY local_worn (sender's worn armor on change, first sample always).

Totals vs base: 63 files, +8,626/-269. The wire format changed again (Drawn), so client and server must both come from this build.

Testing done

  • 89 test cases pass (TPTests); the VR client and server build; the changed client units compile for SE (compile only, not linked or run on SE).
  • 2026-09-28 (edb057e): one tester on two headsets: remote bodies both ways, F10 off/on, one- and two-handed weapons, NPC bumps (with bDisableCollisionBetweenOtherCharactersAndObjects=true), door trips, idle on tracking loss, the legacy toggle, the map menu.
  • 2026-10-03 (with 91c87fb + aba2981): two people, two headsets. Menus both directions: inventory, magic, skills, map and tween; the opener showed the game's idle and resumed on close (BODY menu: lines). Favorites was not exercised. A melee hit on an NPC showed no canned swing on the watching seat.
  • 2026-10-03/04 (with 80a04c6): a refused body with a weapon out used the game's drawn animation, without collapsed arms. Worn-list logs (BODY remote_worn/local_worn) plus owner observations showed armor present in the inventory but not drawn on rebuilt copies; the separate gear-redraw fix restored it.

Known issues

  • F10 is polled system-wide, so a press in another window toggles it too.
  • Arms can follow stale hand data for about 6-9 s after a player loses tracking before falling back to the idle animation.
  • A rare one-frame pose flash, seen only with the watched player's headset unworn.
  • Legacy mode is not byte-identical to the original hand sync: it runs through this PR's HandPoseService (reader and ownership latch), keeps posing the head alone when hands are absent (possible head jut on idle entry) and keeps the original one-frame flicker.
  • Two-handed grip works but the off hand isn't exact. Not covered: Staff of Magnus effects, a left-hand weapon, lean without a sidestep.
  • Hands may sit 10-20 cm apart in height during a handshake (per-seat VRIK calibration differs; we send joint rotations, not world hand positions). Not fixed.
  • No arrow is shown on a drawn bow; a sheathed weapon is not shown at the hip (see upstream list).
  • Only tested with our own race and body content; low-end performance untested.
  • A client without VRIK or HIGGS 1.10.10 reports body mode but sends no body.
  • The last published pose is kept while the other player is off-screen.
  • A sheathe can log a [critical] Hand sync: ... cached bone pointers ... no longer point line: a guard re-resolving a detached subtree, not a failure; too loud.

Found upstream, not caused by this PR

  • A rebuilt remote copy with no torso armor draws none of its worn armor (feet/hands vanish with boots/gauntlets); our body lane then correctly refuses the footless body (missing-skin-detail NPC L Toe0). Fixed in the separate PR VR: other players' worn armor is not drawn when they wear no torso armor #14 (fix/vr-remote-gear-redraw).
  • The VR inventory sometimes stays open after its panel closes: a rotating item preview floats in view and the game still reports the menu open (so this PR keeps the body suppressed). Reopening and closing the inventory clears it. Seen 10-04 on both seats.
  • A sheathed weapon is missing at the hip on remote copies in our tests. DetachHandItems skips drawn spawn requests and bodies with no equipped weapon or shield, without logging; whether that is the cause is unproven.
  • The bleedout respawn runs with a menu open and leaves it orphaned; a player went down with no damage logged (10-03).
  • Remote-player copies logged as deleted can load again as an extra copy after door trips (client CharacterService).

Data flow

  1. Capture (sender). A post-VRIK/HIGGS callback, registered once before GameWinMain, reads the local skeleton through a guarded reader (BodyReadMemory.h, BodyProbeMemory.h, ReadProbe.h) and sends RequestBodyPose unreliable with a 33 ms minimum interval. While a kAllowList menu is open it sends an explicit loss instead.
  2. Relay (server CharacterService). Ownership, schema, clock, order and rate checks, then NotifyBodyPose to players in range; nothing while bUseLegacyHandPose is true. NotifyHandPose now relays Drawn.
  3. Presentation (receiver, BodyPoseService). Retarget onto the remote actor's own reference skeleton (BodyReferenceSkeleton, BodyPoseComposition.h); write bones and the flattened bone tree from the render hook. A body is admitted only when every retail finger, forearm twist and toe bone is consumed by a verified skin (prevents the partial-skin limb warping).
  4. Post-pass (BodyPostPass). Thunks on BSFlattenedBoneTree vtable slots 0x2d/0x2e/0x2f (VR 0x140CB0030/60/90, pinned by RVA, 27-byte signature and RTTI name; refuses loudly on mismatch) re-apply the last published pose after the job-thread recompute, validated against the live tree and a 64-unit fresh-root guard.
  5. Fallback (HandPoseService). The hand pose path covers any puppet the body lane is not writing, behind HandPoseLatch.h. In body mode a drawn weapon there is left to the game's animation (HandPoseLatch::DrawnFallbackIdle).

Log lines (client tp_client.log)

  • Startup: BODY startup: post-VRIK capture registered once before GameWinMain; schema=2 world axes, BODY POSTPASS mode=on|off|refused ....
  • Mode: Pose sync mode: body|legacy (server bUseLegacyHandPose=0|1).
  • Receiver every 10 s per remote: BODY status remote= actor= reason= frames= writes= refusals= ... pp_calls= pp_reapplies= .... Healthy: refusals, pp_stale, pp_busy, pp_overlap, publish_fail, unmapped are 0; pp_moved bursts at doors are expected.
  • Sender every 10 s: BODY source sent= captured= gaps= lost= capture_us_max= state= reason= native=.
  • Menus: BODY menu: <Menu> open, local body pose suppressed (sent as a loss) / ... closed, local body pose resumes from a capture taken after this; Hand sync: local hands are now not synced (... menu open, no head sent: <Menu>).
  • Drawn fallback: Hand sync: actor X has a weapon drawn and no body write this frame; its animation owns it until a body write or a sheathe, Hand sync: actor X sheathe settled after the drawn fallback; re-resolving its arms with a fresh rest.
  • Refusals: BODY native_detail scope=remote actor= reason=missing-skin-detail ... bone=, BODY remote_worn remote= actor= worn=[...], BODY local_worn worn=[...].
  • F10: BODY receiver OFF|ON ..., Pose sync: displaying other players' tracked pose is now OFF|ON. Sending is unchanged.

Review provenance

  • Feature: spec and diff reviewed adversarially by Codex (13 and 5 findings; all applied except the off-screen snapshot, listed above); Opus review "SAFE TO TEST".
  • 91c87fb + aba2981: Codex diagnosis review, diff review (REVISE: 1 High fence, 1 Medium, 1 Low; all fixed), recheck ACCEPT.
  • 80a04c6: Codex REVISE (2 Medium, 3 Low), recheck REVISE (2 Medium, 2 Low), recheck ACCEPT with one non-blocking edge: a sheathe settle that started while the puppet was off screen can expire before a redraw+sheathe.

🤖 Generated with Claude Code

https://claude.ai/code/session_014b5v4UAeR2a8gR537GMuvi

cec339 and others added 2 commits September 27, 2026 22:48
Adds Gameplay:bUseLegacyHandPose (off by default) to ServerSettings and a
ToggleLegacyHandPose console command that flips it for every connected
player at runtime through the existing settings broadcast. On, VR clients
use the original hand pose sync: no body poses are captured, sent or
relayed, and hands are not synced while a weapon is drawn. The following
commit is what reads it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLbDoEM12w8cqAb9MnBTPC
Shows every VR player's tracked VRIK body on the other clients instead of
the stock animation: head, arms, hands, spine, legs and the held weapon,
while Tilted keeps the actor's identity, equipment, AI, collision and
root position. On by default; F10 still switches the display back to the
game's own animation at runtime, and the server's bUseLegacyHandPose
restores the original hand pose sync for everybody.

- Capture (sender): a post-VRIK/HIGGS callback reads the local skeleton
  through a guarded reader and sends RequestBodyPose at ~30 Hz, unreliable.
  The server checks ownership, schema, clock, order and rate before
  relaying NotifyBodyPose to players in range, and relays nothing while
  bUseLegacyHandPose is on.
- Presentation (receiver): the pose is retargeted onto the remote actor's
  own reference skeleton and written to the bones and the flattened bone
  tree from the render hook, before the hand pose pass. A re-apply on the
  BSFlattenedBoneTree vtable's three downward-pass slots (pinned by RVA,
  signature and RTTI; refused loudly on any mismatch) restores the written
  pose after the job-thread animation pass, which removes the one-frame
  flash between tracked and animated pose.
- HandPoseService stays the fallback for any puppet the body lane is not
  writing, behind an ownership latch. In body mode hands keep syncing
  while drawn and the received grip is applied to the attachment node; in
  legacy mode the original drawn rule and head tracking are unchanged.
- VR only (#if TP_SKYRIMVR at every new call site; the SE path is
  unchanged). Tests cover the codec, relay decision, history, composition,
  post-pass registry, readers, latch and settings on the wire.

Both client and server must run this build (BUILD_COMMIT check).
Built on the HandPoseService by Sergei Kazakov.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLbDoEM12w8cqAb9MnBTPC
@cec339 cec339 changed the title VR: full-body tracking for other players (VRIK body on remote players) VR: tracking with weapons drawn (other players' VRIK body pose) Sep 28, 2026
cec339 and others added 3 commits October 3, 2026 12:05
The VRIK body pose now shows the swing on other players, so the attack
animation replayed on a hit (added in 984bf95) is no longer needed and
fights the pose. Only the Actor.cpp hook is removed; the rest of that
commit (struct offsets, activation guard, dump flags, tools) stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVeCPjbR9c2doxTDCJa3p6
Since the inventory, magic, skills, favorites, map, container, tween,
message box, tutorial and console menus no longer pause the game in VR
(UI.cpp kAllowList), VRIK's menu pose kept going out as the player's body
and the other player saw a twisted body while one was open. While one is
open the body stream now sends an explicit loss, with or without a new
capture, and resumes only from a capture taken after the update that saw
the menu close; the hand/head lane sends no hands and no head, at once on
the menu change even when the hands were already off, so the receiver
shows the game's animation, as for tracking loss. Both log the menu on
change.

Also logs, VR only, the player's health, open menu and position when it
goes down and when the bleedout respawn starts and returns: on 2026-10-03
a player went down with no damage seen while the inventory was open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVeCPjbR9c2doxTDCJa3p6
When the body lane cannot write a puppet (2026-10-03: every frame refused
with missing-skin-detail after a rebuild), the legacy hand lane took over
and posed the arms with a weapon drawn: the arms collapsed into the torso
with the sword at the shoulder. In body mode that fallback now leaves a
drawn weapon to the game's animation, decided from the sender's own drawn
state, which the hand pose messages now carry and send at once on change (RequestHandPose/
NotifyHandPose Drawn, relayed by the server). When it ends with a sheathe,
nothing is resolved or posed until the receiver's copy has read sheathed for 4.5 s, then the
arms are re-resolved with a fresh rest. Legacy mode is unchanged.

Also logs, VR only, the puppet's worn armor when a body refusal streak
starts (BODY remote_worn, re-armed per rebuilt tree) and the sender's worn
armor on change, the first sample per connection always (BODY
local_worn), so a skin refusal can be traced to what each side wears.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVeCPjbR9c2doxTDCJa3p6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant