Skip to content

Avatar Context Examples

TL;DR: An avatar script runs on every client that sees the avatar, not just the wearer’s. Reads are generous (hierarchy, components, player identity), writes are gated to the avatar’s own content root. Global APIs like LocalPlayer.*, FileStorage.*, WorldPermissions.*, WorldSettings.* are world-only and will throw WasmAccessDeniedException at runtime if called from an avatar script.

  • Read and write their own hierarchy: transform, child GameObjects, components on them.
  • Respond to Unity lifecycle events (Awake, Start, Update, LateUpdate, FixedUpdate, OnEnable, OnDisable).
  • Respond to trigger and collision events via Unity’s standard events (OnTriggerEnter, OnCollisionEnter, …).
  • Listen to CVR game events: OnPlayerJoined, OnPlayerLeft, OnPlayerRespawned, OnInstanceOwnerChange, OnWorldPermissionsChanged, OnInputReady.
  • Differentiate “me” vs “everyone else” via CVRScriptOwnerContext — a script in Self owner context runs on the wearer’s client; same script in Other context runs on remote viewers.
  • Send/receive network messages via WasmScripting.Networking (no context restriction).
  • Sample input via CVR.CVRInput.* (reads only — writes only materialize when OwnerContext == Self).
  • Call LocalPlayer.* — world-only.
  • Touch FileStorage / WorldPermissions / WorldSettings — world-only.
  • Mutate transforms outside the avatar’s own root (scope check fails).
  • Create new portals, spawn props, or destroy other players’ content.
  • Observe OnPropSpawned / OnPortalCreated meaningfully — those dispatch in world VMs.
  • Cache the owner check. CVR.WasmUtils.GetOwnerContext() returns Self on the wearer’s client; capture it once in Start.
  • Use the owner check to gate write-heavy code. Cosmetic/visual code runs on every client; expensive logic should short-circuit unless you’re the wearer.
  • Subscribe to OnInputReady before reading CVRInput.* — input is only guaranteed populated after this event.
  • Wire UI via persistent UnityEvents (Button.onClick → a public void handler on your behaviour). Build-time rewiring sends the click into your WASM script automatically.
  • Use [WasmSerialized] on any private field that represents “preserved-across-reload” avatar state (toggle status, accessory index, etc.).
  • Don’t try to rewire world state from an avatar script — scope checks will fail.
  • Don’t assume your script only runs on the wearer. Avatar scripts run on every client that sees the avatar. Compute one set of values for visuals; gate authoritative writes behind OwnerContext == Self.
  • Don’t call FileStorage.* from an avatar script — every binding calls CheckAccess(World, Any) and throws WasmAccessDeniedException outside a world VM.
  • Don’t hold references to external-content transforms (world props, other avatars). Scope checks will reject mutations; even reads depend on the specific binder’s scope mask.
  • Don’t use OnApplicationQuit / OnGUI / OnApplicationFocus — these are stubbed and never dispatch (see Events).

Author-facing patterns in the avatar/ subtree demonstrate the typical shapes. See the pages below or open them directly in the validator.

  • Accessory Toggle — toggle a cosmetic child, preserve state across play sessions, broadcast the choice so remote viewers match.

More patterns will be added as the API surface stabilizes.

  • Permissions — why certain calls throw.
  • Events — what you can hook from an avatar.
  • Networking — reaching other clients without touching world state.
  • CVR Input — input read/write rules by context.