<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://osimdev.org/wiki/index.php?action=history&amp;feed=atom&amp;title=OpenSimulator_Internals%2FCode_Map%2FROBUST%2FAvatarService</id>
	<title>OpenSimulator Internals/Code Map/ROBUST/AvatarService - Revision history</title>
	<link rel="self" type="application/atom+xml" href="http://osimdev.org/wiki/index.php?action=history&amp;feed=atom&amp;title=OpenSimulator_Internals%2FCode_Map%2FROBUST%2FAvatarService"/>
	<link rel="alternate" type="text/html" href="http://osimdev.org/wiki/index.php?title=OpenSimulator_Internals/Code_Map/ROBUST/AvatarService&amp;action=history"/>
	<updated>2026-08-04T15:31:59Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>http://osimdev.org/wiki/index.php?title=OpenSimulator_Internals/Code_Map/ROBUST/AvatarService&amp;diff=62&amp;oldid=prev</id>
		<title>Jwbshaw: first</title>
		<link rel="alternate" type="text/html" href="http://osimdev.org/wiki/index.php?title=OpenSimulator_Internals/Code_Map/ROBUST/AvatarService&amp;diff=62&amp;oldid=prev"/>
		<updated>2026-07-07T12:00:45Z</updated>

		<summary type="html">&lt;p&gt;first&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;= OpenSimulator Internals/Code Map/ROBUST/AvatarService =&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
AvatarService stores and retrieves avatar appearance data. It is a thin layer over a key-value database table -- all appearance state is stored as named string pairs keyed by PrincipalID.&lt;br /&gt;
&lt;br /&gt;
Source files:&lt;br /&gt;
&lt;br /&gt;
 OpenSim/Services/AvatarService/AvatarService.cs&lt;br /&gt;
 OpenSim/Services/AvatarService/AvatarServiceBase.cs&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Constructor and Config Loading ===&lt;br /&gt;
&lt;br /&gt;
 AvatarServiceBase(IConfigSource config)&lt;br /&gt;
