Jump to content

OpenSimulator Internals/Walkthroughs/Avatar Change Outfit

From Open Simulator Technical Help
Revision as of 13:04, 7 July 2026 by Jwbshaw (talk | contribs) (first)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

OpenSimulator Internals/Walkthroughs/Avatar Change Outfit

[edit]

Overview

[edit]

Traces what happens in code, connectors, and database when an avatar changes outfit -- replacing wearables and rebaking appearance textures.

This covers the standard viewer-initiated outfit change. The sequence differs slightly between V1-style viewers (AvatarIsWearing only) and V2/3 viewers (AvatarIsWearing followed by AgentSetAppearance with baked textures).


1. Viewer Sends AvatarIsWearing

[edit]
OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs -- Client_OnAvatarNowWearing()
  1. Avatar selects new outfit or drags wearable items onto avatar in viewer.
  2. Viewer sends AvatarIsWearing packet listing new wearable ItemIDs by WearableType slot.
  3. AvatarFactoryModule.Client_OnAvatarNowWearing() fires.
  4. Makes a copy of current AvatarAppearance (without textures).
  5. Updates Wearables array on the copy: sets ItemID for each type, AssetID = UUID.Zero initially.
  6. Calls avatAppearance.GetAssetsFrom(sp.Appearance) -- fills in known AssetIDs from current appearance where ItemID matches.
  7. Under m_setAppearanceLock: writes sp.Appearance.Wearables only. Does NOT overwrite baked textures or visual params.
  8. Calls QueueAppearanceSave() -- schedules async save after m_savetime (default 5s).

Note: No appearance send at this step. The viewer will bake and send new textures next.

No DB writes yet (save is queued, not executed).

2. Viewer Bakes New Textures Locally

[edit]

This step happens entirely on the viewer side. No server involvement.

  • Viewer composites the new wearable assets (clothing layers, skin, etc.) into baked texture images for each BakeType (Head, UpperBody, LowerBody, Eyes, Skirt, Hair, and PBR variants).
  • Viewer uploads baked texture assets to the region's asset upload cap.
  • Assets arrive at AssetService and are stored (temporary=true, local=true initially).
DB writes (via AssetService)
  • assets -- INSERT: each new baked texture asset (temporary, local)

3. Viewer Sends AgentSetAppearance

[edit]
OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs -- Client_OnSetAppearance() → SetAppearance()
  1. Viewer sends AgentSetAppearance packet containing:
    • New TextureEntry -- baked texture UUIDs for each face index.
    • New VisualParams -- slider values (height, body shape, etc.).
    • WearableCacheItems -- CacheId/TextureID pairs for each bake index.
    • AvatarSize vector.
  2. AvatarFactoryModule.SetAppearance() called under m_setAppearanceLock.
  3. Calls sp.Appearance.SetVisualParams() -- updates slider bytes, recalculates avatar height if changed.
  4. Calls sp.Appearance.SetTextureEntries() -- updates baked texture UUIDs in appearance.
  5. Calls UpdateBakedTextureCache():
    • Checks local IAssetCache for each baked texture asset.
    • Updates WearableCacheItems with confirmed TextureID and CacheId.
    • If any textures missing from cache: sends SendRebakeAvatarTextures() -- viewer must re-upload. Throttled to one request per texture per 30 seconds.
    • If full set valid and changed: stores to IBakedTextureModule (bake server cache).
  6. Calls QueueAppearanceSave() (appearance changed).
  7. Calls QueueAppearanceSend().
No immediate DB writes -- both saves are queued.

4. Appearance Sent to Other Clients (Deferred)

[edit]
AvatarFactoryModule.HandleAppearanceUpdateTimer() → SendAppearance()

After m_sendtime (default 2s), timer drains m_sendqueue:

  1. Calls sp.SendAppearanceToAllOtherAgents():
    • Iterates all root ScenePresences.
    • Calls ControllingClient.SendAppearance() for each -- sends AvatarAppearance packet with new VisualParams and baked texture bytes.
    • Respects parcel visibility (ParcelHideThisAvatar).
  2. Calls Animator.SendAnimPack() -- sends current animation state.

Other clients now render the avatar with the new appearance.


5. Appearance Saved to AvatarService (Deferred)

[edit]
AvatarFactoryModule.HandleAppearanceUpdateTimer() → SaveAppearance()

After m_savetime (default 5s), timer drains m_savequeue. Runs on thread pool.

  1. Calls SetAppearanceAssets():
    • For each wearable slot: calls InventoryService.GetItem(userID, itemID) -- resolves ItemID to AssetID.
    • If item not found: removes wearable from appearance, logs warning.
    • Ignores Ruth default wearables.
  2. Calls m_scene.AvatarService.SetAppearance(agentID, sp.Appearance):
    • Connector: HTTP PUT to ROBUST AvatarService.
    • ROBUST writes avatarappearance table (wearable list, texture entry, visual params) and avatarattachments table.
DB writes
  • inventoryitems -- SELECT: resolve each wearable ItemID to AssetID
  • avatarappearance -- UPDATE: new wearable list, texture entry, visual params
  • avatarattachments -- UPDATE: current attachment list

6. Attachments Changed (if outfit includes attachment changes)

[edit]
OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs

If the outfit change includes adding or removing attachments, the viewer sends separate RezSingleAttachmentFromInv / DetachAttachmentIntoInv packets independently of the appearance packets above.

Adding an attachment:

  1. Client_OnRezSingleAttachmentFromInv() fires.
  2. RezSingleAttachmentFromInventory() checks not already worn.
  3. RezSingleAttachmentFromInventoryInternal() calls IInventoryAccessModule.RezObject() -- fetches asset, deserialises SOG, adds to scene.
  4. AttachObjectInternal() attaches to avatar, displaces any existing attachment at same point (if WearReplacesAll=true).
  5. UpdateUserInventoryWithAttachment() updates inventory item to reflect attachment point.
  6. ShowAttachInUserInventory() calls sp.Appearance.SetAttachment() and QueueAppearanceSave().
  7. Scripts created and started (CreateScriptInstances, ResumeScripts).
  8. TriggerOnAttach() fires.

Removing an attachment:

  1. Client_OnDetachAttachmentIntoInv() fires.
  2. DetachSingleAttachmentToInv() called -- see OpenSimulator Internals/Code Map/AttachmentsModule.
  3. Script state snapshotted, object removed from scene, asset and inventory item updated.
DB writes (per attachment change)
  • assets -- INSERT: updated attachment object asset (serialised XML)
  • inventoryitems -- UPDATE: new assetID, attach point

Summary: DB Tables Touched

[edit]
Table Operation Step
assets INSERT Viewer uploads baked textures (step 2)
inventoryitems SELECT SetAppearanceAssets() resolves wearable AssetIDs (step 5)
avatarappearance UPDATE AvatarService.SetAppearance() (step 5)
avatarattachments UPDATE AvatarService.SetAppearance() (step 5)
assets INSERT Attachment object re-saved (step 6, if attachments changed)
inventoryitems UPDATE Attachment inventory item updated (step 6, if attachments changed)

Timing Notes

[edit]
  • Appearance send is deferred 2s (m_sendtime) -- prevents flooding if multiple rapid changes.
  • Appearance save is deferred 5s (m_savetime) -- ensures all wearable asset uploads complete before inventory lookup.
  • Both delays are configurable in [Appearance] section of OpenSim.ini.
  • The AvatarIsWearing and AgentSetAppearance packets often arrive nearly simultaneously. m_setAppearanceLock ensures AvatarIsWearing does not overwrite SetAppearance texture changes.

See Also

[edit]