Jump to content

OpenSimulator Internals/Connector Architecture/Inventory Connector

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

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.

See Also

[edit]