&lt;br /&gt;
Config is read in two layers; [AvatarService] overrides [DatabaseService]:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Section !! Key !! Default !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| [DatabaseService] || StorageProvider || (none) || DLL name -- fallback&lt;br /&gt;
|-&lt;br /&gt;
| [DatabaseService] || ConnectionString || (none) || DB connection string -- fallback&lt;br /&gt;
|-&lt;br /&gt;
| [AvatarService] || StorageProvider || (inherited) || Overrides [DatabaseService]&lt;br /&gt;
|-&lt;br /&gt;
| [AvatarService] || ConnectionString || (inherited) || Overrides [DatabaseService]&lt;br /&gt;
|-&lt;br /&gt;
| [AvatarService] || Realm || Avatars || Table name&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Throws if StorageProvider is empty or if the plugin cannot be loaded.&lt;br /&gt;
&lt;br /&gt;
The loaded plugin is stored as m_Database (IAvatarData).&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Data Model ===&lt;br /&gt;
&lt;br /&gt;
Avatar data is stored as a flat key-value table. Each row has:&lt;br /&gt;
&lt;br /&gt;
* PrincipalID -- avatar UUID&lt;br /&gt;
* Name -- attribute name (e.g. &amp;quot;AvatarType&amp;quot;, &amp;quot;AvatarHeight&amp;quot;, attachment slot names)&lt;br /&gt;
* Value -- string value&lt;br /&gt;
&lt;br /&gt;
AvatarType is stored as a separate named row, not in the Data dictionary. Value 1 = SL-style avatar.&lt;br /&gt;
&lt;br /&gt;
Attachment slots are stored with names prefixed by underscore (&amp;quot;_&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
AvatarData.ToAvatarAppearance() and AvatarData(AvatarAppearance) handle conversion between the flat key-value representation and the structured AvatarAppearance object. Those conversions are in OpenSim/Framework/, not in this service.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== GetAvatar ===&lt;br /&gt;
&lt;br /&gt;
 public AvatarData GetAvatar(UUID principalID)&lt;br /&gt;
&lt;br /&gt;
# Queries m_Database.Get(&amp;quot;PrincipalID&amp;quot;, principalID.ToString()) -- returns all rows for this avatar&lt;br /&gt;
# If no rows found: returns AvatarData with AvatarType = 1 (SL avatar), empty Data dictionary&lt;br /&gt;
# Iterates rows:&lt;br /&gt;
#* Row with Name == &amp;quot;AvatarType&amp;quot; sets ret.AvatarType&lt;br /&gt;
#* All other rows go into ret.Data[Name] = Value&lt;br /&gt;
# Returns populated AvatarData&lt;br /&gt;
&lt;br /&gt;
GetAppearance() calls GetAvatar() then calls avatar.ToAvatarAppearance() on the result.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== SetAvatar ===&lt;br /&gt;
&lt;br /&gt;
 public bool SetAvatar(UUID principalID, AvatarData avatar)&lt;br /&gt;
&lt;br /&gt;
Write is delete-then-reinsert -- no partial update:&lt;br /&gt;
&lt;br /&gt;
# Deletes all existing rows for principalID&lt;br /&gt;
# Stores the AvatarType row first&lt;br /&gt;
# Iterates avatar.Data:&lt;br /&gt;
#* Special case: AvatarHeight -- validates as float in range [0, 10]&lt;br /&gt;
#** Attempts locale-insensitive parse (replaces comma with period)&lt;br /&gt;
#** If unparseable or out of range: silently substitutes 1.771488f&lt;br /&gt;
#** Comment in source notes this was a hack added 2011-07-30 to cope with bad values injected by buggy simulators on OSGrid. Comment suggests removal after six months -- it is still present.&lt;br /&gt;
#* All other keys stored as-is&lt;br /&gt;
#* On any Store() failure: deletes all rows for principalID and returns false&lt;br /&gt;
# Returns true if all rows stored successfully&lt;br /&gt;
&lt;br /&gt;
SetAppearance() constructs AvatarData from an AvatarAppearance object, then calls SetAvatar().&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== ResetAvatar ===&lt;br /&gt;
&lt;br /&gt;
 public bool ResetAvatar(UUID principalID)&lt;br /&gt;
&lt;br /&gt;
Deletes all rows for principalID. Returns the result of m_Database.Delete().&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== SetItems ===&lt;br /&gt;
&lt;br /&gt;
 public bool SetItems(UUID principalID, string[] names, string[] values)&lt;br /&gt;
&lt;br /&gt;
Stores a parallel array of name/value pairs for principalID. Returns false if arrays are different lengths. Stores each pair individually; stops and returns false on first Store() failure. Does not delete existing rows first -- this is a partial update, unlike SetAvatar().&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== RemoveItems ===&lt;br /&gt;
&lt;br /&gt;
 public bool RemoveItems(UUID principalID, string[] names)&lt;br /&gt;
&lt;br /&gt;
Deletes individual named rows for principalID. Iterates names and calls m_Database.Delete(principalID, name) for each. Always returns true regardless of individual delete results.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Notes ===&lt;br /&gt;
&lt;br /&gt;
* The AvatarHeight sanitization hack (justified 2011-07-30) is still present and active. The original comment estimated it could be removed after six months.&lt;br /&gt;
* SetAvatar() and SetItems() have different write semantics: SetAvatar() is a full replace (delete-all, reinsert); SetItems() is upsert (no prior delete).&lt;br /&gt;
* RemoveItems() always returns true -- individual delete failures are silently ignored.&lt;br /&gt;
* There are no console commands registered by this service.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[OpenSimulator Internals/Code Map/ROBUST]]&lt;br /&gt;
* [[OpenSimulator Internals/Code Map/Shared]]&lt;br /&gt;
* [[OpenSimulator Internals/Databases]]&lt;/div&gt;</summary>
		<author><name>Jwbshaw</name></author>
	</entry>
</feed>