Files
MetaCrate/docs/inventory.md
Chili Palmer d5c318d280
All checks were successful
Native code generation / deterministic (push) Successful in 13m18s
Imaging and meshing gate / native (push) Successful in 4m12s
Native Rust workspace compile / compile (push) Successful in 4m12s
Implement native AIS inventory reconciliation (#63)
2026-08-10 06:41:29 +00:00

5.8 KiB

Inventory models, hierarchy, and cache

The native inventory layer mirrors the value and hierarchy behavior of LibreMetaverse's InventoryBase, InventoryItem, InventoryFolder, InventoryNode, and Inventory types without depending on a network transport. Concrete item subclasses remain concrete when inserted, queried, notified, or restored from disk; link detection is based on AssetType, not on the Rust wrapper type. This preserves the C# misclassified-link behavior.

Store mutation is serialized by one inventory state lock. Parent/child and link indexes and recursive item counts are rebuilt as one transaction, with cycle and depth bounds. Missing parents become placeholder folders until their real folder record arrives. Self-loops and longer parent cycles remain queryable but are not linked into a cyclic ownership graph. Removing a folder removes its bounded descendant subtree. Root and library roots are tracked independently, and direct system folders can be found by FolderType. Sorting supports the ByDate, FoldersByName, and SystemFoldersToTop flags.

Added, updated, and removed notifications clone their arguments and subscriber list before invoking observers. No inventory, node, or subscriber lock is held during a callback, so observers may safely query or mutate the store. A panic in one observer is isolated by the shared event registry.

Cache files start with INVCACHE, a little-endian format version, owner and root identities, and tagged concrete records. Reads are limited to 64 MiB, one million records, one MiB per string, and a 512-level hierarchy. Invalid magic, versions, UTF-8, enums, duplicate IDs, zero IDs, truncated records, and trailing data are rejected before the live store is replaced. Synchronous restore returns -1 for compatibility; asynchronous restore returns the typed error. Saves snapshot under the state lock, release it before I/O, write and sync a same-directory temporary file, then replace the destination. The replacement includes a rollback path on platforms that cannot rename over an existing file.

The focused compatibility suites are inventory_store_semantics and misclassified_link_semantics; native unit tests additionally cover concrete cache round trips, permissions, corruption, unknown versions, system-folder sorting, and callback re-entry.

UDP inventory manager

InventoryManager now owns the live inventory protocol boundary. Login installs the agent and library skeletons in the transport-free store, while validated UpdateCreateInventoryItem, BulkUpdateInventory, FetchInventoryReply, and InventoryDescendents replies populate or reconcile it. Create and copy callback IDs, item fetches, task replies, and inventory offers are correlated independently; cancelling one waiter removes only that waiter. A cleanup worker bounds abandoned requests to one minute, and the manager rejects more than 4,096 pending operations.

Outbound create, update, move, copy, remove, give, rez/derez, script-state, and task-inventory operations build the generated protocol packet types. Generated multi-block packets use the packet codec's encode_multiple path, so large batches are split according to the UDP transport bounds while retaining the same agent, session, transaction, callback, owner, and permission data. Capability-backed inventory fetch and folder-content responses are parsed before becoming visible in the store. Asset, material, script, notecard, and thumbnail upload bodies remain a separate asset-pipeline concern.

Legacy task inventory uses a correlated ReplyTaskInventory filename followed by a RequestXfer transfer. The implementation confirms packets, handles duplicate or out-of-order packet numbers deterministically, aborts cancelled transfers, and rejects announced or accumulated data above 16 MiB. Task text parsing is capped at 100,000 entries and accepts both plain asset IDs and the reference shadow-ID XOR encoding. The capability path applies the same byte and entry limits.

No store lock is held across an await, a packet send, or an observer callback. Reply data is decoded and validated before store mutation, and event registries snapshot subscribers before invoking them. Focused coverage lives in inventory_manager_semantics, task_inventory_semantics, and the native inventory_manager::tests fake-packet cases for callback isolation, bulk-copy completion, stale folder versions, offers, capability events, and bounded Xfer.

Inventory API v3

InventoryAISClient implements the InventoryAPIv3 and LibraryAPIv3 capability resources for categories, children, links, individual items, the current outfit folder, orphans, and trash. Requests preserve the AIS verbs and resource shapes, including COPY with its Destination header, transaction IDs, depth/subset queries, and LLSD XML request content type. The client uses the injected capability HTTP transport, so tests and applications can record requests deterministically without replacing inventory logic.

Successful response bodies are fully decoded and validated before any local state changes. Embedded folders, concrete items, and links are parsed together; object or mesh links with the legacy texture inventory type are corrected to attachments. Side-effect removals and category version updates are combined with returned objects and installed atomically through a detached candidate inventory graph. Malformed, oversized, cancelled, or timed-out responses leave the live store unchanged, and no store lock crosses an await or callback.

AIS remains capability-optional. Missing capabilities and HTTP failures return the fixed false/empty compatibility results, while cancellation is propagated. The existing InventoryManager UDP fallback remains the contract-correct path for operations whose capability is absent; AIS reconciliation shares that manager's transport-independent store and observer ordering.