Implement native inventory manager workflows (#62)
All checks were successful
Native code generation / deterministic (push) Successful in 13m14s
Imaging and meshing gate / native (push) Successful in 4m13s
Native Rust workspace compile / compile (push) Successful in 4m19s

This commit is contained in:
2026-08-10 06:00:28 +00:00
parent eb7be428c6
commit 61d6721287
15 changed files with 5564 additions and 576 deletions

View File

@@ -359,9 +359,10 @@ async fn run_seed_requests(weak: Weak<CapsInner>, cancellation: CancellationToke
let Some(simulator) = inner.simulator() else {
return;
};
if !simulator.native_is_connected() {
return;
}
// Seed discovery is HTTP-only. It is valid for composed/fake clients
// to install a simulator and query capabilities before a UDP circuit
// is started, and tying this request to UDP connectivity creates an
// unnecessary race for every capability-backed manager.
let http = simulator.client.native_http_caps_client();
let response = http
.post_with_uri_osd_format_osd_cancellation_token_i_progress(

View File

@@ -118,6 +118,7 @@ struct ClientRuntime {
caps_rate_limiter: Mutex<crate::caps_http::CapsRateLimiter>,
network_manager: Mutex<std::sync::Weak<crate::network_manager::NetworkManagerInner>>,
agent_manager: Mutex<std::sync::Weak<crate::agent_manager::AgentManagerInner>>,
inventory_manager: Mutex<Option<Arc<crate::inventory_manager::InventoryManagerInner>>>,
shutdown_complete: Condvar,
shutdown_wait: Mutex<()>,
}
@@ -146,6 +147,7 @@ impl ClientRuntime {
caps_rate_limiter: Mutex::new(caps_rate_limiter),
network_manager: Mutex::new(std::sync::Weak::new()),
agent_manager: Mutex::new(std::sync::Weak::new()),
inventory_manager: Mutex::new(None),
shutdown_complete: Condvar::new(),
shutdown_wait: Mutex::new(()),
}
@@ -218,6 +220,10 @@ impl ClientRuntime {
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.take();
self.inventory_manager
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.take();
self.http_caps_client
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
@@ -239,6 +245,30 @@ impl ClientRuntime {
}
}
/// A non-owning route back to a live client runtime.
///
/// Manager caches can hold this handle without creating an `Arc` cycle. The
/// settings and time provider are immutable client composition data and are
/// cloned only when a manager needs to perform an operation.
#[derive(Clone)]
pub(crate) struct ClientWeakHandle {
runtime: std::sync::Weak<ClientRuntime>,
settings: Settings,
time_provider: TimeProvider,
}
impl ClientWeakHandle {
pub(crate) fn upgrade(&self) -> Option<GridClient> {
Some(GridClient {
settings: self.settings.clone(),
time_provider: self.time_provider.clone(),
runtime: self.runtime.upgrade()?,
network_manager: None,
agent_manager: None,
})
}
}
/// Native owner for the mapped C# `GridClient` lifecycle slice.
///
/// Construction is side-effect free: it creates no runtime, task, socket, or
@@ -386,6 +416,40 @@ impl GridClient {
Ok(manager)
}
pub(crate) fn native_weak_handle(&self) -> ClientWeakHandle {
ClientWeakHandle {
runtime: Arc::downgrade(&self.runtime),
settings: self.settings.clone(),
time_provider: self.time_provider.clone(),
}
}
pub(crate) fn native_inventory(&self) -> Result<crate::InventoryManager, crate::Error> {
let mut cached = self
.runtime
.inventory_manager
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(inner) = cached.as_ref() {
return Ok(
crate::inventory_manager::InventoryManager::native_from_inner(Arc::clone(inner)),
);
}
let manager =
crate::inventory_manager::InventoryManager::native_new(Some(Arc::new(self.clone())))?;
*cached = Some(manager.native_inner());
Ok(manager)
}
#[allow(clippy::needless_pass_by_value)] // The mapped C# property setter owns its value.
pub(crate) fn native_set_inventory(&mut self, value: crate::InventoryManager) {
*self
.runtime
.inventory_manager
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(value.native_inner());
}
#[allow(clippy::needless_pass_by_value)] // The mapped C# property setter owns its value.
pub(crate) fn native_set_network(&mut self, value: crate::NetworkManager) {
*self

File diff suppressed because it is too large Load Diff

View File

@@ -1462,6 +1462,16 @@ impl Inventory {
.and_then(|value| value.clone_as::<T>()))
}
/// Returns the shared item portion regardless of the concrete inventory
/// subclass stored for the UUID.
pub(crate) fn native_item_value(&self, uuid: UUID) -> Option<InventoryItem> {
read(&self.inner.state)
.items
.get(&uuid)
.and_then(InventoryNode::value)
.and_then(|value| value.item().cloned())
}
pub fn try_get_value_with_uuid_inventory_base(
&self,
uuid: UUID,

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@ mod download_manager;
mod event_queue;
mod gesture;
mod inventory;
mod inventory_manager;
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
mod foliage_catalog;
mod generated;

View File

@@ -1216,6 +1216,10 @@ impl Simulator {
.map(|inner| Caps::native_from_inner(inner, self.native_clone_without_caps()))
}
pub(crate) fn native_id(&self) -> UUID {
self.data.id
}
pub(crate) fn native_from_weak(data: &Weak<SimulatorData>) -> Option<Self> {
data.upgrade().map(|data| Self { caps: None, data })
}