OpenSimulator Internals/Connector Architecture/Inventory Connector
OpenSimulator Internals/Connector Architecture/Inventory Connector
[edit]Overview
[edit]XInventoryServicesConnector is the simulator-side remote connector for IInventoryService. It translates IInventoryService calls into HTTP POST requests to the ROBUST XInventory handler at /xinventory.
Source file:
OpenSim/Services/Connectors/Inventory/XInventoryServicesConnector.cs
Initialisation
[edit]Config section: [InventoryService] (or configName argument if provided)
Required key:
- InventoryServerURI -- URL of the ROBUST inventory endpoint; throws if missing
The endpoint URL is always InventoryServerURI + "/xinventory" (trailing slash normalized).
Optional key:
- RemoteRequestTimeout -- timeout in seconds for HTTP requests; default -1 (100 seconds, not infinite)
A stats counter (RequestsMade) is registered with StatsManager on initialisation.
Four constructors:
- Default (no args) -- URI must be set externally
- String URI -- used by callers that pass the URI directly; appends /xinventory
- IConfigSource -- calls Initialise(), normal grid mode
- IConfigSource + configName -- used for non-default section names
Transport
[edit]Unlike other connectors, XInventoryServicesConnector does not use SynchronousRestFormsRequester. It has its own MakePostDicRequest() method using HttpClient directly.
MakePostDicRequest() features:
- Uses WebUtil.GetNewGlobalHttpClient() with configured timeout
- Sends Keep-Alive headers (timeout=30, max=10)
- Sends Content-Type: application/x-www-form-urlencoded
- Uses HttpCompletionOption.ResponseHeadersRead -- reads headers first, then streams body
- Body receive has its own CancellationTokenSource timeout via WebUtil.EstimatedReceiveTimeout()
- Logs requests taking longer than WebUtil.LongCallTime
- Logs body timeouts separately from other errors
- Always disposes request, response, and client in finally block
- Returns empty Dictionary on null response (never returns null)
All requests are POST to m_InventoryURL. The METHOD and parameters are encoded as URL query string in the POST body.
MakeRequest() increments RequestsMade counter on every call.
Item Cache
[edit]Static ExpiringCacheOS<UUID, InventoryItemBase> shared across all instances. TTL is 30 seconds.
Cache is checked in GetItem() and GetMultipleItems() before making remote requests. Cache is updated on successful GetItem(), UpdateItem(), and GetMultipleItems(). Cache entries are removed on MoveItems() and DeleteItems().
CheckReturn()
[edit]Helper used by most methods. Returns false if the reply dictionary is null or empty. If RESULT key exists and its value parses as bool, returns that bool. If RESULT key exists but is not parseable as bool, returns false. If RESULT key is absent, returns true (assumes success if the server returned any data).
GetAssetPermissions() cannot use CheckReturn() because its RESULT is an int, not a bool -- it has its own inline parse.
Methods
[edit]| Method | METHOD field | Notes |
|---|---|---|
| CreateUserInventory(principalID) | CREATEUSERINVENTORY | Returns bool |
| GetInventorySkeleton(principalID) | GETINVENTORYSKELETON | Returns List<InventoryFolderBase> from FOLDERS dict |
| GetRootFolder(principalID) | GETROOTFOLDER | Returns single InventoryFolderBase from "folder" key |
| GetFolderForType(principalID, type) | GETFOLDERFORTYPE | TYPE sent as int |
| GetFolderContent(principalID, folderID) | GETFOLDERCONTENT | Returns InventoryCollection with Folders and Items lists |
| GetMultipleFoldersContent(principalID, folderIDs[]) | GETMULTIPLEFOLDERSCONTENT | Batch; results keyed as F_{uuid}; null entry per missing folder |
| GetFolderItems(principalID, folderID) | GETFOLDERITEMS | Returns List<InventoryItemBase> from ITEMS dict |
| AddFolder(folder) | ADDFOLDER | Sends full folder fields as dictionary |
| UpdateFolder(folder) | UPDATEFOLDER | Sends fields as query string; URL-encodes Name |
| MoveFolder(folder) | MOVEFOLDER | Sends ParentID, ID, PRINCIPAL only |
| DeleteFolders(principalID, folderIDs) | DELETEFOLDERS | Sends FOLDERS as List<string> |
| PurgeFolder(folder) | PURGEFOLDER | Sends ID only |
| AddItem(item) | ADDITEM | Sends full item fields; null-coalesces Description, CreatorData, CreatorId |
| UpdateItem(item) | UPDATEITEM | Updates cache on success |
| MoveItems(principalID, items) | MOVEITEMS | Sends parallel IDLIST and DESTLIST; removes from cache |
| DeleteItems(principalID, itemIDs) | DELETEITEMS | Removes from cache |
| GetItem(principalID, itemID) | GETITEM | Cache-first; updates cache on fetch |
| GetMultipleItems(principalID, itemIDs[]) | GETMULTIPLEITEMS | Cache-first per item; only fetches uncached IDs; results keyed as item_N |
| GetFolder(principalID, folderID) | GETFOLDER | Returns single InventoryFolderBase |
| GetActiveGestures(principalID) | GETACTIVEGESTURES | Returns List<InventoryItemBase> from ITEMS dict |
| GetAssetPermissions(principalID, assetID) | GETASSETPERMISSIONS | Returns int from RESULT field; 0 on failure |
| HasInventoryForUser(principalID) | (none) | Always returns false -- not implemented for remote connector |
BuildFolder() / BuildItem()
[edit]Static helpers that construct InventoryFolderBase and InventoryItemBase from reply dictionaries. Both catch exceptions and return an empty object on parse failure rather than throwing. CreatorData is optional in BuildItem() -- fetched via TryGetValue.
Notes
[edit]- This is the only connector in the set that uses HttpClient directly rather than SynchronousRestFormsRequester.
- HasInventoryForUser() always returns false -- not implemented.
- GetMultipleItems() builds the request body with a StringBuilder, skipping items already in cache. The result index tracking (variable i) carries over between the cache-hit loop and the network-result loop -- order of itemArr entries may not match order of itemIDs if some were cached and some were not.
- GetMultipleFoldersContent() returns null entries (not omits) for folders not found in the reply, preserving index alignment with the input folderIDs array.
- UpdateFolder() URL-encodes Name via HttpUtility.UrlEncode() but AddFolder() does not -- inconsistency within the same file.