PlayerPicker port
TL;DR: The original picks up another player by checking hand distance to their bones and teleporting that player’s origin each frame. CVR WASM never lets one client move another client’s player — only the local player can set its own position. The port inverts the mechanic: the picker broadcasts a “you’re being held” message, and the target’s own script moves them toward the picker on its own client. This respects the security boundary and still visually presents a pickup.
What ports and what doesn’t
Section titled “What ports and what doesn’t”| Feature | Ports? |
|---|---|
| Find nearest player within radius | Yes — Player.GetRemotePlayers() + GetViewPoint().GetPointPosition() |
| Move the picked-up player | Yes — via message, target self-moves via LocalPlayer.SetPosition() |
| Whitelist | Yes — comma-separated user-id list |
| Hand-to-bone distance check | No — remote bones not exposed; approximates with view point |
| Legacy fake ground support | No — not necessary in CVR |
| Logging / event spam | No — not ported |
Port strategy
Section titled “Port strategy”Two network messages:
| Tag | Payload | Direction | Effect on receiver |
|---|---|---|---|
MSG_HOLD | target network id | picker → target | target sets beingHeld = true, enters self-move |
MSG_RELEASE | target network id | picker → target | target sets beingHeld = false, stops self-move |
LateUpdate on the target moves LocalPlayer to the picker’s transform each frame. Only the local client does this, so there’s no cross-client authority.
Why the inversion matters
Section titled “Why the inversion matters”UdonSharp’s VRCPlayerApi.SetPosition() works on any remote player because VRChat grants that cross-client capability. CVR does not — LocalPlayer.SetPosition() only moves the local client. Any pattern that relies on remote-move-another-player needs this inversion. The Networking page covers the message model in depth.
Wiring
Section titled “Wiring”- Add
PlayerPickerto a grab trigger volume or a handheld prop root. - Hook a CCK button (or trigger volume) to
OnGrabClicked()andOnReleaseClicked(). - Add the same
PlayerPickerbehaviour to any world / player context where you want pickup to be receivable. TheHandleMessagepath is what actually moves the target, so the target client needs an instance subscribed to the network channel. whitelist: leave empty to allow everyone, or fill with comma-separated user ids. The whitelist path callsGetUserId()which requires theAccessUserIdentityworld permission — request it once at startup (see World Permissions) or leave the whitelist empty to skip the check.