feat: complete stable OpenSim live interactions
Some checks failed
CI / rust-skia (Rust only) (push) Has been cancelled
CI / required (push) Has been cancelled

This commit is contained in:
2026-08-22 00:48:58 +02:00
parent e4e84dcdcb
commit 2f5f03ac6f
11 changed files with 396 additions and 100 deletions

View File

@@ -168,6 +168,7 @@ impl LibremetaverseClientOwner {
.map_err(|_| BackendError::Configuration {
component: "libremetaverse client defaults",
})?;
let _grid = client.grid();
let agent = Arc::new(
libremetaverse::AgentManager::new(Some(Arc::new(client.clone()))).map_err(|_| {
BackendError::Configuration {
@@ -568,13 +569,26 @@ impl crate::behavior::EmbodimentSink for LibremetaverseEmbodimentSink {
.network()
.native_current_sim()
.ok_or(crate::behavior::BehaviorError::NotReady)?;
simulator
let object_position = simulator
.objects_avatars
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.values()
.find(|avatar| avatar.id == avatar_id)
.map(|avatar| native_position(avatar.position))
.map(|avatar| native_position(avatar.position));
let coarse_position = self
.client
.grid()
.native_coarse_position(simulator.handle, avatar_id);
object_position
.or_else(|| {
let origin = self.agent.sim_position();
coarse_position.map(|position| crate::perception::WorldPosition {
x: unwrap_coarse_axis(position.x, origin.x),
y: unwrap_coarse_axis(position.y, origin.y),
z: f64::from(position.z),
})
})
.ok_or(crate::behavior::BehaviorError::TargetUnavailable)
})
}
@@ -642,21 +656,41 @@ impl crate::behavior::EmbodimentSink for LibremetaverseEmbodimentSink {
.network()
.native_current_sim()
.ok_or(crate::behavior::BehaviorError::NotReady)?;
let parcels = self.client.parcels();
let current = parcels
.get_parcel_local_id(simulator.clone(), self.agent.sim_position())
.map_err(|_| crate::behavior::BehaviorError::NativeOperation)?;
let target = parcels
.get_parcel_local_id(
simulator,
libremetaverse_types::Vector3 {
x: point.x as f32,
y: point.y as f32,
z: point.z as f32,
},
)
.map_err(|_| crate::behavior::BehaviorError::NativeOperation)?;
if current == 0 || current != target {
let current_position = self.agent.sim_position();
let reported_bounds_contain_agent = f64::from(current_position.x)
<= f64::from(simulator.size_x)
&& f64::from(current_position.y) <= f64::from(simulator.size_y);
if point.x < 0.0
|| point.y < 0.0
|| (reported_bounds_contain_agent
&& (point.x > f64::from(simulator.size_x)
|| point.y > f64::from(simulator.size_y)))
{
return Err(crate::behavior::BehaviorError::RegionBoundary);
}
let parcels = simulator
.parcels
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let parcel_at = |x: f32, y: f32| {
parcels
.values()
.filter(|parcel| {
x >= parcel.aabb_min.x
&& x <= parcel.aabb_max.x
&& y >= parcel.aabb_min.y
&& y <= parcel.aabb_max.y
})
.map(|parcel| parcel.local_id)
.min()
};
let current = parcel_at(current_position.x, current_position.y);
let target = parcel_at(point.x as f32, point.y as f32);
if current
.zip(target)
.is_some_and(|(current, target)| current != target)
|| (reported_bounds_contain_agent && (current.is_none() || target.is_none()))
{
return Err(crate::behavior::BehaviorError::RegionBoundary);
}
Ok(())
@@ -714,6 +748,20 @@ impl crate::behavior::EmbodimentSink for LibremetaverseEmbodimentSink {
}
}
#[cfg(feature = "live-grid")]
fn unwrap_coarse_axis(encoded: f32, origin: f32) -> f64 {
f64::from(encoded + ((origin - encoded) / 256.0).round() * 256.0)
}
#[cfg(all(test, feature = "live-grid"))]
mod live_grid_tests {
#[test]
fn coarse_avatar_axis_unwraps_to_nearest_varregion_tile() {
assert!((super::unwrap_coarse_axis(164.0, 678.0) - 676.0).abs() < f64::EPSILON);
assert!((super::unwrap_coarse_axis(26.0, 539.0) - 538.0).abs() < f64::EPSILON);
}
}
#[cfg(feature = "live-grid")]
fn native_position(value: libremetaverse_types::Vector3) -> crate::perception::WorldPosition {
crate::perception::WorldPosition {