OpenSimulator Internals/Walkthroughs/Avatar Change Outfit
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()
- Avatar selects new outfit or drags wearable items onto avatar in viewer.
- Viewer sends AvatarIsWearing packet listing new wearable ItemIDs by WearableType slot.
- AvatarFactoryModule.Client_OnAvatarNowWearing() fires.
- Makes a copy of current AvatarAppearance (without textures).
- Updates Wearables array on the copy: sets ItemID for each type, AssetID = UUID.Zero initially.
- Calls avatAppearance.GetAssetsFrom(sp.Appearance) -- fills in known AssetIDs from current appearance where ItemID matches.
- Under m_setAppearanceLock: writes sp.Appearance.Wearables only. Does NOT overwrite baked textures or visual params.
- 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()
- 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.
- AvatarFactoryModule.SetAppearance() called under m_setAppearanceLock.
- Calls sp.Appearance.SetVisualParams() -- updates slider bytes, recalculates avatar height if changed.
- Calls sp.Appearance.SetTextureEntries() -- updates baked texture UUIDs in appearance.
- 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).
- Calls QueueAppearanceSave() (appearance changed).
- 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:
- 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).
- 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.
- 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.
- 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:
- Client_OnRezSingleAttachmentFromInv() fires.
- RezSingleAttachmentFromInventory() checks not already worn.
- RezSingleAttachmentFromInventoryInternal() calls IInventoryAccessModule.RezObject() -- fetches asset, deserialises SOG, adds to scene.
- AttachObjectInternal() attaches to avatar, displaces any existing attachment at same point (if WearReplacesAll=true).
- UpdateUserInventoryWithAttachment() updates inventory item to reflect attachment point.
- ShowAttachInUserInventory() calls sp.Appearance.SetAttachment() and QueueAppearanceSave().
- Scripts created and started (CreateScriptInstances, ResumeScripts).
- TriggerOnAttach() fires.
Removing an attachment:
- Client_OnDetachAttachmentIntoInv() fires.
- DetachSingleAttachmentToInv() called -- see OpenSimulator Internals/Code Map/AttachmentsModule.
- 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]- OpenSimulator Internals/Walkthroughs
- OpenSimulator Internals/Code Map/AvatarFactoryModule
- OpenSimulator Internals/Code Map/AttachmentsModule
- OpenSimulator Internals/Code Map/ScenePresence
- OpenSimulator Internals/Code Map/ROBUST/AvatarService
- OpenSimulator Internals/Walkthroughs/Avatar Rez In Region
- OpenSimulator Internals/Walkthroughs/Avatar Logs Out