Phasmophobia VR: a one-instruction fix for the remote item offset
VR players' grabbed items appeared floating away from their hands on remote clients. The fix was a single mov instruction in the IL2CPP-compiled DLL.
In Phasmophobia (Unity + IL2CPP), VR players had a visual bug that only existed on remote clients: when a VR player picked up an item, everyone else saw it floating somewhere away from their hand. On the VR player's own screen everything looked correct.
It's the kind of bug that's hard to even describe in a bug report — it works for you, it's broken for everyone else, and nothing logs the cause. So I went reversing.
What the player sees
A VR player grabs a prop. Locally, it's glued to the controller — moves with the hand, rotates as expected. On every other player's screen the same prop hovers off to the side and drifts wherever the VR player physically walks in their room. Smooth network sync. Wrong transform.

Digging in
Phasmophobia ships as an IL2CPP build, so the original C# is gone — what's on disk is GameAssembly.dll, a native binary where every Unity class was transpiled to C++ and then compiled to machine code. After dumping the IL2CPP metadata to recover class and method names, I opened the binary in a disassembler and walked to the grab logic.
The function I was after is VRGrabbable::NetworkGrab, called both for the local VR player and for the remote replica of any other VR player.
Root cause
The function has two code paths:
- Local VR path — parents the grabbed item to the player root transform (the same root the character is driven from).
- Remote path — parents the grabbed item to the visual character model (head, arms, body rig).
That divergence is the bug. The subtlety that makes it specifically a VR problem:
In VR, you can physically walk around your room without the game root moving. The character model is driven by the VR head and hand poses, and floats relative to the root.
For a non-VR character, the model sits at the root, so parenting to the model or to the root gives the same result. For a VR character, the model can be meters away from the root — and that offset is exactly what everyone else saw on the floating items.
In short: the bug had been in the code forever, but only ever manifested when the player doing the grabbing was using VR.
The fix
In the disassembled remote path, the wrong parent was being loaded with:
mov rcx, [rdi+30h] ; rcx = this->characterMeshThe local path doesn't dereference any field — it just uses this directly:
mov rcx, rdi ; rcx = this (player root)The encodings line up cleanly. The original memory load is 4 bytes, the register-direct mov is 3, so a single 0x90 nop pads the slot and preserves instruction boundaries for the rest of the function:
- 48 8B 4F 30 mov rcx, [rdi+30h]
+ 48 8B CF 90 mov rcx, rdi ; + nopI shipped it as a runtime patch via a memory write at the function offset — no relocation, no trampolines, no hooking framework needed.
Outcome
After the patch, remote clients parent items to the same transform the local client uses, so the visual position matches exactly. I sent the analysis and the byte-level diff to Kinetic Games, and the official fix shipped in patch v0.1710.
One instruction. Four bytes. The kind of fix that takes longer to write up than to apply.