From 13f85bfa349bae5f592bfd43cd0e71cc07b836c8 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Mon, 10 Aug 2026 14:32:46 +0000 Subject: [PATCH] Implement object manager vertical slice (#69) --- api/SHIM-COVERAGE.md | 2 +- crates/libremetaverse/src/client_core.rs | 55 + crates/libremetaverse/src/generated.rs | 996 ++---- crates/libremetaverse/src/lib.rs | 1 + crates/libremetaverse/src/object_manager.rs | 3019 ++++++++++++++++++ crates/libremetaverse/src/object_material.rs | 383 ++- crates/libremetaverse/src/object_model.rs | 8 +- tools/generate_api_shims.py | 179 +- tools/generate_rust_mapping.py | 1 + 9 files changed, 3954 insertions(+), 690 deletions(-) create mode 100644 crates/libremetaverse/src/object_manager.rs diff --git a/api/SHIM-COVERAGE.md b/api/SHIM-COVERAGE.md index 3bbd16b..ac220da 100644 --- a/api/SHIM-COVERAGE.md +++ b/api/SHIM-COVERAGE.md @@ -4,7 +4,7 @@ Generated by `python3 tools/generate_api_shims.py`; do not edit by hand. | Assembly | Types | Members | Status | |---|---:|---:|---| -| `LibreMetaverse` | 2,711 | 27,281 | native implementation: 237 types / 15,757 members; remaining surface is callable failure-only shims | +| `LibreMetaverse` | 2,711 | 27,281 | native implementation: 253 types / 15,906 members; remaining surface is callable failure-only shims | | `LibreMetaverse.Imaging.Abstractions` | 3 | 20 | native implementation: 3 types / 20 members; no generated shims remain | | `LibreMetaverse.Imaging.Skia` | 1 | 3 | native implementation: 1 type / 3 members; no generated shims remain | | `LibreMetaverse.LslTools` | 164 | 768 | callable failure-only shim | diff --git a/crates/libremetaverse/src/client_core.rs b/crates/libremetaverse/src/client_core.rs index ea953f0..2b7442a 100644 --- a/crates/libremetaverse/src/client_core.rs +++ b/crates/libremetaverse/src/client_core.rs @@ -118,6 +118,7 @@ struct ClientRuntime { caps_rate_limiter: Mutex, network_manager: Mutex>, agent_manager: Mutex>, + object_manager: Mutex>>, appearance_manager: Mutex>>, avatar_manager: Mutex>>, animesh_manager: Mutex>>, @@ -152,6 +153,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()), + object_manager: Mutex::new(None), appearance_manager: Mutex::new(None), avatar_manager: Mutex::new(None), animesh_manager: Mutex::new(None), @@ -238,6 +240,10 @@ impl ClientRuntime { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) .take(); + self.object_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .take(); self.http_caps_client .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) @@ -629,6 +635,55 @@ impl GridClient { .unwrap_or_else(std::sync::PoisonError::into_inner) = Arc::downgrade(inner); } + pub(crate) fn cached_object_manager_inner( + &self, + ) -> Option> { + self.runtime + .object_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() + } + + pub(crate) fn cache_object_manager_inner( + &self, + inner: &Arc, + ) { + *self + .runtime + .object_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(Arc::clone(inner)); + } + + pub(crate) fn install_object_manager_inner( + &self, + candidate: Arc, + ) -> Arc { + let mut cached = self + .runtime + .object_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(inner) = cached.as_ref() { + return Arc::clone(inner); + } + *cached = Some(Arc::clone(&candidate)); + candidate + } + + pub(crate) fn native_objects(&self) -> Result { + if let Some(inner) = self.cached_object_manager_inner() { + return Ok(crate::ObjectManager { inner }); + } + crate::object_manager::ObjectManagerInner::new(Some(Arc::new(self.clone()))) + } + + #[allow(clippy::needless_pass_by_value)] + pub(crate) fn native_set_objects(&self, value: crate::ObjectManager) { + self.cache_object_manager_inner(&value.inner); + } + pub(crate) fn native_set_caps_rate_limiter( &mut self, value: crate::caps_http::CapsRateLimiter, diff --git a/crates/libremetaverse/src/generated.rs b/crates/libremetaverse/src/generated.rs index 673cc3b..d6776f3 100644 --- a/crates/libremetaverse/src/generated.rs +++ b/crates/libremetaverse/src/generated.rs @@ -3640,6 +3640,7 @@ pub use crate::attention_catalog::AttentionData; pub use crate::attention_catalog::AttentionSet; /// C# type: `T:LibreMetaverse.Avatar`. +#[derive(Clone)] pub struct Avatar { /// C# member: `F:LibreMetaverse.Avatar.Animations`. pub animations: Vec, @@ -3710,6 +3711,7 @@ impl Avatar { } /// C# type: `T:LibreMetaverse.Avatar.Attachment`. +#[derive(Clone)] pub struct AvatarAttachment { /// C# member: `F:LibreMetaverse.Avatar.Attachment.AttachmentID`. pub attachment_id: libremetaverse_types::UUID, @@ -3736,6 +3738,7 @@ impl AvatarAttachment { } /// C# type: `T:LibreMetaverse.Avatar.AvatarProperties`. +#[derive(Clone)] pub struct AvatarAvatarProperties { /// C# member: `F:LibreMetaverse.Avatar.AvatarProperties.AboutText`. pub about_text: String, @@ -3830,6 +3833,7 @@ impl AvatarAvatarProperties { } /// C# type: `T:LibreMetaverse.Avatar.Interests`. +#[derive(Clone)] pub struct AvatarInterests { /// C# member: `F:LibreMetaverse.Avatar.Interests.LanguagesText`. pub languages_text: String, @@ -3856,6 +3860,7 @@ impl AvatarInterests { } /// C# type: `T:LibreMetaverse.Avatar.Statistics`. +#[derive(Clone)] pub struct AvatarStatistics { /// C# member: `F:LibreMetaverse.Avatar.Statistics.AppearanceNegative`. pub appearance_negative: i32, @@ -4463,44 +4468,12 @@ impl AvatarPropertiesReplyEventArgs { pub use crate::agent_messages::AvatarRenderInfoEventArgs; /// C# type: `T:LibreMetaverse.AvatarSitChangedEventArgs`. -pub struct AvatarSitChangedEventArgs; -impl AvatarSitChangedEventArgs { - /// C# member: `M:LibreMetaverse.AvatarSitChangedEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Avatar,System.UInt32,System.UInt32)`. - pub fn new( - simulator: libremetaverse::Simulator, - avatar: libremetaverse::Avatar, - sitting_on: u32, - old_seat: u32, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.AvatarSitChangedEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Avatar,System.UInt32,System.UInt32)", - ) - } - /// C# member: `P:LibreMetaverse.AvatarSitChangedEventArgs.Avatar`. - pub fn avatar(&self) -> libremetaverse::Avatar { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AvatarSitChangedEventArgs.Avatar" - ) - } - /// C# member: `P:LibreMetaverse.AvatarSitChangedEventArgs.OldSeat`. - pub fn old_seat(&self) -> u32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AvatarSitChangedEventArgs.OldSeat" - ) - } - /// C# member: `P:LibreMetaverse.AvatarSitChangedEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AvatarSitChangedEventArgs.Simulator" - ) - } - /// C# member: `P:LibreMetaverse.AvatarSitChangedEventArgs.SittingOn`. - pub fn sitting_on(&self) -> u32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AvatarSitChangedEventArgs.SittingOn" - ) - } -} +/// C# member: `M:LibreMetaverse.AvatarSitChangedEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Avatar,System.UInt32,System.UInt32)`. +/// C# member: `P:LibreMetaverse.AvatarSitChangedEventArgs.Avatar`. +/// C# member: `P:LibreMetaverse.AvatarSitChangedEventArgs.OldSeat`. +/// C# member: `P:LibreMetaverse.AvatarSitChangedEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.AvatarSitChangedEventArgs.SittingOn`. +pub use crate::object_manager::AvatarSitChangedEventArgs; /// C# type: `T:LibreMetaverse.AvatarSitResponseEventArgs`. /// C# member: `M:LibreMetaverse.AvatarSitResponseEventArgs.#ctor(LibreMetaverse.UUID,System.Boolean,LibreMetaverse.Vector3,LibreMetaverse.Vector3,System.Boolean,LibreMetaverse.Vector3,LibreMetaverse.Quaternion)`. @@ -4614,38 +4587,12 @@ pub enum AvatarTextureIndex { } /// C# type: `T:LibreMetaverse.AvatarUpdateEventArgs`. -pub struct AvatarUpdateEventArgs; -impl AvatarUpdateEventArgs { - /// C# member: `M:LibreMetaverse.AvatarUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Avatar,System.UInt16,System.Boolean)`. - pub fn new( - simulator: libremetaverse::Simulator, - avatar: libremetaverse::Avatar, - time_dilation: u16, - is_new: bool, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.AvatarUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Avatar,System.UInt16,System.Boolean)", - ) - } - /// C# member: `P:LibreMetaverse.AvatarUpdateEventArgs.Avatar`. - pub fn avatar(&self) -> libremetaverse::Avatar { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AvatarUpdateEventArgs.Avatar") - } - /// C# member: `P:LibreMetaverse.AvatarUpdateEventArgs.IsNew`. - pub fn is_new(&self) -> bool { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AvatarUpdateEventArgs.IsNew") - } - /// C# member: `P:LibreMetaverse.AvatarUpdateEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AvatarUpdateEventArgs.Simulator") - } - /// C# member: `P:LibreMetaverse.AvatarUpdateEventArgs.TimeDilation`. - pub fn time_dilation(&self) -> u16 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AvatarUpdateEventArgs.TimeDilation" - ) - } -} +/// C# member: `M:LibreMetaverse.AvatarUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Avatar,System.UInt16,System.Boolean)`. +/// C# member: `P:LibreMetaverse.AvatarUpdateEventArgs.Avatar`. +/// C# member: `P:LibreMetaverse.AvatarUpdateEventArgs.IsNew`. +/// C# member: `P:LibreMetaverse.AvatarUpdateEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.AvatarUpdateEventArgs.TimeDilation`. +pub use crate::object_manager::AvatarUpdateEventArgs; /// C# type: `T:LibreMetaverse.BakeType`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -8737,15 +8684,13 @@ impl GridClient { /// C# member: `P:LibreMetaverse.GridClient.Objects`. pub fn objects(&self) -> libremetaverse::ObjectManager { /* native client-core implementation */ - crate::object_material::ObjectManager::native_new(Some(std::sync::Arc::new(self.clone()))) + self.native_objects() .unwrap_or_else(|_| panic!("failed to construct ObjectManager")) } /// Setter for C# member: `P:LibreMetaverse.GridClient.Objects`. pub fn set_objects(&mut self, value: libremetaverse::ObjectManager) { /* native client-core implementation */ - { - let _ = value; - } + self.native_set_objects(value) } /// C# member: `P:LibreMetaverse.GridClient.Parcels`. pub fn parcels(&self) -> libremetaverse::ParcelManager { @@ -13087,49 +13032,16 @@ pub use crate::inventory::InventoryWearable; pub use crate::inventory_manager::ItemReceivedEventArgs; /// C# type: `T:LibreMetaverse.KillObjectEventArgs`. -pub struct KillObjectEventArgs; -impl KillObjectEventArgs { - /// C# member: `M:LibreMetaverse.KillObjectEventArgs.#ctor(LibreMetaverse.Simulator,System.UInt32)`. - pub fn new(simulator: libremetaverse::Simulator, object_id: u32) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.KillObjectEventArgs.#ctor(LibreMetaverse.Simulator,System.UInt32)", - ) - } - /// C# member: `P:LibreMetaverse.KillObjectEventArgs.ObjectLocalID`. - pub fn object_local_id(&self) -> u32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.KillObjectEventArgs.ObjectLocalID" - ) - } - /// C# member: `P:LibreMetaverse.KillObjectEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.KillObjectEventArgs.Simulator") - } -} +/// C# member: `M:LibreMetaverse.KillObjectEventArgs.#ctor(LibreMetaverse.Simulator,System.UInt32)`. +/// C# member: `P:LibreMetaverse.KillObjectEventArgs.ObjectLocalID`. +/// C# member: `P:LibreMetaverse.KillObjectEventArgs.Simulator`. +pub use crate::object_manager::KillObjectEventArgs; /// C# type: `T:LibreMetaverse.KillObjectsEventArgs`. -pub struct KillObjectsEventArgs; -impl KillObjectsEventArgs { - /// C# member: `M:LibreMetaverse.KillObjectsEventArgs.#ctor(LibreMetaverse.Simulator,System.UInt32[])`. - pub fn new( - simulator: libremetaverse::Simulator, - object_i_ds: Vec, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.KillObjectsEventArgs.#ctor(LibreMetaverse.Simulator,System.UInt32[])", - ) - } - /// C# member: `P:LibreMetaverse.KillObjectsEventArgs.ObjectLocalIDs`. - pub fn object_local_i_ds(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.KillObjectsEventArgs.ObjectLocalIDs" - ) - } - /// C# member: `P:LibreMetaverse.KillObjectsEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.KillObjectsEventArgs.Simulator") - } -} +/// C# member: `M:LibreMetaverse.KillObjectsEventArgs.#ctor(LibreMetaverse.Simulator,System.UInt32[])`. +/// C# member: `P:LibreMetaverse.KillObjectsEventArgs.ObjectLocalIDs`. +/// C# member: `P:LibreMetaverse.KillObjectsEventArgs.Simulator`. +pub use crate::object_manager::KillObjectsEventArgs; /// C# type: `T:LibreMetaverse.LandPatchReceivedEventArgs`. pub struct LandPatchReceivedEventArgs; @@ -14500,6 +14412,7 @@ pub enum MuteType { } /// C# type: `T:LibreMetaverse.NameValue`. +#[derive(Clone, Debug)] pub struct NameValue { /// C# member: `F:LibreMetaverse.NameValue.Class`. pub class_: libremetaverse::NameValueClassType, @@ -15333,91 +15246,21 @@ pub use crate::login::NetworkManagerLoginResponseCallback; pub use crate::network_manager::NetworkManagerOutgoingPacket; /// C# type: `T:LibreMetaverse.ObjectAnimationEventArgs`. -pub struct ObjectAnimationEventArgs; -impl ObjectAnimationEventArgs { - /// C# member: `M:LibreMetaverse.ObjectAnimationEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Collections.Generic.List{LibreMetaverse.Animation})`. - pub fn new( - simulator: libremetaverse::Simulator, - object_id: libremetaverse_types::UUID, - anims: Vec, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectAnimationEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Collections.Generic.List{LibreMetaverse.Animation})", - ) - } - /// C# member: `P:LibreMetaverse.ObjectAnimationEventArgs.Animations`. - pub fn animations(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectAnimationEventArgs.Animations" - ) - } - /// C# member: `P:LibreMetaverse.ObjectAnimationEventArgs.ObjectID`. - pub fn object_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectAnimationEventArgs.ObjectID" - ) - } - /// C# member: `P:LibreMetaverse.ObjectAnimationEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectAnimationEventArgs.Simulator" - ) - } -} +/// C# member: `M:LibreMetaverse.ObjectAnimationEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Collections.Generic.List{LibreMetaverse.Animation})`. +/// C# member: `P:LibreMetaverse.ObjectAnimationEventArgs.Animations`. +/// C# member: `P:LibreMetaverse.ObjectAnimationEventArgs.ObjectID`. +/// C# member: `P:LibreMetaverse.ObjectAnimationEventArgs.Simulator`. +pub use crate::object_manager::ObjectAnimationEventArgs; /// C# type: `T:LibreMetaverse.ObjectDataBlockUpdateEventArgs`. -pub struct ObjectDataBlockUpdateEventArgs; -impl ObjectDataBlockUpdateEventArgs { - /// C# member: `M:LibreMetaverse.ObjectDataBlockUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,LibreMetaverse.Primitive.ConstructionData,LibreMetaverse.Packets.ObjectUpdatePacket.ObjectDataBlock,LibreMetaverse.ObjectMovementUpdate,LibreMetaverse.NameValue[])`. - pub fn new( - simulator: libremetaverse::Simulator, - prim: libremetaverse::Primitive, - construction_data: libremetaverse::PrimitiveConstructionData, - block: libremetaverse::packets::ObjectUpdatePacketObjectDataBlock, - objectupdate: libremetaverse::ObjectMovementUpdate, - name_values: Vec, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectDataBlockUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,LibreMetaverse.Primitive.ConstructionData,LibreMetaverse.Packets.ObjectUpdatePacket.ObjectDataBlock,LibreMetaverse.ObjectMovementUpdate,LibreMetaverse.NameValue[])", - ) - } - /// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Block`. - pub fn block(&self) -> libremetaverse::packets::ObjectUpdatePacketObjectDataBlock { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Block" - ) - } - /// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.ConstructionData`. - pub fn construction_data(&self) -> libremetaverse::PrimitiveConstructionData { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.ConstructionData" - ) - } - /// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.NameValues`. - pub fn name_values(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.NameValues" - ) - } - /// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Prim`. - pub fn prim(&self) -> libremetaverse::Primitive { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Prim" - ) - } - /// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Simulator" - ) - } - /// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Update`. - pub fn update(&self) -> libremetaverse::ObjectMovementUpdate { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Update" - ) - } -} +/// C# member: `M:LibreMetaverse.ObjectDataBlockUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,LibreMetaverse.Primitive.ConstructionData,LibreMetaverse.Packets.ObjectUpdatePacket.ObjectDataBlock,LibreMetaverse.ObjectMovementUpdate,LibreMetaverse.NameValue[])`. +/// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Block`. +/// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.ConstructionData`. +/// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.NameValues`. +/// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Prim`. +/// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.ObjectDataBlockUpdateEventArgs.Update`. +pub use crate::object_manager::ObjectDataBlockUpdateEventArgs; /// C# type: `T:LibreMetaverse.ObjectManager`. pub use crate::object_material::ObjectManager; @@ -15429,28 +15272,28 @@ impl ObjectManager { libremetaverse::AvatarSitChangedEventArgs, >, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ObjectManager.AvatarSitChanged") + self.native_subscribe_avatar_sit_changed(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.AvatarUpdate`. pub fn subscribe_avatar_update( &self, handler: libremetaverse_types::compat::EventHandler, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ObjectManager.AvatarUpdate") + self.native_subscribe_avatar_update(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.KillObject`. pub fn subscribe_kill_object( &self, handler: libremetaverse_types::compat::EventHandler, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ObjectManager.KillObject") + self.native_subscribe_kill_object(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.KillObjects`. pub fn subscribe_kill_objects( &self, handler: libremetaverse_types::compat::EventHandler, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ObjectManager.KillObjects") + self.native_subscribe_kill_objects(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.ObjectAnimation`. pub fn subscribe_object_animation( @@ -15459,7 +15302,7 @@ impl ObjectManager { libremetaverse::ObjectAnimationEventArgs, >, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ObjectManager.ObjectAnimation") + self.native_subscribe_object_animation(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.ObjectDataBlockUpdate`. pub fn subscribe_object_data_block_update( @@ -15468,9 +15311,7 @@ impl ObjectManager { libremetaverse::ObjectDataBlockUpdateEventArgs, >, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.ObjectManager.ObjectDataBlockUpdate" - ) + self.native_subscribe_object_data_block_update(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.ObjectProperties`. pub fn subscribe_object_properties( @@ -15479,7 +15320,7 @@ impl ObjectManager { libremetaverse::ObjectPropertiesEventArgs, >, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ObjectManager.ObjectProperties") + self.native_subscribe_object_properties(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.ObjectPropertiesFamily`. pub fn subscribe_object_properties_family( @@ -15488,9 +15329,7 @@ impl ObjectManager { libremetaverse::ObjectPropertiesFamilyEventArgs, >, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.ObjectManager.ObjectPropertiesFamily" - ) + self.native_subscribe_object_properties_family(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.ObjectPropertiesUpdated`. pub fn subscribe_object_properties_updated( @@ -15499,16 +15338,14 @@ impl ObjectManager { libremetaverse::ObjectPropertiesUpdatedEventArgs, >, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.ObjectManager.ObjectPropertiesUpdated" - ) + self.native_subscribe_object_properties_updated(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.ObjectUpdate`. pub fn subscribe_object_update( &self, handler: libremetaverse_types::compat::EventHandler, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ObjectManager.ObjectUpdate") + self.native_subscribe_object_update(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.ParticleUpdate`. pub fn subscribe_particle_update( @@ -15517,14 +15354,14 @@ impl ObjectManager { libremetaverse::ParticleUpdateEventArgs, >, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ObjectManager.ParticleUpdate") + self.native_subscribe_particle_update(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.PayPriceReply`. pub fn subscribe_pay_price_reply( &self, handler: libremetaverse_types::compat::EventHandler, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ObjectManager.PayPriceReply") + self.native_subscribe_pay_price_reply(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.PhysicsProperties`. pub fn subscribe_physics_properties( @@ -15533,7 +15370,7 @@ impl ObjectManager { libremetaverse::PhysicsPropertiesEventArgs, >, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ObjectManager.PhysicsProperties") + self.native_subscribe_physics_properties(handler) } /// C# member: `E:LibreMetaverse.ObjectManager.TerseObjectUpdate`. pub fn subscribe_terse_object_update( @@ -15542,7 +15379,7 @@ impl ObjectManager { libremetaverse::TerseObjectUpdateEventArgs, >, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ObjectManager.TerseObjectUpdate") + self.native_subscribe_terse_object_update(handler) } /// C# member: `F:LibreMetaverse.ObjectManager.HAVOK_TIMESTEP`. pub const HAVOK_TIMESTEP: f32 = 0.022222223; @@ -15562,8 +15399,14 @@ impl ObjectManager { grass_type: libremetaverse::Grass, group_owner: libremetaverse_types::UUID, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.AddGrass(LibreMetaverse.Simulator,LibreMetaverse.Vector3,LibreMetaverse.Quaternion,LibreMetaverse.Vector3,LibreMetaverse.Grass,LibreMetaverse.UUID)", + self.native_add_foliage( + simulator, + scale, + rotation, + position, + grass_type as u8, + group_owner, + libremetaverse_types::PCode::Grass, ) } /// C# member: `M:LibreMetaverse.ObjectManager.AddPrim(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ConstructionData,LibreMetaverse.UUID,LibreMetaverse.Vector3,LibreMetaverse.Vector3,LibreMetaverse.Quaternion)`. @@ -15576,8 +15419,14 @@ impl ObjectManager { scale: libremetaverse_types::Vector3, rotation: libremetaverse_types::Quaternion, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.AddPrim(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ConstructionData,LibreMetaverse.UUID,LibreMetaverse.Vector3,LibreMetaverse.Vector3,LibreMetaverse.Quaternion)", + self.native_add_prim( + simulator, + prim, + group_id, + position, + scale, + rotation, + libremetaverse_types::PrimFlags::CREATE_SELECTED, ) } /// C# member: `M:LibreMetaverse.ObjectManager.AddPrim(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ConstructionData,LibreMetaverse.UUID,LibreMetaverse.Vector3,LibreMetaverse.Vector3,LibreMetaverse.Quaternion,LibreMetaverse.PrimFlags)`. @@ -15591,8 +15440,14 @@ impl ObjectManager { rotation: libremetaverse_types::Quaternion, create_flags: libremetaverse_types::PrimFlags, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.AddPrim(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ConstructionData,LibreMetaverse.UUID,LibreMetaverse.Vector3,LibreMetaverse.Vector3,LibreMetaverse.Quaternion,LibreMetaverse.PrimFlags)", + self.native_add_prim( + simulator, + prim, + group_id, + position, + scale, + rotation, + create_flags, ) } /// C# member: `M:LibreMetaverse.ObjectManager.AddTree(LibreMetaverse.Simulator,LibreMetaverse.Vector3,LibreMetaverse.Quaternion,LibreMetaverse.Vector3,LibreMetaverse.Tree,LibreMetaverse.UUID,System.Boolean)`. @@ -15606,8 +15461,18 @@ impl ObjectManager { group_owner: libremetaverse_types::UUID, new_tree: bool, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.AddTree(LibreMetaverse.Simulator,LibreMetaverse.Vector3,LibreMetaverse.Quaternion,LibreMetaverse.Vector3,LibreMetaverse.Tree,LibreMetaverse.UUID,System.Boolean)", + self.native_add_foliage( + simulator, + scale, + rotation, + position, + tree_type as u8, + group_owner, + if new_tree { + libremetaverse_types::PCode::NewTree + } else { + libremetaverse_types::PCode::Tree + }, ) } /// C# member: `M:LibreMetaverse.ObjectManager.ApplyMaterialAsync(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Int32,LibreMetaverse.UUID,LibreMetaverse.Assets.AssetMaterial,System.Threading.CancellationToken)`. @@ -15638,17 +15503,13 @@ impl ObjectManager { attach_point: libremetaverse_types::AttachmentPoint, rotation: libremetaverse_types::Quaternion, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.AttachObject(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.AttachmentPoint,LibreMetaverse.Quaternion)", - ) + self.native_attach(simulator, local_id, attach_point, rotation) } /// C# member: `M:LibreMetaverse.ObjectManager.BuildBasicShape(LibreMetaverse.PrimType)`. pub fn build_basic_shape( type_: libremetaverse_types::PrimType, ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.BuildBasicShape(LibreMetaverse.PrimType)", - ) + crate::object_material::ObjectManager::native_basic_shape(type_) } /// C# member: `M:LibreMetaverse.ObjectManager.BuyObject(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.SaleType,System.Int32,LibreMetaverse.UUID,LibreMetaverse.UUID)`. pub fn buy_object( @@ -15660,15 +15521,14 @@ impl ObjectManager { group_id: libremetaverse_types::UUID, category_id: libremetaverse_types::UUID, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.BuyObject(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.SaleType,System.Int32,LibreMetaverse.UUID,LibreMetaverse.UUID)", - ) + self.native_buy_object(simulator, local_id, sale_type, price, group_id, category_id) } /// C# member: `M:LibreMetaverse.ObjectManager.CleanupObjectTracking`. pub fn cleanup_object_tracking(&self) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.CleanupObjectTracking", - ) + { + self.native_cleanup_tracking(); + Ok(()) + } } /// C# member: `M:LibreMetaverse.ObjectManager.ClearMaterialOverrideAsync(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Int32,System.Threading.CancellationToken)`. pub async fn clear_material_override( @@ -15694,9 +15554,18 @@ impl ObjectManager { binormal: libremetaverse_types::Vector3, cancellation_token: Option, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.ClickObjectAsync(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3,LibreMetaverse.Vector3,System.Int32,LibreMetaverse.Vector3,LibreMetaverse.Vector3,LibreMetaverse.Vector3,System.Threading.CancellationToken)", + self.native_click_object( + simulator, + local_id, + uv_coord, + st_coord, + face_index, + position, + normal, + binormal, + cancellation_token, ) + .await } /// C# member: `M:LibreMetaverse.ObjectManager.ClickObjectAsync(LibreMetaverse.Simulator,System.UInt32,System.Threading.CancellationToken)`. pub async fn click_object_with_simulator_u_int32_cancellation_token( @@ -15705,9 +15574,18 @@ impl ObjectManager { local_id: u32, cancellation_token: Option, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.ClickObjectAsync(LibreMetaverse.Simulator,System.UInt32,System.Threading.CancellationToken)", + self.native_click_object( + simulator, + local_id, + libremetaverse_types::Vector3::zero(), + libremetaverse_types::Vector3::zero(), + 0, + libremetaverse_types::Vector3::zero(), + libremetaverse_types::Vector3::zero(), + libremetaverse_types::Vector3::zero(), + cancellation_token, ) + .await } /// C# member: `M:LibreMetaverse.ObjectManager.DeedObject(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.UUID)`. pub fn deed_object( @@ -15716,9 +15594,7 @@ impl ObjectManager { local_id: u32, group_owner: libremetaverse_types::UUID, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.DeedObject(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.UUID)", - ) + self.native_deed_objects(simulator, vec![local_id], group_owner) } /// C# member: `M:LibreMetaverse.ObjectManager.DeedObjects(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.UUID)`. pub fn deed_objects( @@ -15727,9 +15603,7 @@ impl ObjectManager { local_i_ds: Vec, group_owner: libremetaverse_types::UUID, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.DeedObjects(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.UUID)", - ) + self.native_deed_objects(simulator, local_i_ds, group_owner) } /// C# member: `M:LibreMetaverse.ObjectManager.DelinkPrims(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})`. pub fn delink_prims( @@ -15737,9 +15611,7 @@ impl ObjectManager { simulator: libremetaverse::Simulator, local_i_ds: Vec, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.DelinkPrims(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})", - ) + self.native_link_objects(simulator, local_i_ds, false) } /// C# member: `M:LibreMetaverse.ObjectManager.DeselectObject(LibreMetaverse.Simulator,System.UInt32)`. pub fn deselect_object( @@ -15747,9 +15619,7 @@ impl ObjectManager { simulator: libremetaverse::Simulator, local_id: u32, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.DeselectObject(LibreMetaverse.Simulator,System.UInt32)", - ) + self.native_deselect_objects(simulator, vec![local_id]) } /// C# member: `M:LibreMetaverse.ObjectManager.DeselectObjects(LibreMetaverse.Simulator,System.UInt32[])`. pub fn deselect_objects( @@ -15757,9 +15627,7 @@ impl ObjectManager { simulator: libremetaverse::Simulator, local_i_ds: Vec, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.DeselectObjects(LibreMetaverse.Simulator,System.UInt32[])", - ) + self.native_deselect_objects(simulator, local_i_ds) } /// C# member: `M:LibreMetaverse.ObjectManager.DetachObjects(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})`. pub fn detach_objects( @@ -15767,13 +15635,14 @@ impl ObjectManager { simulator: libremetaverse::Simulator, local_i_ds: Vec, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.DetachObjects(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})", - ) + self.native_attachment_action(simulator, local_i_ds, false) } /// C# member: `M:LibreMetaverse.ObjectManager.Dispose`. pub fn dispose(&self) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented("M:LibreMetaverse.ObjectManager.Dispose") + { + self.native_dispose(); + Ok(()) + } } /// C# member: `M:LibreMetaverse.ObjectManager.DropObject(LibreMetaverse.Simulator,System.UInt32)`. pub fn drop_object( @@ -15781,9 +15650,7 @@ impl ObjectManager { simulator: libremetaverse::Simulator, local_id: u32, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.DropObject(LibreMetaverse.Simulator,System.UInt32)", - ) + self.native_attachment_action(simulator, vec![local_id], true) } /// C# member: `M:LibreMetaverse.ObjectManager.GetPrimitive(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.UUID,System.Boolean)`. pub fn get_primitive( @@ -15793,18 +15660,14 @@ impl ObjectManager { full_id: libremetaverse_types::UUID, create_if_missing: bool, ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.GetPrimitive(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.UUID,System.Boolean)", - ) + self.native_get_primitive(simulator, local_id, full_id, create_if_missing) } /// C# member: `M:LibreMetaverse.ObjectManager.GetSimulatorsForObject(LibreMetaverse.UUID)`. pub fn get_simulators_for_object( &self, object_id: libremetaverse_types::UUID, ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.GetSimulatorsForObject(LibreMetaverse.UUID)", - ) + Ok(self.native_simulators_for(object_id)) } /// C# member: `M:LibreMetaverse.ObjectManager.IsNearBorder(LibreMetaverse.Vector3,System.UInt32,System.UInt32,System.Single)`. pub fn is_near_border( @@ -15814,8 +15677,11 @@ impl ObjectManager { region_size_y: u32, threshold: Option, ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.IsNearBorder(LibreMetaverse.Vector3,System.UInt32,System.UInt32,System.Single)", + crate::object_material::ObjectManager::native_is_near_border( + position, + region_size_x, + region_size_y, + threshold, ) } /// C# member: `M:LibreMetaverse.ObjectManager.LinkPrims(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})`. @@ -15824,9 +15690,7 @@ impl ObjectManager { simulator: libremetaverse::Simulator, local_i_ds: Vec, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.LinkPrims(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})", - ) + self.native_link_objects(simulator, local_i_ds, true) } /// C# member: `M:LibreMetaverse.ObjectManager.NavigateObjectMediaAsync(LibreMetaverse.UUID,System.Int32,System.String,LibreMetaverse.Simulator,System.Threading.CancellationToken)`. pub async fn navigate_object_media( @@ -15837,9 +15701,8 @@ impl ObjectManager { sim: libremetaverse::Simulator, cancellation_token: Option, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.NavigateObjectMediaAsync(LibreMetaverse.UUID,System.Int32,System.String,LibreMetaverse.Simulator,System.Threading.CancellationToken)", - ) + self.native_navigate_object_media(prim_id, face, new_url, sim, cancellation_token) + .await } /// C# member: `M:LibreMetaverse.ObjectManager.RequestMaterialsAsync(LibreMetaverse.Simulator,System.Collections.Generic.IEnumerable{LibreMetaverse.UUID},System.Threading.CancellationToken)`. pub async fn request_materials_with_simulator_i_enumerable_cancellation_token( @@ -15849,9 +15712,8 @@ impl ObjectManager { cancellation_token: Option, ) -> Result>, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.RequestMaterialsAsync(LibreMetaverse.Simulator,System.Collections.Generic.IEnumerable{LibreMetaverse.UUID},System.Threading.CancellationToken)", - ) + self.native_request_materials(sim, Some(materials.collect()), cancellation_token) + .await } /// C# member: `M:LibreMetaverse.ObjectManager.RequestMaterialsAsync(LibreMetaverse.Simulator,System.Threading.CancellationToken)`. pub async fn request_materials_with_simulator_cancellation_token( @@ -15860,9 +15722,8 @@ impl ObjectManager { cancellation_token: Option, ) -> Result>, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.RequestMaterialsAsync(LibreMetaverse.Simulator,System.Threading.CancellationToken)", - ) + self.native_request_materials(sim, None, cancellation_token) + .await } /// C# member: `M:LibreMetaverse.ObjectManager.RequestObject(LibreMetaverse.Simulator,System.UInt32)`. pub fn request_object( @@ -15870,9 +15731,7 @@ impl ObjectManager { simulator: libremetaverse::Simulator, local_id: u32, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.RequestObject(LibreMetaverse.Simulator,System.UInt32)", - ) + self.native_request_objects(simulator, vec![local_id]) } /// C# member: `M:LibreMetaverse.ObjectManager.RequestObjectMediaAsync(LibreMetaverse.UUID,LibreMetaverse.Simulator,System.Threading.CancellationToken)`. pub async fn request_object_media( @@ -15881,9 +15740,8 @@ impl ObjectManager { sim: libremetaverse::Simulator, cancellation_token: Option, ) -> Result<(bool, String, Option>), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.RequestObjectMediaAsync(LibreMetaverse.UUID,LibreMetaverse.Simulator,System.Threading.CancellationToken)", - ) + self.native_request_object_media(prim_id, sim, cancellation_token) + .await } /// C# member: `M:LibreMetaverse.ObjectManager.RequestObjectPropertiesFamily(LibreMetaverse.Simulator,LibreMetaverse.UUID)`. pub fn request_object_properties_family_with_simulator_uuid( @@ -15891,9 +15749,7 @@ impl ObjectManager { simulator: libremetaverse::Simulator, object_id: libremetaverse_types::UUID, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.RequestObjectPropertiesFamily(LibreMetaverse.Simulator,LibreMetaverse.UUID)", - ) + self.native_request_properties_family(simulator, object_id, true) } /// C# member: `M:LibreMetaverse.ObjectManager.RequestObjectPropertiesFamily(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Boolean)`. pub fn request_object_properties_family_with_simulator_uuid_boolean( @@ -15902,9 +15758,7 @@ impl ObjectManager { object_id: libremetaverse_types::UUID, reliable: bool, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.RequestObjectPropertiesFamily(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Boolean)", - ) + self.native_request_properties_family(simulator, object_id, reliable) } /// C# member: `M:LibreMetaverse.ObjectManager.RequestObjects(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})`. pub fn request_objects( @@ -15912,9 +15766,7 @@ impl ObjectManager { simulator: libremetaverse::Simulator, local_i_ds: Vec, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.RequestObjects(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})", - ) + self.native_request_objects(simulator, local_i_ds) } /// C# member: `M:LibreMetaverse.ObjectManager.RequestPayPrice(LibreMetaverse.Simulator,LibreMetaverse.UUID)`. pub fn request_pay_price( @@ -15922,9 +15774,7 @@ impl ObjectManager { simulator: libremetaverse::Simulator, object_id: libremetaverse_types::UUID, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.RequestPayPrice(LibreMetaverse.Simulator,LibreMetaverse.UUID)", - ) + self.native_request_pay_price(simulator, object_id) } /// C# member: `M:LibreMetaverse.ObjectManager.SelectObject(LibreMetaverse.Simulator,System.UInt32)`. pub fn select_object_with_simulator_u_int32( @@ -15932,9 +15782,7 @@ impl ObjectManager { simulator: libremetaverse::Simulator, local_id: u32, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SelectObject(LibreMetaverse.Simulator,System.UInt32)", - ) + self.native_select_objects(simulator, vec![local_id], true) } /// C# member: `M:LibreMetaverse.ObjectManager.SelectObject(LibreMetaverse.Simulator,System.UInt32,System.Boolean)`. pub fn select_object_with_simulator_u_int32_boolean( @@ -15943,9 +15791,7 @@ impl ObjectManager { local_id: u32, automatic_deselect: bool, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SelectObject(LibreMetaverse.Simulator,System.UInt32,System.Boolean)", - ) + self.native_select_objects(simulator, vec![local_id], automatic_deselect) } /// C# member: `M:LibreMetaverse.ObjectManager.SelectObjects(LibreMetaverse.Simulator,System.UInt32[])`. pub fn select_objects_with_simulator_u_int32_array( @@ -15953,9 +15799,7 @@ impl ObjectManager { simulator: libremetaverse::Simulator, local_i_ds: Vec, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SelectObjects(LibreMetaverse.Simulator,System.UInt32[])", - ) + self.native_select_objects(simulator, local_i_ds, true) } /// C# member: `M:LibreMetaverse.ObjectManager.SelectObjects(LibreMetaverse.Simulator,System.UInt32[],System.Boolean)`. pub fn select_objects_with_simulator_u_int32_array_boolean( @@ -15964,9 +15808,7 @@ impl ObjectManager { local_i_ds: Vec, automatic_deselect: bool, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SelectObjects(LibreMetaverse.Simulator,System.UInt32[],System.Boolean)", - ) + self.native_select_objects(simulator, local_i_ds, automatic_deselect) } /// C# member: `M:LibreMetaverse.ObjectManager.SendMaterialUpdatesAsync(LibreMetaverse.Simulator,System.Collections.Generic.IEnumerable{LibreMetaverse.GLTFMaterialUpdate},System.Threading.CancellationToken)`. pub async fn send_material_updates( @@ -15985,9 +15827,7 @@ impl ObjectManager { local_id: u32, description: String, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetDescription(LibreMetaverse.Simulator,System.UInt32,System.String)", - ) + self.native_set_descriptions(simulator, vec![local_id], vec![description]) } /// C# member: `M:LibreMetaverse.ObjectManager.SetDescriptions(LibreMetaverse.Simulator,System.UInt32[],System.String[])`. pub fn set_descriptions( @@ -15996,9 +15836,7 @@ impl ObjectManager { local_i_ds: Vec, descriptions: Vec, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetDescriptions(LibreMetaverse.Simulator,System.UInt32[],System.String[])", - ) + self.native_set_descriptions(simulator, local_i_ds, descriptions) } /// C# member: `M:LibreMetaverse.ObjectManager.SetExtraParamOff(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.ExtraParamType)`. pub fn set_extra_param_off( @@ -16007,9 +15845,7 @@ impl ObjectManager { local_id: u32, type_: libremetaverse_types::ExtraParamType, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetExtraParamOff(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.ExtraParamType)", - ) + self.native_set_extra_param(simulator, local_id, type_, false, Vec::new()) } /// C# member: `M:LibreMetaverse.ObjectManager.SetFlags(LibreMetaverse.Simulator,System.UInt32,System.Boolean,System.Boolean,System.Boolean,System.Boolean)`. pub fn set_flags_with_simulator_u_int32_boolean_boolean_boolean_boolean( @@ -16021,8 +15857,18 @@ impl ObjectManager { phantom: bool, casts_shadow: bool, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetFlags(LibreMetaverse.Simulator,System.UInt32,System.Boolean,System.Boolean,System.Boolean,System.Boolean)", + self.native_set_flags( + simulator, + local_id, + physical, + temporary, + phantom, + casts_shadow, + libremetaverse_types::PhysicsShapeType::Prim, + 1000.0, + 0.6, + 0.5, + 1.0, ) } /// C# member: `M:LibreMetaverse.ObjectManager.SetFlags(LibreMetaverse.Simulator,System.UInt32,System.Boolean,System.Boolean,System.Boolean,System.Boolean,LibreMetaverse.PhysicsShapeType,System.Single,System.Single,System.Single,System.Single)`. @@ -16040,8 +15886,18 @@ impl ObjectManager { restitution: f32, gravity_multiplier: f32, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetFlags(LibreMetaverse.Simulator,System.UInt32,System.Boolean,System.Boolean,System.Boolean,System.Boolean,LibreMetaverse.PhysicsShapeType,System.Single,System.Single,System.Single,System.Single)", + self.native_set_flags( + simulator, + local_id, + physical, + temporary, + phantom, + casts_shadow, + physics_type, + density, + friction, + restitution, + gravity_multiplier, ) } /// C# member: `M:LibreMetaverse.ObjectManager.SetFlexible(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.FlexibleData)`. @@ -16051,9 +15907,16 @@ impl ObjectManager { local_id: u32, flexible: libremetaverse::PrimitiveFlexibleData, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetFlexible(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.FlexibleData)", - ) + { + let data = flexible.get_bytes()?; + self.native_set_extra_param( + simulator, + local_id, + libremetaverse_types::ExtraParamType::FLEXIBLE, + true, + data, + ) + } } /// C# member: `M:LibreMetaverse.ObjectManager.SetLight(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.LightData)`. pub fn set_light( @@ -16062,9 +15925,17 @@ impl ObjectManager { local_id: u32, light: libremetaverse::PrimitiveLightData, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetLight(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.LightData)", - ) + { + let in_use = light.intensity != 0.0; + let data = light.get_bytes()?; + self.native_set_extra_param( + simulator, + local_id, + libremetaverse_types::ExtraParamType::LIGHT, + in_use, + data, + ) + } } /// C# member: `M:LibreMetaverse.ObjectManager.SetMaterial(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Material)`. pub fn set_material( @@ -16073,9 +15944,7 @@ impl ObjectManager { local_id: u32, material: libremetaverse_types::Material, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetMaterial(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Material)", - ) + self.native_set_material(simulator, local_id, material) } /// C# member: `M:LibreMetaverse.ObjectManager.SetMaterialOverrideAsync(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Int32,LibreMetaverse.Assets.AssetMaterial,System.Threading.CancellationToken)`. pub async fn set_material_override( @@ -16102,9 +15971,7 @@ impl ObjectManager { local_id: u32, name: String, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetName(LibreMetaverse.Simulator,System.UInt32,System.String)", - ) + self.native_set_names(simulator, vec![local_id], vec![name]) } /// C# member: `M:LibreMetaverse.ObjectManager.SetNames(LibreMetaverse.Simulator,System.UInt32[],System.String[])`. pub fn set_names( @@ -16113,9 +15980,7 @@ impl ObjectManager { local_i_ds: Vec, names: Vec, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetNames(LibreMetaverse.Simulator,System.UInt32[],System.String[])", - ) + self.native_set_names(simulator, local_i_ds, names) } /// C# member: `M:LibreMetaverse.ObjectManager.SetObjectsGroup(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.UUID)`. pub fn set_objects_group( @@ -16124,9 +15989,7 @@ impl ObjectManager { local_ids: Vec, group_id: libremetaverse_types::UUID, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetObjectsGroup(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.UUID)", - ) + self.native_set_group(simulator, local_ids, group_id) } /// C# member: `M:LibreMetaverse.ObjectManager.SetPermissions(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.PermissionWho,LibreMetaverse.PermissionMask,System.Boolean)`. pub fn set_permissions( @@ -16137,9 +16000,7 @@ impl ObjectManager { permissions: libremetaverse::PermissionMask, set: bool, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetPermissions(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.PermissionWho,LibreMetaverse.PermissionMask,System.Boolean)", - ) + self.native_set_permissions(simulator, local_i_ds, who, permissions, set) } /// C# member: `M:LibreMetaverse.ObjectManager.SetPosition(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3)`. pub fn set_position_with_simulator_u_int32_vector3( @@ -16148,8 +16009,11 @@ impl ObjectManager { local_id: u32, position: libremetaverse_types::Vector3, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetPosition(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3)", + self.native_update_vector( + simulator, + local_id, + position, + crate::UpdateType(crate::UpdateType::POSITION.0 | crate::UpdateType::LINKED.0), ) } /// C# member: `M:LibreMetaverse.ObjectManager.SetPosition(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3,System.Boolean)`. @@ -16160,8 +16024,18 @@ impl ObjectManager { position: libremetaverse_types::Vector3, child_only: bool, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetPosition(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3,System.Boolean)", + self.native_update_vector( + simulator, + local_id, + position, + crate::UpdateType( + crate::UpdateType::POSITION.0 + | if child_only { + 0 + } else { + crate::UpdateType::LINKED.0 + }, + ), ) } /// C# member: `M:LibreMetaverse.ObjectManager.SetRotation(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Quaternion)`. @@ -16171,9 +16045,7 @@ impl ObjectManager { local_id: u32, rotation: libremetaverse_types::Quaternion, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetRotation(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Quaternion)", - ) + self.native_update_rotation(simulator, local_id, rotation, false) } /// C# member: `M:LibreMetaverse.ObjectManager.SetRotation(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Quaternion,System.Boolean)`. pub fn set_rotation_with_simulator_u_int32_quaternion_boolean( @@ -16183,9 +16055,7 @@ impl ObjectManager { quat: libremetaverse_types::Quaternion, child_only: bool, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetRotation(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Quaternion,System.Boolean)", - ) + self.native_update_rotation(simulator, local_id, quat, child_only) } /// C# member: `M:LibreMetaverse.ObjectManager.SetSaleInfo(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.SaleType,System.Int32)`. pub fn set_sale_info_with_simulator_list_sale_type_int32( @@ -16195,9 +16065,7 @@ impl ObjectManager { sale_type: libremetaverse_types::SaleType, price: i32, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetSaleInfo(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.SaleType,System.Int32)", - ) + self.native_set_sale_info(simulator, local_i_ds, sale_type, price) } /// C# member: `M:LibreMetaverse.ObjectManager.SetSaleInfo(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.SaleType,System.Int32)`. pub fn set_sale_info_with_simulator_u_int32_sale_type_int32( @@ -16207,9 +16075,7 @@ impl ObjectManager { sale_type: libremetaverse_types::SaleType, price: i32, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetSaleInfo(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.SaleType,System.Int32)", - ) + self.native_set_sale_info(simulator, vec![local_id], sale_type, price) } /// C# member: `M:LibreMetaverse.ObjectManager.SetScale(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3,System.Boolean,System.Boolean)`. pub fn set_scale( @@ -16220,8 +16086,23 @@ impl ObjectManager { child_only: bool, uniform: bool, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetScale(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3,System.Boolean,System.Boolean)", + self.native_update_vector( + simulator, + local_id, + scale, + crate::UpdateType( + crate::UpdateType::SCALE.0 + | if child_only { + 0 + } else { + crate::UpdateType::LINKED.0 + } + | if uniform { + crate::UpdateType::UNIFORM.0 + } else { + 0 + }, + ), ) } /// C# member: `M:LibreMetaverse.ObjectManager.SetSculpt(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.SculptData)`. @@ -16231,9 +16112,7 @@ impl ObjectManager { local_id: u32, sculpt: libremetaverse::PrimitiveSculptData, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetSculpt(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.SculptData)", - ) + self.native_set_sculpt(simulator, local_id, sculpt) } /// C# member: `M:LibreMetaverse.ObjectManager.SetShape(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.ConstructionData)`. pub fn set_shape( @@ -16242,9 +16121,7 @@ impl ObjectManager { local_id: u32, prim: libremetaverse::PrimitiveConstructionData, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetShape(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.ConstructionData)", - ) + self.native_set_shape(simulator, local_id, prim) } /// C# member: `M:LibreMetaverse.ObjectManager.SetTextures(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.TextureEntry)`. pub fn set_textures_with_simulator_u_int32_texture_entry( @@ -16253,9 +16130,7 @@ impl ObjectManager { local_id: u32, textures: libremetaverse::PrimitiveTextureEntry, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetTextures(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.TextureEntry)", - ) + self.native_set_textures(simulator, local_id, textures, String::new()) } /// C# member: `M:LibreMetaverse.ObjectManager.SetTextures(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.TextureEntry,System.String)`. pub fn set_textures_with_simulator_u_int32_texture_entry_string( @@ -16265,9 +16140,7 @@ impl ObjectManager { textures: libremetaverse::PrimitiveTextureEntry, media_url: String, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.SetTextures(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.TextureEntry,System.String)", - ) + self.native_set_textures(simulator, local_id, textures, media_url) } /// C# member: `M:LibreMetaverse.ObjectManager.TrackObjectInSimulator(LibreMetaverse.UUID,LibreMetaverse.Simulator)`. pub fn track_object_in_simulator( @@ -16275,9 +16148,10 @@ impl ObjectManager { object_id: libremetaverse_types::UUID, sim: libremetaverse::Simulator, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.TrackObjectInSimulator(LibreMetaverse.UUID,LibreMetaverse.Simulator)", - ) + { + self.native_track(object_id, sim); + Ok(()) + } } /// C# member: `M:LibreMetaverse.ObjectManager.UntrackObjectInSimulator(LibreMetaverse.UUID,LibreMetaverse.Simulator)`. pub fn untrack_object_in_simulator( @@ -16285,9 +16159,10 @@ impl ObjectManager { object_id: libremetaverse_types::UUID, sim: libremetaverse::Simulator, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.UntrackObjectInSimulator(LibreMetaverse.UUID,LibreMetaverse.Simulator)", - ) + { + self.native_untrack(object_id, &sim); + Ok(()) + } } /// C# member: `M:LibreMetaverse.ObjectManager.UpdateObject(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3,LibreMetaverse.UpdateType)`. pub fn update_object( @@ -16297,9 +16172,7 @@ impl ObjectManager { data: libremetaverse_types::Vector3, type_: libremetaverse::UpdateType, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.UpdateObject(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3,LibreMetaverse.UpdateType)", - ) + self.native_update_vector(simulator, local_id, data, type_) } /// C# member: `M:LibreMetaverse.ObjectManager.UpdateObjectMediaAsync(LibreMetaverse.UUID,LibreMetaverse.MediaEntry[],LibreMetaverse.Simulator,System.Threading.CancellationToken)`. pub async fn update_object_media( @@ -16309,155 +16182,48 @@ impl ObjectManager { sim: libremetaverse::Simulator, cancellation_token: Option, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectManager.UpdateObjectMediaAsync(LibreMetaverse.UUID,LibreMetaverse.MediaEntry[],LibreMetaverse.Simulator,System.Threading.CancellationToken)", - ) + self.native_update_object_media(prim_id, face_media, sim, cancellation_token) + .await } } /// C# type: `T:LibreMetaverse.ObjectMediaEventArgs`. -pub struct ObjectMediaEventArgs; -impl ObjectMediaEventArgs { - /// C# member: `M:LibreMetaverse.ObjectMediaEventArgs.#ctor(System.Boolean,System.String,LibreMetaverse.MediaEntry[])`. - pub fn new( - success: bool, - version: String, - face_media: Vec, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectMediaEventArgs.#ctor(System.Boolean,System.String,LibreMetaverse.MediaEntry[])", - ) - } - /// C# member: `P:LibreMetaverse.ObjectMediaEventArgs.FaceMedia`. - pub fn face_media(&self) -> Vec { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ObjectMediaEventArgs.FaceMedia") - } - /// Setter for C# member: `P:LibreMetaverse.ObjectMediaEventArgs.FaceMedia`. - pub fn set_face_media(&mut self, value: Vec) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ObjectMediaEventArgs.FaceMedia") - } - /// C# member: `P:LibreMetaverse.ObjectMediaEventArgs.Success`. - pub fn success(&self) -> bool { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ObjectMediaEventArgs.Success") - } - /// Setter for C# member: `P:LibreMetaverse.ObjectMediaEventArgs.Success`. - pub fn set_success(&mut self, value: bool) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ObjectMediaEventArgs.Success") - } - /// C# member: `P:LibreMetaverse.ObjectMediaEventArgs.Version`. - pub fn version(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ObjectMediaEventArgs.Version") - } - /// Setter for C# member: `P:LibreMetaverse.ObjectMediaEventArgs.Version`. - pub fn set_version(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ObjectMediaEventArgs.Version") - } -} +/// C# member: `M:LibreMetaverse.ObjectMediaEventArgs.#ctor(System.Boolean,System.String,LibreMetaverse.MediaEntry[])`. +/// C# member: `P:LibreMetaverse.ObjectMediaEventArgs.FaceMedia`. +/// C# member: `P:LibreMetaverse.ObjectMediaEventArgs.Success`. +/// C# member: `P:LibreMetaverse.ObjectMediaEventArgs.Version`. +pub use crate::object_material::ObjectMediaEventArgs; /// C# type: `T:LibreMetaverse.ObjectMovementUpdate`. -pub struct ObjectMovementUpdate { - /// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Acceleration`. - pub acceleration: libremetaverse_types::Vector3, - /// C# member: `F:LibreMetaverse.ObjectMovementUpdate.AngularVelocity`. - pub angular_velocity: libremetaverse_types::Vector3, - /// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Avatar`. - pub avatar: bool, - /// C# member: `F:LibreMetaverse.ObjectMovementUpdate.CollisionPlane`. - pub collision_plane: libremetaverse_types::Vector4, - /// C# member: `F:LibreMetaverse.ObjectMovementUpdate.LocalID`. - pub local_id: u32, - /// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Position`. - pub position: libremetaverse_types::Vector3, - /// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Rotation`. - pub rotation: libremetaverse_types::Quaternion, - /// C# member: `F:LibreMetaverse.ObjectMovementUpdate.State`. - pub state: u8, - /// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Textures`. - pub textures: libremetaverse::PrimitiveTextureEntry, - /// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Velocity`. - pub velocity: libremetaverse_types::Vector3, -} -impl ObjectMovementUpdate {} +/// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Acceleration`. +/// C# member: `F:LibreMetaverse.ObjectMovementUpdate.AngularVelocity`. +/// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Avatar`. +/// C# member: `F:LibreMetaverse.ObjectMovementUpdate.CollisionPlane`. +/// C# member: `F:LibreMetaverse.ObjectMovementUpdate.LocalID`. +/// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Position`. +/// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Rotation`. +/// C# member: `F:LibreMetaverse.ObjectMovementUpdate.State`. +/// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Textures`. +/// C# member: `F:LibreMetaverse.ObjectMovementUpdate.Velocity`. +pub use crate::object_manager::ObjectMovementUpdate; /// C# type: `T:LibreMetaverse.ObjectPropertiesEventArgs`. -pub struct ObjectPropertiesEventArgs; -impl ObjectPropertiesEventArgs { - /// C# member: `M:LibreMetaverse.ObjectPropertiesEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ObjectProperties)`. - pub fn new( - simulator: libremetaverse::Simulator, - props: libremetaverse::PrimitiveObjectProperties, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectPropertiesEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ObjectProperties)", - ) - } - /// C# member: `P:LibreMetaverse.ObjectPropertiesEventArgs.Properties`. - pub fn properties(&self) -> libremetaverse::PrimitiveObjectProperties { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectPropertiesEventArgs.Properties" - ) - } - /// C# member: `P:LibreMetaverse.ObjectPropertiesEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectPropertiesEventArgs.Simulator" - ) - } -} +/// C# member: `M:LibreMetaverse.ObjectPropertiesEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ObjectProperties)`. +/// C# member: `P:LibreMetaverse.ObjectPropertiesEventArgs.Properties`. +/// C# member: `P:LibreMetaverse.ObjectPropertiesEventArgs.Simulator`. +pub use crate::object_manager::ObjectPropertiesEventArgs; /// C# type: `T:LibreMetaverse.ObjectPropertiesFamilyEventArgs`. -pub struct ObjectPropertiesFamilyEventArgs; -impl ObjectPropertiesFamilyEventArgs { - /// C# member: `M:LibreMetaverse.ObjectPropertiesFamilyEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ObjectProperties,LibreMetaverse.ReportType)`. - pub fn new( - simulator: libremetaverse::Simulator, - props: libremetaverse::PrimitiveObjectProperties, - type_: libremetaverse::ReportType, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectPropertiesFamilyEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ObjectProperties,LibreMetaverse.ReportType)", - ) - } - /// C# member: `P:LibreMetaverse.ObjectPropertiesFamilyEventArgs.Properties`. - pub fn properties(&self) -> libremetaverse::PrimitiveObjectProperties { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectPropertiesFamilyEventArgs.Properties" - ) - } - /// C# member: `P:LibreMetaverse.ObjectPropertiesFamilyEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectPropertiesFamilyEventArgs.Simulator" - ) - } - /// C# member: `P:LibreMetaverse.ObjectPropertiesFamilyEventArgs.Type`. - pub fn type_(&self) -> libremetaverse::ReportType { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectPropertiesFamilyEventArgs.Type" - ) - } -} +/// C# member: `M:LibreMetaverse.ObjectPropertiesFamilyEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ObjectProperties,LibreMetaverse.ReportType)`. +/// C# member: `P:LibreMetaverse.ObjectPropertiesFamilyEventArgs.Properties`. +/// C# member: `P:LibreMetaverse.ObjectPropertiesFamilyEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.ObjectPropertiesFamilyEventArgs.Type`. +pub use crate::object_manager::ObjectPropertiesFamilyEventArgs; /// C# type: `T:LibreMetaverse.ObjectPropertiesUpdatedEventArgs`. -pub struct ObjectPropertiesUpdatedEventArgs; -impl ObjectPropertiesUpdatedEventArgs { - /// C# member: `M:LibreMetaverse.ObjectPropertiesUpdatedEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,LibreMetaverse.Primitive.ObjectProperties)`. - pub fn new( - simulator: libremetaverse::Simulator, - prim: libremetaverse::Primitive, - props: libremetaverse::PrimitiveObjectProperties, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ObjectPropertiesUpdatedEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,LibreMetaverse.Primitive.ObjectProperties)", - ) - } - /// C# member: `P:LibreMetaverse.ObjectPropertiesUpdatedEventArgs.Prim`. - pub fn prim(&self) -> libremetaverse::Primitive { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ObjectPropertiesUpdatedEventArgs.Prim" - ) - } -} +/// C# member: `M:LibreMetaverse.ObjectPropertiesUpdatedEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,LibreMetaverse.Primitive.ObjectProperties)`. +/// C# member: `P:LibreMetaverse.ObjectPropertiesUpdatedEventArgs.Prim`. +pub use crate::object_manager::ObjectPropertiesUpdatedEventArgs; /// C# type: `T:LibreMetaverse.ObjectReturnType`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -17896,73 +17662,19 @@ impl ParticleSimulator { } /// C# type: `T:LibreMetaverse.ParticleUpdateEventArgs`. -pub struct ParticleUpdateEventArgs; -impl ParticleUpdateEventArgs { - /// C# member: `M:LibreMetaverse.ParticleUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ParticleSystem,LibreMetaverse.Primitive)`. - pub fn new( - simulator: libremetaverse::Simulator, - particlesystem: libremetaverse::PrimitiveParticleSystem, - source: libremetaverse::Primitive, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParticleUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ParticleSystem,LibreMetaverse.Primitive)", - ) - } - /// C# member: `P:LibreMetaverse.ParticleUpdateEventArgs.ParticleSystem`. - pub fn particle_system(&self) -> libremetaverse::PrimitiveParticleSystem { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParticleUpdateEventArgs.ParticleSystem" - ) - } - /// C# member: `P:LibreMetaverse.ParticleUpdateEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParticleUpdateEventArgs.Simulator" - ) - } - /// C# member: `P:LibreMetaverse.ParticleUpdateEventArgs.Source`. - pub fn source(&self) -> libremetaverse::Primitive { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ParticleUpdateEventArgs.Source") - } -} +/// C# member: `M:LibreMetaverse.ParticleUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ParticleSystem,LibreMetaverse.Primitive)`. +/// C# member: `P:LibreMetaverse.ParticleUpdateEventArgs.ParticleSystem`. +/// C# member: `P:LibreMetaverse.ParticleUpdateEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.ParticleUpdateEventArgs.Source`. +pub use crate::object_manager::ParticleUpdateEventArgs; /// C# type: `T:LibreMetaverse.PayPriceReplyEventArgs`. -pub struct PayPriceReplyEventArgs; -impl PayPriceReplyEventArgs { - /// C# member: `M:LibreMetaverse.PayPriceReplyEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Int32,System.Int32[])`. - pub fn new( - simulator: libremetaverse::Simulator, - object_id: libremetaverse_types::UUID, - default_price: i32, - button_prices: Vec, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PayPriceReplyEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Int32,System.Int32[])", - ) - } - /// C# member: `P:LibreMetaverse.PayPriceReplyEventArgs.ButtonPrices`. - pub fn button_prices(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.PayPriceReplyEventArgs.ButtonPrices" - ) - } - /// C# member: `P:LibreMetaverse.PayPriceReplyEventArgs.DefaultPrice`. - pub fn default_price(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.PayPriceReplyEventArgs.DefaultPrice" - ) - } - /// C# member: `P:LibreMetaverse.PayPriceReplyEventArgs.ObjectID`. - pub fn object_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PayPriceReplyEventArgs.ObjectID") - } - /// C# member: `P:LibreMetaverse.PayPriceReplyEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.PayPriceReplyEventArgs.Simulator" - ) - } -} +/// C# member: `M:LibreMetaverse.PayPriceReplyEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Int32,System.Int32[])`. +/// C# member: `P:LibreMetaverse.PayPriceReplyEventArgs.ButtonPrices`. +/// C# member: `P:LibreMetaverse.PayPriceReplyEventArgs.DefaultPrice`. +/// C# member: `P:LibreMetaverse.PayPriceReplyEventArgs.ObjectID`. +/// C# member: `P:LibreMetaverse.PayPriceReplyEventArgs.Simulator`. +pub use crate::object_manager::PayPriceReplyEventArgs; /// C# type: `T:LibreMetaverse.PayPriceType`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -18038,23 +17750,10 @@ impl PermissionWho { pub use crate::inventory::Permissions; /// C# type: `T:LibreMetaverse.PhysicsPropertiesEventArgs`. -pub struct PhysicsPropertiesEventArgs { - /// C# member: `F:LibreMetaverse.PhysicsPropertiesEventArgs.PhysicsProperties`. - pub physics_properties: libremetaverse::PrimitivePhysicsProperties, - /// C# member: `F:LibreMetaverse.PhysicsPropertiesEventArgs.Simulator`. - pub simulator: libremetaverse::Simulator, -} -impl PhysicsPropertiesEventArgs { - /// C# member: `M:LibreMetaverse.PhysicsPropertiesEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.PhysicsProperties)`. - pub fn new( - sim: libremetaverse::Simulator, - props: libremetaverse::PrimitivePhysicsProperties, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PhysicsPropertiesEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.PhysicsProperties)", - ) - } -} +/// C# member: `F:LibreMetaverse.PhysicsPropertiesEventArgs.PhysicsProperties`. +/// C# member: `F:LibreMetaverse.PhysicsPropertiesEventArgs.Simulator`. +/// C# member: `M:LibreMetaverse.PhysicsPropertiesEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive.PhysicsProperties)`. +pub use crate::object_manager::PhysicsPropertiesEventArgs; /// C# type: `T:LibreMetaverse.PickInfoReplyEventArgs`. pub struct PickInfoReplyEventArgs; @@ -18243,41 +17942,13 @@ impl PreloadSoundEventArgs { } /// C# type: `T:LibreMetaverse.PrimEventArgs`. -pub struct PrimEventArgs; -impl PrimEventArgs { - /// C# member: `M:LibreMetaverse.PrimEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,System.UInt16,System.Boolean,System.Boolean)`. - pub fn new( - simulator: libremetaverse::Simulator, - prim: libremetaverse::Primitive, - time_dilation: u16, - is_new: bool, - is_attachment: bool, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PrimEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,System.UInt16,System.Boolean,System.Boolean)", - ) - } - /// C# member: `P:LibreMetaverse.PrimEventArgs.IsAttachment`. - pub fn is_attachment(&self) -> bool { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PrimEventArgs.IsAttachment") - } - /// C# member: `P:LibreMetaverse.PrimEventArgs.IsNew`. - pub fn is_new(&self) -> bool { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PrimEventArgs.IsNew") - } - /// C# member: `P:LibreMetaverse.PrimEventArgs.Prim`. - pub fn prim(&self) -> libremetaverse::Primitive { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PrimEventArgs.Prim") - } - /// C# member: `P:LibreMetaverse.PrimEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PrimEventArgs.Simulator") - } - /// C# member: `P:LibreMetaverse.PrimEventArgs.TimeDilation`. - pub fn time_dilation(&self) -> u16 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PrimEventArgs.TimeDilation") - } -} +/// C# member: `M:LibreMetaverse.PrimEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,System.UInt16,System.Boolean,System.Boolean)`. +/// C# member: `P:LibreMetaverse.PrimEventArgs.IsAttachment`. +/// C# member: `P:LibreMetaverse.PrimEventArgs.IsNew`. +/// C# member: `P:LibreMetaverse.PrimEventArgs.Prim`. +/// C# member: `P:LibreMetaverse.PrimEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.PrimEventArgs.TimeDilation`. +pub use crate::object_manager::PrimEventArgs; /// C# type: `T:LibreMetaverse.Primitive`. /// C# member: `F:LibreMetaverse.Primitive.Acceleration`. @@ -21013,42 +20684,12 @@ impl TerrainPatchLayerType { } /// C# type: `T:LibreMetaverse.TerseObjectUpdateEventArgs`. -pub struct TerseObjectUpdateEventArgs; -impl TerseObjectUpdateEventArgs { - /// C# member: `M:LibreMetaverse.TerseObjectUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,LibreMetaverse.ObjectMovementUpdate,System.UInt16)`. - pub fn new( - simulator: libremetaverse::Simulator, - prim: libremetaverse::Primitive, - update: libremetaverse::ObjectMovementUpdate, - time_dilation: u16, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerseObjectUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,LibreMetaverse.ObjectMovementUpdate,System.UInt16)", - ) - } - /// C# member: `P:LibreMetaverse.TerseObjectUpdateEventArgs.Prim`. - pub fn prim(&self) -> libremetaverse::Primitive { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.TerseObjectUpdateEventArgs.Prim") - } - /// C# member: `P:LibreMetaverse.TerseObjectUpdateEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.TerseObjectUpdateEventArgs.Simulator" - ) - } - /// C# member: `P:LibreMetaverse.TerseObjectUpdateEventArgs.TimeDilation`. - pub fn time_dilation(&self) -> u16 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.TerseObjectUpdateEventArgs.TimeDilation" - ) - } - /// C# member: `P:LibreMetaverse.TerseObjectUpdateEventArgs.Update`. - pub fn update(&self) -> libremetaverse::ObjectMovementUpdate { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.TerseObjectUpdateEventArgs.Update" - ) - } -} +/// C# member: `M:LibreMetaverse.TerseObjectUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,LibreMetaverse.ObjectMovementUpdate,System.UInt16)`. +/// C# member: `P:LibreMetaverse.TerseObjectUpdateEventArgs.Prim`. +/// C# member: `P:LibreMetaverse.TerseObjectUpdateEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.TerseObjectUpdateEventArgs.TimeDilation`. +/// C# member: `P:LibreMetaverse.TerseObjectUpdateEventArgs.Update`. +pub use crate::object_manager::TerseObjectUpdateEventArgs; /// C# type: `T:LibreMetaverse.TextureAttributes`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -119049,6 +118690,7 @@ pub mod packets { } /// C# type: `T:LibreMetaverse.Packets.ObjectUpdatePacket.ObjectDataBlock`. + #[derive(Clone, Debug)] pub struct ObjectUpdatePacketObjectDataBlock { /// C# member: `F:LibreMetaverse.Packets.ObjectUpdatePacket.ObjectDataBlock.CRC`. pub crc: u32, diff --git a/crates/libremetaverse/src/lib.rs b/crates/libremetaverse/src/lib.rs index 28494fc..0a34990 100644 --- a/crates/libremetaverse/src/lib.rs +++ b/crates/libremetaverse/src/lib.rs @@ -42,6 +42,7 @@ mod login; mod message_codec; mod message_decoder; mod network_manager; +mod object_manager; mod object_material; mod object_model; #[rustfmt::skip] diff --git a/crates/libremetaverse/src/object_manager.rs b/crates/libremetaverse/src/object_manager.rs new file mode 100644 index 0000000..b64c0af --- /dev/null +++ b/crates/libremetaverse/src/object_manager.rs @@ -0,0 +1,3019 @@ +//! Object update state, event correlation, and simulator action routing. + +#![allow(clippy::missing_errors_doc)] +#![allow(clippy::must_use_candidate)] +#![allow(clippy::needless_pass_by_value)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::too_many_lines)] +#![allow(clippy::new_ret_no_self)] +#![allow(clippy::fn_params_excessive_bools)] +#![allow(clippy::unused_self)] + +use crate::agent_manager::EventRegistry; +use crate::network_manager::RawPacketReceivedEventArgs; +use crate::packet_catalog::GeneratedPacket; +use crate::{ + Animation, Avatar, Error, GridClient, NameValue, ObjectManager, Primitive, + PrimitiveConstructionData, PrimitiveObjectProperties, PrimitiveParticleSystem, + PrimitivePhysicsProperties, PrimitiveTextureAnimation, PrimitiveTextureEntry, Simulator, +}; +use libremetaverse_types::compat::{EventHandler, Subscription}; +use libremetaverse_types::{ + ClickAction, Color4, JointType, Material, ObjectCategory, PCode, PathCurve, PrimFlags, + PrimType, ProfileCurve, Quaternion, SaleType, SoundFlags, UUID, Vector3, Vector4, +}; +use std::collections::{HashMap, HashSet}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex, RwLock}; +use std::time::{Duration, UNIX_EPOCH}; + +const MAX_TRACKED_OBJECTS: usize = 262_144; +const MAX_PACKET_BLOCKS: usize = 65_535; +const MAX_FIELD_BYTES: usize = 1024 * 1024; + +fn mutex(value: &Mutex) -> std::sync::MutexGuard<'_, T> { + value + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) +} + +fn read(value: &RwLock) -> std::sync::RwLockReadGuard<'_, T> { + value + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) +} + +fn write(value: &RwLock) -> std::sync::RwLockWriteGuard<'_, T> { + value + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner) +} + +fn decode_packet(bytes: &[u8]) -> Result { + if bytes.len() > MAX_FIELD_BYTES * 16 { + return Err(Error::Argument); + } + let mut position = 0_i32; + T::new_from_bytes(bytes, &mut position) +} + +fn wire_string(bytes: &[u8]) -> Result { + if bytes.len() > MAX_FIELD_BYTES { + return Err(Error::Argument); + } + let end = bytes + .iter() + .position(|byte| *byte == 0) + .unwrap_or(bytes.len()); + Ok(String::from_utf8_lossy(&bytes[..end]).into_owned()) +} + +fn name_values(bytes: &[u8]) -> Result<(Vec, bool), Error> { + let text = wire_string(bytes)?; + let mut values = Vec::new(); + let mut attachment = false; + for line in text.lines().map(str::trim).filter(|line| !line.is_empty()) { + let mut fields = line.splitn(5, char::is_whitespace); + let name = fields.next().unwrap_or_default().to_owned(); + let _wire_type = fields.next(); + let _wire_class = fields.next(); + let _wire_send_to = fields.next(); + let value = fields.next().unwrap_or_default().trim_start().to_owned(); + attachment |= name == "AttachItemID"; + values.push(NameValue { + class_: crate::NameValueClassType::ReadOnly, + name, + sendto: crate::NameValueSendtoType::Sim, + type_: crate::NameValueValueType::String, + value: Some(value.into()), + }); + } + Ok((values, attachment)) +} + +fn f32_at(bytes: &[u8], offset: usize) -> Result { + let raw: [u8; 4] = bytes + .get(offset..offset + 4) + .ok_or(Error::IndexOutOfRange)? + .try_into() + .map_err(|_| Error::IndexOutOfRange)?; + Ok(f32::from_le_bytes(raw)) +} + +fn u16_at(bytes: &[u8], offset: usize) -> Result { + let raw: [u8; 2] = bytes + .get(offset..offset + 2) + .ok_or(Error::IndexOutOfRange)? + .try_into() + .map_err(|_| Error::IndexOutOfRange)?; + Ok(u16::from_le_bytes(raw)) +} + +fn u32_at(bytes: &[u8], offset: usize) -> Result { + let raw: [u8; 4] = bytes + .get(offset..offset + 4) + .ok_or(Error::IndexOutOfRange)? + .try_into() + .map_err(|_| Error::IndexOutOfRange)?; + Ok(u32::from_le_bytes(raw)) +} + +fn vector3_at(bytes: &[u8], offset: usize) -> Result { + Ok(Vector3 { + x: f32_at(bytes, offset)?, + y: f32_at(bytes, offset + 4)?, + z: f32_at(bytes, offset + 8)?, + }) +} + +fn vector4_at(bytes: &[u8], offset: usize) -> Result { + Ok(Vector4 { + x: f32_at(bytes, offset)?, + y: f32_at(bytes, offset + 4)?, + z: f32_at(bytes, offset + 8)?, + w: f32_at(bytes, offset + 12)?, + }) +} + +fn quaternion3_at(bytes: &[u8], offset: usize) -> Result { + Quaternion::new_with_single_single_single( + f32_at(bytes, offset)?, + f32_at(bytes, offset + 4)?, + f32_at(bytes, offset + 8)?, + ) +} + +fn quantized_u16(bytes: &[u8], offset: usize, low: f32, high: f32) -> Result { + Ok(low + f32::from(u16_at(bytes, offset)?) * (high - low) / 65_535.0) +} + +fn quantized_u8(value: u8, low: f32, high: f32) -> f32 { + low + f32::from(value) * (high - low) / 255.0 +} + +fn decode_movement(bytes: &[u8]) -> Result { + let mut value = ObjectMovementUpdate::default(); + let mut p = 0; + if matches!(bytes.len(), 76 | 48) { + value.collision_plane = vector4_at(bytes, 0)?; + p = 16; + } + match bytes.len() { + 60 | 76 => { + value.position = vector3_at(bytes, p)?; + value.velocity = vector3_at(bytes, p + 12)?; + value.acceleration = vector3_at(bytes, p + 24)?; + value.rotation = quaternion3_at(bytes, p + 36)?; + value.angular_velocity = vector3_at(bytes, p + 48)?; + } + 32 | 48 => { + value.position = Vector3 { + x: quantized_u16(bytes, p, -128.0, 384.0)?, + y: quantized_u16(bytes, p + 2, -128.0, 384.0)?, + z: quantized_u16(bytes, p + 4, -256.0, 768.0)?, + }; + p += 6; + value.velocity = Vector3 { + x: quantized_u16(bytes, p, -256.0, 256.0)?, + y: quantized_u16(bytes, p + 2, -256.0, 256.0)?, + z: quantized_u16(bytes, p + 4, -256.0, 256.0)?, + }; + p += 6; + value.acceleration = Vector3 { + x: quantized_u16(bytes, p, -256.0, 256.0)?, + y: quantized_u16(bytes, p + 2, -256.0, 256.0)?, + z: quantized_u16(bytes, p + 4, -256.0, 256.0)?, + }; + p += 6; + value.rotation = Quaternion::new_with_single_single_single_single( + quantized_u16(bytes, p, -1.0, 1.0)?, + quantized_u16(bytes, p + 2, -1.0, 1.0)?, + quantized_u16(bytes, p + 4, -1.0, 1.0)?, + quantized_u16(bytes, p + 6, -1.0, 1.0)?, + )?; + p += 8; + value.angular_velocity = Vector3 { + x: quantized_u16(bytes, p, -256.0, 256.0)?, + y: quantized_u16(bytes, p + 2, -256.0, 256.0)?, + z: quantized_u16(bytes, p + 4, -256.0, 256.0)?, + }; + } + 16 => { + let q3 = |at: usize, low, high| -> Result { + let slice = bytes.get(at..at + 3).ok_or(Error::IndexOutOfRange)?; + Ok(Vector3 { + x: quantized_u8(slice[0], low, high), + y: quantized_u8(slice[1], low, high), + z: quantized_u8(slice[2], low, high), + }) + }; + value.position = q3(0, -256.0, 256.0)?; + value.velocity = q3(3, -256.0, 256.0)?; + value.acceleration = q3(6, -256.0, 256.0)?; + value.rotation = Quaternion::new_with_single_single_single_single( + quantized_u8(bytes[9], -1.0, 1.0), + quantized_u8(bytes[10], -1.0, 1.0), + quantized_u8(bytes[11], -1.0, 1.0), + quantized_u8(bytes[12], -1.0, 1.0), + )?; + value.angular_velocity = q3(13, -256.0, 256.0)?; + } + _ => return Err(Error::Argument), + } + Ok(value) +} + +fn click_action(value: u8) -> ClickAction { + match value { + 1 => ClickAction::Sit, + 2 => ClickAction::Buy, + 3 => ClickAction::Pay, + 4 => ClickAction::OpenTask, + 5 => ClickAction::PlayMedia, + 6 => ClickAction::OpenMedia, + _ => ClickAction::Touch, + } +} + +fn joint_type(value: u8) -> JointType { + match value { + 1 => JointType::Hinge, + 2 => JointType::Point, + _ => JointType::Invalid, + } +} + +fn object_category(value: u32) -> ObjectCategory { + match value { + 1 => ObjectCategory::Owner, + 2 => ObjectCategory::Group, + 3 => ObjectCategory::Other, + 4 => ObjectCategory::Selected, + 5 => ObjectCategory::Temporary, + _ => ObjectCategory::None, + } +} + +fn sale_type(value: u8) -> SaleType { + match value { + 1 => SaleType::Original, + 2 => SaleType::Copy, + 3 => SaleType::Contents, + _ => SaleType::Not, + } +} + +fn report_type(value: u32) -> crate::ReportType { + match value { + 1 => crate::ReportType::Unknown, + 2 => crate::ReportType::Bug, + 3 => crate::ReportType::Complaint, + 4 => crate::ReportType::CustomerServiceRequest, + _ => crate::ReportType::None, + } +} + +struct Cursor<'a> { + bytes: &'a [u8], + position: usize, +} + +impl<'a> Cursor<'a> { + fn new(bytes: &'a [u8]) -> Result { + if bytes.len() > MAX_FIELD_BYTES { + return Err(Error::Argument); + } + Ok(Self { bytes, position: 0 }) + } + fn remaining(&self) -> &'a [u8] { + &self.bytes[self.position..] + } + fn take(&mut self, length: usize) -> Result<&'a [u8], Error> { + let end = self.position.checked_add(length).ok_or(Error::Argument)?; + let value = self + .bytes + .get(self.position..end) + .ok_or(Error::IndexOutOfRange)?; + self.position = end; + Ok(value) + } + fn skip(&mut self, length: usize) -> Result<(), Error> { + self.take(length).map(|_| ()) + } + fn u8(&mut self) -> Result { + Ok(self.take(1)?[0]) + } + fn i8(&mut self) -> Result { + Ok(self.u8()?.cast_signed()) + } + fn u16(&mut self) -> Result { + u16_at(self.take(2)?, 0) + } + fn u32(&mut self) -> Result { + u32_at(self.take(4)?, 0) + } + fn f32(&mut self) -> Result { + f32_at(self.take(4)?, 0) + } + fn uuid(&mut self) -> Result { + UUID::new_with_bytes_int32(self.take(16)?.to_vec(), 0) + } + fn vector3(&mut self) -> Result { + vector3_at(self.take(12)?, 0) + } + fn quaternion3(&mut self) -> Result { + quaternion3_at(self.take(12)?, 0) + } + fn nul_string(&mut self) -> Result { + let end = self + .remaining() + .iter() + .position(|byte| *byte == 0) + .ok_or(Error::Argument)?; + let value = wire_string(self.take(end)?)?; + self.skip(1)?; + Ok(value) + } +} + +#[derive(Clone)] +pub struct PrimEventArgs { + simulator: Simulator, + prim: Primitive, + time_dilation: u16, + is_new: bool, + is_attachment: bool, +} +impl PrimEventArgs { + pub fn new( + simulator: Simulator, + prim: Primitive, + time_dilation: u16, + is_new: bool, + is_attachment: bool, + ) -> Result { + Ok(Self { + simulator, + prim, + time_dilation, + is_new, + is_attachment, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn prim(&self) -> Primitive { + self.prim.clone() + } + pub const fn time_dilation(&self) -> u16 { + self.time_dilation + } + pub const fn is_new(&self) -> bool { + self.is_new + } + pub const fn is_attachment(&self) -> bool { + self.is_attachment + } +} + +#[derive(Clone)] +pub struct AvatarUpdateEventArgs { + simulator: Simulator, + avatar: Avatar, + time_dilation: u16, + is_new: bool, +} +impl AvatarUpdateEventArgs { + pub fn new( + simulator: Simulator, + avatar: Avatar, + time_dilation: u16, + is_new: bool, + ) -> Result { + Ok(Self { + simulator, + avatar, + time_dilation, + is_new, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn avatar(&self) -> Avatar { + self.avatar.clone() + } + pub const fn time_dilation(&self) -> u16 { + self.time_dilation + } + pub const fn is_new(&self) -> bool { + self.is_new + } +} + +#[derive(Clone)] +pub struct KillObjectEventArgs { + simulator: Simulator, + object_local_id: u32, +} +impl KillObjectEventArgs { + pub fn new(simulator: Simulator, object_local_id: u32) -> Result { + Ok(Self { + simulator, + object_local_id, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub const fn object_local_id(&self) -> u32 { + self.object_local_id + } +} + +#[derive(Clone)] +pub struct KillObjectsEventArgs { + simulator: Simulator, + object_local_i_ds: Vec, +} +impl KillObjectsEventArgs { + pub fn new(simulator: Simulator, object_local_i_ds: Vec) -> Result { + Ok(Self { + simulator, + object_local_i_ds, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn object_local_i_ds(&self) -> Vec { + self.object_local_i_ds.clone() + } +} + +#[derive(Clone)] +pub struct ObjectAnimationEventArgs { + simulator: Simulator, + object_id: UUID, + animations: Vec, +} +impl ObjectAnimationEventArgs { + pub fn new( + simulator: Simulator, + object_id: UUID, + animations: Vec, + ) -> Result { + Ok(Self { + simulator, + object_id, + animations, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub const fn object_id(&self) -> UUID { + self.object_id + } + pub fn animations(&self) -> Vec { + self.animations.clone() + } +} + +#[derive(Clone)] +pub struct ObjectPropertiesEventArgs { + simulator: Simulator, + properties: PrimitiveObjectProperties, +} +impl ObjectPropertiesEventArgs { + pub fn new(simulator: Simulator, properties: PrimitiveObjectProperties) -> Result { + Ok(Self { + simulator, + properties, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn properties(&self) -> PrimitiveObjectProperties { + self.properties.clone() + } +} + +#[derive(Clone)] +pub struct ObjectPropertiesFamilyEventArgs { + simulator: Simulator, + properties: PrimitiveObjectProperties, + type_: crate::ReportType, +} +impl ObjectPropertiesFamilyEventArgs { + pub fn new( + simulator: Simulator, + properties: PrimitiveObjectProperties, + type_: crate::ReportType, + ) -> Result { + Ok(Self { + simulator, + properties, + type_, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn properties(&self) -> PrimitiveObjectProperties { + self.properties.clone() + } + pub const fn type_(&self) -> crate::ReportType { + self.type_ + } +} + +#[derive(Clone)] +pub struct ObjectPropertiesUpdatedEventArgs { + simulator: Simulator, + prim: Primitive, + properties: PrimitiveObjectProperties, +} +impl ObjectPropertiesUpdatedEventArgs { + pub fn new( + simulator: Simulator, + prim: Primitive, + properties: PrimitiveObjectProperties, + ) -> Result { + Ok(Self { + simulator, + prim, + properties, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn prim(&self) -> Primitive { + self.prim.clone() + } + pub fn properties(&self) -> PrimitiveObjectProperties { + self.properties.clone() + } +} + +#[derive(Clone)] +pub struct ParticleUpdateEventArgs { + simulator: Simulator, + particle_system: PrimitiveParticleSystem, + source: Primitive, +} +impl ParticleUpdateEventArgs { + pub fn new( + simulator: Simulator, + particle_system: PrimitiveParticleSystem, + source: Primitive, + ) -> Result { + Ok(Self { + simulator, + particle_system, + source, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn particle_system(&self) -> PrimitiveParticleSystem { + self.particle_system.clone() + } + pub fn source(&self) -> Primitive { + self.source.clone() + } +} + +#[derive(Clone)] +pub struct PayPriceReplyEventArgs { + simulator: Simulator, + object_id: UUID, + default_price: i32, + button_prices: Vec, +} +impl PayPriceReplyEventArgs { + pub fn new( + simulator: Simulator, + object_id: UUID, + default_price: i32, + button_prices: Vec, + ) -> Result { + Ok(Self { + simulator, + object_id, + default_price, + button_prices, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub const fn object_id(&self) -> UUID { + self.object_id + } + pub const fn default_price(&self) -> i32 { + self.default_price + } + pub fn button_prices(&self) -> Vec { + self.button_prices.clone() + } +} + +#[derive(Clone)] +pub struct PhysicsPropertiesEventArgs { + pub simulator: Simulator, + pub physics_properties: PrimitivePhysicsProperties, +} +impl PhysicsPropertiesEventArgs { + pub fn new( + simulator: Simulator, + physics_properties: PrimitivePhysicsProperties, + ) -> Result { + Ok(Self { + simulator, + physics_properties, + }) + } +} + +#[derive(Clone)] +pub struct AvatarSitChangedEventArgs { + simulator: Simulator, + avatar: Avatar, + sitting_on: u32, + old_seat: u32, +} +impl AvatarSitChangedEventArgs { + pub fn new( + simulator: Simulator, + avatar: Avatar, + sitting_on: u32, + old_seat: u32, + ) -> Result { + Ok(Self { + simulator, + avatar, + sitting_on, + old_seat, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn avatar(&self) -> Avatar { + self.avatar.clone() + } + pub const fn sitting_on(&self) -> u32 { + self.sitting_on + } + pub const fn old_seat(&self) -> u32 { + self.old_seat + } +} + +#[derive(Clone)] +pub struct ObjectMovementUpdate { + pub acceleration: Vector3, + pub angular_velocity: Vector3, + pub avatar: bool, + pub collision_plane: Vector4, + pub local_id: u32, + pub position: Vector3, + pub rotation: Quaternion, + pub state: u8, + pub textures: PrimitiveTextureEntry, + pub velocity: Vector3, +} +impl Default for ObjectMovementUpdate { + fn default() -> Self { + Self { + acceleration: Vector3::zero(), + angular_velocity: Vector3::zero(), + avatar: false, + collision_plane: Vector4::zero(), + local_id: 0, + position: Vector3::zero(), + rotation: Quaternion::identity(), + state: 0, + textures: PrimitiveTextureEntry::new_with_uuid(UUID::zero()) + .expect("default texture entry"), + velocity: Vector3::zero(), + } + } +} + +#[derive(Clone)] +pub struct TerseObjectUpdateEventArgs { + simulator: Simulator, + prim: Primitive, + update: ObjectMovementUpdate, + time_dilation: u16, +} +impl TerseObjectUpdateEventArgs { + pub fn new( + simulator: Simulator, + prim: Primitive, + update: ObjectMovementUpdate, + time_dilation: u16, + ) -> Result { + Ok(Self { + simulator, + prim, + update, + time_dilation, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn prim(&self) -> Primitive { + self.prim.clone() + } + pub fn update(&self) -> ObjectMovementUpdate { + self.update.clone() + } + pub const fn time_dilation(&self) -> u16 { + self.time_dilation + } +} + +#[derive(Clone)] +pub struct ObjectDataBlockUpdateEventArgs { + simulator: Simulator, + prim: Primitive, + construction_data: PrimitiveConstructionData, + block: crate::packets::ObjectUpdatePacketObjectDataBlock, + update: ObjectMovementUpdate, + name_values: Vec, +} +impl ObjectDataBlockUpdateEventArgs { + pub fn new( + simulator: Simulator, + prim: Primitive, + construction_data: PrimitiveConstructionData, + block: crate::packets::ObjectUpdatePacketObjectDataBlock, + update: ObjectMovementUpdate, + name_values: Vec, + ) -> Result { + Ok(Self { + simulator, + prim, + construction_data, + block, + update, + name_values, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn prim(&self) -> Primitive { + self.prim.clone() + } + pub fn construction_data(&self) -> PrimitiveConstructionData { + self.construction_data.clone() + } + pub fn block(&self) -> crate::packets::ObjectUpdatePacketObjectDataBlock { + self.block.clone() + } + pub fn update(&self) -> ObjectMovementUpdate { + self.update.clone() + } + pub fn name_values(&self) -> Vec { + self.name_values.clone() + } +} + +#[derive(Default)] +pub(crate) struct ObjectEvents { + avatar_sit_changed: EventRegistry, + avatar_update: EventRegistry, + kill_object: EventRegistry, + kill_objects: EventRegistry, + object_animation: EventRegistry, + object_data_block_update: EventRegistry, + object_properties: EventRegistry, + object_properties_family: EventRegistry, + object_properties_updated: EventRegistry, + object_update: EventRegistry, + particle_update: EventRegistry, + pay_price_reply: EventRegistry, + physics_properties: EventRegistry, + terse_object_update: EventRegistry, +} +pub(crate) struct ObjectManagerInner { + client: crate::client_core::ClientWeakHandle, + events: ObjectEvents, + tracked: RwLock>>, + avatar_parents: RwLock>, + raw_subscription: Mutex>, + caps_subscription: Mutex>, + disposed: AtomicBool, +} + +impl ObjectManagerInner { + pub(crate) fn new(client: Option>) -> Result { + let client = client.ok_or(Error::ArgumentNull)?; + if let Some(inner) = client.cached_object_manager_inner() { + return Ok(ObjectManager { inner }); + } + let inner = Arc::new(Self { + client: client.native_weak_handle(), + events: ObjectEvents::default(), + tracked: RwLock::new(HashMap::new()), + avatar_parents: RwLock::new(HashMap::new()), + raw_subscription: Mutex::new(None), + caps_subscription: Mutex::new(None), + disposed: AtomicBool::new(false), + }); + let weak = Arc::downgrade(&inner); + *mutex(&inner.raw_subscription) = Some(client.network().subscribe_raw_packet(Arc::new( + move |event| { + if let Some(inner) = weak.upgrade() { + inner.handle_raw_packet(event); + } + }, + ))); + let weak = Arc::downgrade(&inner); + *mutex(&inner.caps_subscription) = Some(client.network().subscribe_caps( + "ObjectPhysicsProperties".into(), + crate::CapsEventQueueCallback::from_handler(move |_name, message, simulator| { + let Some(inner) = weak.upgrade() else { + return; + }; + let Ok(map) = message.serialize() else { + return; + }; + let Ok(mut decoded) = + crate::messages::linden::ObjectPhysicsPropertiesMessage::new() + else { + return; + }; + if decoded.deserialize(map).is_ok() { + inner.handle_physics_properties(decoded.object_physics_properties, simulator); + } + }), + )); + let inner = client.install_object_manager_inner(inner); + Ok(ObjectManager { inner }) + } + + pub(crate) fn client(&self) -> Result { + self.client.upgrade().ok_or(Error::InvalidOperation) + } + + fn handle_raw_packet(&self, event: RawPacketReceivedEventArgs) { + if self.disposed.load(Ordering::Acquire) { + return; + } + let _ = self.try_handle_raw_packet(event); + } + + fn try_handle_raw_packet(&self, event: RawPacketReceivedEventArgs) -> Result<(), Error> { + match event.packet_type { + crate::packets::PacketType::ObjectUpdate => { + self.handle_object_update(&event.data, event.simulator) + } + crate::packets::PacketType::ImprovedTerseObjectUpdate => { + self.handle_terse_update(&event.data, event.simulator) + } + crate::packets::PacketType::ObjectUpdateCompressed => { + self.handle_compressed_update(&event.data, event.simulator) + } + crate::packets::PacketType::KillObject => { + self.handle_kill_object(&event.data, event.simulator) + } + crate::packets::PacketType::ObjectProperties => { + self.handle_object_properties(&event.data, event.simulator) + } + crate::packets::PacketType::ObjectPropertiesFamily => { + self.handle_object_properties_family(&event.data, event.simulator) + } + crate::packets::PacketType::PayPriceReply => { + self.handle_pay_price_reply(&event.data, event.simulator) + } + crate::packets::PacketType::ObjectAnimation => { + self.handle_object_animation(&event.data, event.simulator) + } + _ => Ok(()), + } + } + + fn construction_data( + block: &crate::packets::ObjectUpdatePacketObjectDataBlock, + ) -> Result { + Ok(PrimitiveConstructionData { + state: block.state, + material: crate::object_model::material_from_i32(i32::from(block.material)), + path_curve: crate::object_model::path_curve_from_i32(i32::from(block.path_curve)), + profile_curve_with_field: block.profile_curve, + path_begin: Primitive::unpack_begin_cut(block.path_begin)?, + path_end: Primitive::unpack_end_cut(block.path_end)?, + path_scale_x: Primitive::unpack_path_scale(block.path_scale_x)?, + path_scale_y: Primitive::unpack_path_scale(block.path_scale_y)?, + path_shear_x: Primitive::unpack_path_shear(block.path_shear_x.cast_signed())?, + path_shear_y: Primitive::unpack_path_shear(block.path_shear_y.cast_signed())?, + path_twist: Primitive::unpack_path_twist(block.path_twist)?, + path_twist_begin: Primitive::unpack_path_twist(block.path_twist_begin)?, + path_radius_offset: Primitive::unpack_path_twist(block.path_radius_offset)?, + path_taper_x: Primitive::unpack_path_taper(block.path_taper_x)?, + path_taper_y: Primitive::unpack_path_taper(block.path_taper_y)?, + path_revolutions: Primitive::unpack_path_revolutions(block.path_revolutions)?, + path_skew: Primitive::unpack_path_twist(block.path_skew)?, + profile_begin: Primitive::unpack_begin_cut(block.profile_begin)?, + profile_end: Primitive::unpack_end_cut(block.profile_end)?, + profile_hollow: Primitive::unpack_profile_hollow(block.profile_hollow)?, + p_code: crate::object_model::pcode_from_i32(i32::from(block.p_code)), + }) + } + + fn handle_object_update(&self, bytes: &[u8], simulator: Simulator) -> Result<(), Error> { + let packet: crate::packets::ObjectUpdatePacket = decode_packet(bytes)?; + if packet.object_data.len() > MAX_PACKET_BLOCKS { + return Err(Error::Argument); + } + for block in packet.object_data { + let Ok(mut movement) = decode_movement(&block.object_data) else { + continue; + }; + let Ok(construction) = Self::construction_data(&block) else { + continue; + }; + let Ok((names, attachment_by_name)) = name_values(&block.name_value) else { + continue; + }; + movement.local_id = block.id; + movement.avatar = construction.p_code == PCode::Avatar; + movement.textures = if block.texture_entry.is_empty() { + PrimitiveTextureEntry::new_with_uuid(UUID::zero())? + } else { + match PrimitiveTextureEntry::new_with_bytes_int32_int32( + block.texture_entry.clone(), + 0, + i32::try_from(block.texture_entry.len()).map_err(|_| Error::Argument)?, + ) { + Ok(value) => value, + Err(_) => continue, + } + }; + + if construction.p_code == PCode::Avatar { + let is_new = !read(&simulator.objects_avatars).contains_key(&block.id); + let avatar = read(&simulator.objects_avatars) + .get(&block.id) + .cloned() + .unwrap_or(Avatar::new()?); + let mut avatar_prim = Primitive::new_with_constructor()?; + avatar_prim.id = block.full_id; + avatar_prim.local_id = block.id; + avatar_prim.parent_id = block.parent_id; + avatar_prim.region_handle = packet.region_data.region_handle; + avatar_prim.scale = block.scale; + avatar_prim.prim_data = construction.clone(); + avatar_prim.position = movement.position; + avatar_prim.velocity = movement.velocity; + avatar_prim.acceleration = movement.acceleration; + avatar_prim.rotation = movement.rotation; + avatar_prim.angular_velocity = movement.angular_velocity; + avatar_prim.collision_plane = movement.collision_plane; + avatar_prim.textures = Some(movement.textures.clone()); + avatar_prim.name_values = Some(names.clone()); + self.events + .object_data_block_update + .emit(ObjectDataBlockUpdateEventArgs::new( + simulator.clone(), + avatar_prim, + construction, + block.clone(), + movement.clone(), + names, + )?); + write(&simulator.objects_avatars).insert(block.id, avatar.clone()); + write(&simulator.global_to_local_id).insert(block.full_id, block.id); + let old_parent = write(&self.avatar_parents) + .insert( + (packet.region_data.region_handle, block.id), + block.parent_id, + ) + .unwrap_or(0); + if old_parent != block.parent_id { + self.events + .avatar_sit_changed + .emit(AvatarSitChangedEventArgs::new( + simulator.clone(), + avatar.clone(), + block.parent_id, + old_parent, + )?); + } + self.events.avatar_update.emit(AvatarUpdateEventArgs::new( + simulator.clone(), + avatar, + packet.region_data.time_dilation, + is_new, + )?); + continue; + } + if !matches!( + construction.p_code, + PCode::Prim | PCode::Grass | PCode::Tree | PCode::NewTree + ) { + continue; + } + + let (old, is_new) = { + let objects = read(&simulator.objects_primitives); + ( + objects.get(&block.id).cloned(), + !objects.contains_key(&block.id), + ) + }; + let old_id = old.as_ref().map(|value| value.id); + let mut prim = old.unwrap_or(Primitive::new_with_constructor()?); + self.events + .object_data_block_update + .emit(ObjectDataBlockUpdateEventArgs::new( + simulator.clone(), + prim.clone(), + construction.clone(), + block.clone(), + movement.clone(), + names.clone(), + )?); + + prim.flags = PrimFlags(block.update_flags); + if prim.flags.0 & PrimFlags::ZLIB_COMPRESSED.0 != 0 { + continue; + } + prim.local_id = block.id; + prim.id = block.full_id; + prim.parent_id = block.parent_id; + prim.region_handle = packet.region_data.region_handle; + prim.scale = block.scale; + prim.click_action = click_action(block.click_action); + prim.owner_id = block.owner_id; + prim.media_url = wire_string(&block.media_url)?; + prim.text = wire_string(&block.text)?; + if block.text_color.len() >= 4 { + prim.text_color = Color4::new_with_byte_byte_byte_byte( + block.text_color[0], + block.text_color[1], + block.text_color[2], + block.text_color[3], + )?; + } + prim.is_attachment = attachment_by_name && block.parent_id != 0; + prim.sound = block.sound; + prim.sound_flags = SoundFlags(block.flags); + prim.sound_gain = block.gain; + prim.sound_radius = block.radius; + prim.joint = joint_type(block.joint_type); + prim.joint_pivot = block.joint_pivot; + prim.joint_axis_or_anchor = block.joint_axis_or_anchor; + prim.prim_data = construction; + prim.textures = Some(movement.textures.clone()); + prim.texture_anim = + PrimitiveTextureAnimation::new(block.texture_anim.clone(), 0).unwrap_or_default(); + prim.particle_sys = + PrimitiveParticleSystem::new(block.ps_block.clone(), 0).unwrap_or_default(); + if !block.extra_params.is_empty() + && prim + .set_extra_params_from_bytes(block.extra_params.clone(), 0) + .is_err() + { + continue; + } + prim.name_values = Some(names); + prim.scratch_pad.clear(); + prim.collision_plane = movement.collision_plane; + prim.position = movement.position; + prim.velocity = movement.velocity; + prim.acceleration = movement.acceleration; + prim.rotation = movement.rotation; + prim.angular_velocity = movement.angular_velocity; + + { + let mut objects = write(&simulator.objects_primitives); + if is_new && objects.len() >= MAX_TRACKED_OBJECTS { + continue; + } + objects.insert(block.id, prim.clone()); + } + write(&simulator.global_to_local_id).insert(block.full_id, block.id); + if let Some(old_id) = old_id.filter(|old_id| *old_id != block.full_id) { + write(&simulator.global_to_local_id).remove(&old_id); + } + self.track(block.full_id, simulator.clone()); + self.events.object_update.emit(PrimEventArgs::new( + simulator.clone(), + prim.clone(), + packet.region_data.time_dilation, + is_new, + prim.is_attachment, + )?); + if prim.particle_sys.part_max_age != 0.0 { + self.events + .particle_update + .emit(ParticleUpdateEventArgs::new( + simulator.clone(), + prim.particle_sys.clone(), + prim, + )?); + } + } + Ok(()) + } + + fn track(&self, id: UUID, simulator: Simulator) { + let mut tracked = write(&self.tracked); + let simulators = tracked.entry(id).or_default(); + if !simulators.iter().any(|item| item == &simulator) { + simulators.push(simulator); + } + } + + fn handle_terse_update(&self, bytes: &[u8], simulator: Simulator) -> Result<(), Error> { + let packet: crate::packets::ImprovedTerseObjectUpdatePacket = decode_packet(bytes)?; + if packet.object_data.len() > MAX_PACKET_BLOCKS { + return Err(Error::Argument); + } + for block in packet.object_data { + let data = &block.data; + let minimum = if data.get(5).copied().unwrap_or_default() == 0 { + 44 + } else { + 60 + }; + if data.len() < minimum { + continue; + } + let local_id = u32_at(data, 0)?; + let avatar = data[5] != 0; + let mut p = 6; + let collision_plane = if avatar { + let value = vector4_at(data, p)?; + p += 16; + value + } else { + Vector4::zero() + }; + let position = vector3_at(data, p)?; + p += 12; + let q3 = |at, low, high| -> Result { + Ok(Vector3 { + x: quantized_u16(data, at, low, high)?, + y: quantized_u16(data, at + 2, low, high)?, + z: quantized_u16(data, at + 4, low, high)?, + }) + }; + let velocity = q3(p, -128.0, 128.0)?; + p += 6; + let acceleration = q3(p, -64.0, 64.0)?; + p += 6; + let rotation = Quaternion::new_with_single_single_single_single( + quantized_u16(data, p, -1.0, 1.0)?, + quantized_u16(data, p + 2, -1.0, 1.0)?, + quantized_u16(data, p + 4, -1.0, 1.0)?, + quantized_u16(data, p + 6, -1.0, 1.0)?, + )?; + p += 8; + let angular_velocity = q3(p, -64.0, 64.0)?; + let textures = if block.texture_entry.len() > 4 { + PrimitiveTextureEntry::new_with_bytes_int32_int32( + block.texture_entry.clone(), + 4, + i32::try_from(block.texture_entry.len() - 4).map_err(|_| Error::Argument)?, + )? + } else { + PrimitiveTextureEntry::new_with_uuid(UUID::zero())? + }; + let update = ObjectMovementUpdate { + acceleration, + angular_velocity, + avatar, + collision_plane, + local_id, + position, + rotation, + state: data[4], + textures, + velocity, + }; + if avatar { + continue; + } + let Some(mut prim) = read(&simulator.objects_primitives).get(&local_id).cloned() else { + continue; + }; + self.events + .terse_object_update + .emit(TerseObjectUpdateEventArgs::new( + simulator.clone(), + prim.clone(), + update.clone(), + packet.region_data.time_dilation, + )?); + prim.position = update.position; + prim.rotation = update.rotation; + prim.velocity = update.velocity; + prim.collision_plane = update.collision_plane; + prim.acceleration = update.acceleration; + prim.angular_velocity = update.angular_velocity; + prim.prim_data.state = update.state; + if !block.texture_entry.is_empty() { + prim.textures = Some(update.textures); + } + write(&simulator.objects_primitives).insert(local_id, prim); + } + Ok(()) + } + + fn handle_compressed_update(&self, bytes: &[u8], simulator: Simulator) -> Result<(), Error> { + let packet: crate::packets::ObjectUpdateCompressedPacket = decode_packet(bytes)?; + if packet.object_data.len() > MAX_PACKET_BLOCKS { + return Err(Error::Argument); + } + for block in packet.object_data { + let encoded_local_id = block + .data + .get(16..20) + .and_then(|bytes| <[u8; 4]>::try_from(bytes).ok().map(u32::from_le_bytes)); + let old_id = encoded_local_id.and_then(|local_id| { + read(&simulator.objects_primitives) + .get(&local_id) + .map(|prim| prim.id) + }); + let Ok((prim, is_new)) = Self::decode_compressed_prim( + &block.data, + block.update_flags, + packet.region_data.region_handle, + &simulator, + ) else { + continue; + }; + { + let mut objects = write(&simulator.objects_primitives); + if is_new && objects.len() >= MAX_TRACKED_OBJECTS { + continue; + } + objects.insert(prim.local_id, prim.clone()); + } + write(&simulator.global_to_local_id).insert(prim.id, prim.local_id); + if let Some(old_id) = old_id.filter(|old_id| *old_id != prim.id) { + write(&simulator.global_to_local_id).remove(&old_id); + } + self.track(prim.id, simulator.clone()); + self.events.object_update.emit(PrimEventArgs::new( + simulator.clone(), + prim.clone(), + packet.region_data.time_dilation, + is_new, + prim.is_attachment, + )?); + if prim.particle_sys.part_max_age != 0.0 { + self.events + .particle_update + .emit(ParticleUpdateEventArgs::new( + simulator.clone(), + prim.particle_sys.clone(), + prim, + )?); + } + } + Ok(()) + } + + fn decode_compressed_prim( + bytes: &[u8], + update_flags: u32, + region_handle: u64, + simulator: &Simulator, + ) -> Result<(Primitive, bool), Error> { + let mut cursor = Cursor::new(bytes)?; + let full_id = cursor.uuid()?; + let local_id = cursor.u32()?; + let p_code = crate::object_model::pcode_from_i32(i32::from(cursor.u8()?)); + if !matches!( + p_code, + PCode::Prim | PCode::Grass | PCode::Tree | PCode::NewTree + ) { + return Err(Error::Argument); + } + let (mut prim, is_new) = read(&simulator.objects_primitives) + .get(&local_id) + .cloned() + .map_or_else( + || Ok((Primitive::new_with_constructor()?, true)), + |value| Ok::<_, Error>((value, false)), + )?; + prim.id = full_id; + prim.local_id = local_id; + prim.region_handle = region_handle; + prim.flags = PrimFlags(update_flags); + prim.prim_data.p_code = p_code; + prim.prim_data.state = cursor.u8()?; + prim.crc = cursor.u32()?; + prim.prim_data.material = crate::object_model::material_from_i32(i32::from(cursor.u8()?)); + prim.click_action = click_action(cursor.u8()?); + prim.scale = cursor.vector3()?; + prim.position = cursor.vector3()?; + prim.rotation = cursor.quaternion3()?; + let flags = cursor.u32()?; + prim.owner_id = cursor.uuid()?; + if flags & crate::CompressedFlags::HAS_ANGULAR_VELOCITY.0 != 0 { + prim.angular_velocity = cursor.vector3()?; + } else { + prim.angular_velocity = Vector3::zero(); + } + prim.parent_id = if flags & crate::CompressedFlags::HAS_PARENT.0 != 0 { + cursor.u32()? + } else { + 0 + }; + if flags & crate::CompressedFlags::TREE.0 != 0 { + let _species = cursor.u8()?; + } else if flags & crate::CompressedFlags::SCRATCH_PAD.0 != 0 { + let length = usize::from(cursor.u8()?); + cursor.skip(length)?; + } + prim.scratch_pad.clear(); + if flags & crate::CompressedFlags::HAS_TEXT.0 != 0 { + prim.text = cursor.nul_string()?; + let color = cursor.take(4)?; + prim.text_color = + Color4::new_with_byte_byte_byte_byte(color[0], color[1], color[2], color[3])?; + } else { + prim.text.clear(); + } + if flags & crate::CompressedFlags::MEDIA_URL.0 != 0 { + prim.media_url = cursor.nul_string()?; + } else { + prim.media_url.clear(); + } + if flags & crate::CompressedFlags::HAS_PARTICLES.0 != 0 { + prim.particle_sys = PrimitiveParticleSystem::new(cursor.take(86)?.to_vec(), 0)?; + } else if flags & crate::CompressedFlags::HAS_PARTICLES_NEW.0 != 0 { + let length = usize::from(cursor.u8()?); + if length > usize::from(PrimitiveParticleSystem::MAX_DATA_BLOCK_SIZE) { + return Err(Error::Argument); + } + prim.particle_sys = PrimitiveParticleSystem::new(cursor.take(length)?.to_vec(), 0)?; + } else { + prim.particle_sys = PrimitiveParticleSystem::default(); + } + let consumed = prim.set_extra_params_from_bytes(cursor.remaining().to_vec(), 0)?; + cursor.skip(usize::try_from(consumed).map_err(|_| Error::Argument)?)?; + if flags & crate::CompressedFlags::HAS_SOUND.0 != 0 { + prim.sound = cursor.uuid()?; + prim.sound_gain = cursor.f32()?; + prim.sound_flags = SoundFlags(cursor.u8()?); + prim.sound_radius = cursor.f32()?; + } + if flags & crate::CompressedFlags::HAS_NAME_VALUES.0 != 0 { + let text = cursor.nul_string()?; + let (values, _) = name_values(text.as_bytes())?; + prim.name_values = Some(values); + } else { + prim.name_values = None; + } + prim.prim_data.path_curve = + crate::object_model::path_curve_from_i32(i32::from(cursor.u8()?)); + prim.prim_data.path_begin = Primitive::unpack_begin_cut(cursor.u16()?)?; + prim.prim_data.path_end = Primitive::unpack_end_cut(cursor.u16()?)?; + prim.prim_data.path_scale_x = Primitive::unpack_path_scale(cursor.u8()?)?; + prim.prim_data.path_scale_y = Primitive::unpack_path_scale(cursor.u8()?)?; + prim.prim_data.path_shear_x = Primitive::unpack_path_shear(cursor.i8()?)?; + prim.prim_data.path_shear_y = Primitive::unpack_path_shear(cursor.i8()?)?; + prim.prim_data.path_twist = Primitive::unpack_path_twist(cursor.i8()?)?; + prim.prim_data.path_twist_begin = Primitive::unpack_path_twist(cursor.i8()?)?; + prim.prim_data.path_radius_offset = Primitive::unpack_path_twist(cursor.i8()?)?; + prim.prim_data.path_taper_x = Primitive::unpack_path_taper(cursor.i8()?)?; + prim.prim_data.path_taper_y = Primitive::unpack_path_taper(cursor.i8()?)?; + prim.prim_data.path_revolutions = Primitive::unpack_path_revolutions(cursor.u8()?)?; + prim.prim_data.path_skew = Primitive::unpack_path_twist(cursor.i8()?)?; + prim.prim_data.profile_curve_with_field = cursor.u8()?; + prim.prim_data.profile_begin = Primitive::unpack_begin_cut(cursor.u16()?)?; + prim.prim_data.profile_end = Primitive::unpack_end_cut(cursor.u16()?)?; + prim.prim_data.profile_hollow = Primitive::unpack_profile_hollow(cursor.u16()?)?; + let texture_length = usize::try_from(cursor.u32()?).map_err(|_| Error::Argument)?; + if texture_length > MAX_FIELD_BYTES { + return Err(Error::Argument); + } + let textures = cursor.take(texture_length)?.to_vec(); + prim.textures = Some(PrimitiveTextureEntry::new_with_bytes_int32_int32( + textures, + 0, + i32::try_from(texture_length).map_err(|_| Error::Argument)?, + )?); + if flags & crate::CompressedFlags::TEXTURE_ANIMATION.0 != 0 { + let length = usize::try_from(cursor.u32()?).map_err(|_| Error::Argument)?; + if length != 16 { + return Err(Error::Argument); + } + prim.texture_anim = PrimitiveTextureAnimation::new(cursor.take(length)?.to_vec(), 0)?; + } else { + prim.texture_anim = PrimitiveTextureAnimation::default(); + } + if !cursor.remaining().is_empty() { + return Err(Error::Argument); + } + prim.is_attachment = + flags & crate::CompressedFlags::HAS_NAME_VALUES.0 != 0 && prim.parent_id != 0; + Ok((prim, is_new)) + } + + fn handle_kill_object(&self, bytes: &[u8], simulator: Simulator) -> Result<(), Error> { + let packet: crate::packets::KillObjectPacket = decode_packet(bytes)?; + if packet.object_data.len() > MAX_PACKET_BLOCKS { + return Err(Error::Argument); + } + let requested = packet + .object_data + .into_iter() + .map(|block| block.id) + .collect::>(); + for local_id in &requested { + self.events + .kill_object + .emit(KillObjectEventArgs::new(simulator.clone(), *local_id)?); + } + self.events.kill_objects.emit(KillObjectsEventArgs::new( + simulator.clone(), + requested.clone(), + )?); + + let objects = read(&simulator.objects_primitives); + let mut children: HashMap> = HashMap::new(); + for (local_id, prim) in objects.iter() { + if prim.parent_id != 0 { + children.entry(prim.parent_id).or_default().push(*local_id); + } + } + drop(objects); + let mut remove = HashSet::new(); + let mut stack = requested.clone(); + while let Some(local_id) = stack.pop() { + if !remove.insert(local_id) { + continue; + } + if let Some(descendants) = children.get(&local_id) { + stack.extend(descendants.iter().copied()); + } + } + let removed_ids = { + let mut objects = write(&simulator.objects_primitives); + remove + .iter() + .filter_map(|local_id| objects.remove(local_id).map(|prim| prim.id)) + .collect::>() + }; + { + let mut avatars = write(&simulator.objects_avatars); + for local_id in &remove { + avatars.remove(local_id); + } + } + { + let mut mappings = write(&simulator.global_to_local_id); + mappings.retain(|_, local_id| !remove.contains(local_id)); + } + let mut tracked = write(&self.tracked); + for id in removed_ids { + if let Some(simulators) = tracked.get_mut(&id) { + simulators.retain(|item| item != &simulator); + if simulators.is_empty() { + tracked.remove(&id); + } + } + } + Ok(()) + } + + fn properties_from_block( + block: &crate::packets::ObjectPropertiesPacketObjectDataBlock, + ) -> Result { + if block.texture_id.len() > MAX_FIELD_BYTES || !block.texture_id.len().is_multiple_of(16) { + return Err(Error::Argument); + } + let mut texture_i_ds = Vec::with_capacity(block.texture_id.len() / 16); + for bytes in block.texture_id.chunks_exact(16) { + texture_i_ds.push(UUID::new_with_bytes_int32(bytes.to_vec(), 0)?); + } + Ok(PrimitiveObjectProperties { + aggregate_perm_textures: block.aggregate_perm_textures, + aggregate_perm_textures_owner: block.aggregate_perm_textures_owner, + aggregate_perms: block.aggregate_perms, + category: object_category(block.category), + creation_date: UNIX_EPOCH + .checked_add(Duration::from_secs(block.creation_date)) + .ok_or(Error::Argument)?, + creator_id: block.creator_id, + description: wire_string(&block.description)?, + folder_id: block.folder_id, + from_task_id: block.from_task_id, + group_id: block.group_id, + inventory_serial: block.inventory_serial, + item_id: block.item_id, + last_owner_id: block.last_owner_id, + name: wire_string(&block.name)?, + object_id: block.object_id, + owner_id: block.owner_id, + ownership_cost: block.ownership_cost, + permissions: crate::Permissions::new( + block.base_mask, + block.everyone_mask, + block.group_mask, + block.next_owner_mask, + block.owner_mask, + )?, + sale_price: block.sale_price, + sale_type: sale_type(block.sale_type), + sit_name: wire_string(&block.sit_name)?, + texture_i_ds, + touch_name: wire_string(&block.touch_name)?, + }) + } + + fn handle_object_properties(&self, bytes: &[u8], simulator: Simulator) -> Result<(), Error> { + let packet: crate::packets::ObjectPropertiesPacket = decode_packet(bytes)?; + if packet.object_data.len() > MAX_PACKET_BLOCKS { + return Err(Error::Argument); + } + for block in packet.object_data { + let Ok(properties) = Self::properties_from_block(&block) else { + continue; + }; + let local_id = { + read(&simulator.global_to_local_id) + .get(&properties.object_id) + .copied() + }; + let primitive = local_id + .and_then(|local_id| read(&simulator.objects_primitives).get(&local_id).cloned()); + if let Some(mut prim) = primitive { + let local_id = prim.local_id; + self.events + .object_properties_updated + .emit(ObjectPropertiesUpdatedEventArgs::new( + simulator.clone(), + prim.clone(), + properties.clone(), + )?); + prim.properties = Some(properties.clone()); + write(&simulator.objects_primitives).insert(local_id, prim); + } + self.events + .object_properties + .emit(ObjectPropertiesEventArgs::new( + simulator.clone(), + properties, + )?); + } + Ok(()) + } + + fn handle_object_properties_family( + &self, + bytes: &[u8], + simulator: Simulator, + ) -> Result<(), Error> { + let packet: crate::packets::ObjectPropertiesFamilyPacket = decode_packet(bytes)?; + let block = packet.object_data; + let properties = PrimitiveObjectProperties { + category: object_category(block.category), + description: wire_string(&block.description)?, + group_id: block.group_id, + last_owner_id: block.last_owner_id, + name: wire_string(&block.name)?, + object_id: block.object_id, + owner_id: block.owner_id, + ownership_cost: block.ownership_cost, + permissions: crate::Permissions::new( + block.base_mask, + block.everyone_mask, + block.group_mask, + block.next_owner_mask, + block.owner_mask, + )?, + sale_price: block.sale_price, + sale_type: sale_type(block.sale_type), + ..PrimitiveObjectProperties::default() + }; + let local_id = { + read(&simulator.global_to_local_id) + .get(&properties.object_id) + .copied() + }; + let primitive = local_id + .and_then(|local_id| read(&simulator.objects_primitives).get(&local_id).cloned()); + if let Some(mut prim) = primitive { + let local_id = prim.local_id; + let mut current = prim.properties.unwrap_or_default(); + current.set_family_properties(properties.clone())?; + prim.properties = Some(current); + write(&simulator.objects_primitives).insert(local_id, prim); + } + self.events + .object_properties_family + .emit(ObjectPropertiesFamilyEventArgs::new( + simulator, + properties, + report_type(block.request_flags), + )?); + Ok(()) + } + + fn handle_pay_price_reply(&self, bytes: &[u8], simulator: Simulator) -> Result<(), Error> { + let packet: crate::packets::PayPriceReplyPacket = decode_packet(bytes)?; + if packet.button_data.len() > MAX_PACKET_BLOCKS { + return Err(Error::Argument); + } + self.events + .pay_price_reply + .emit(PayPriceReplyEventArgs::new( + simulator, + packet.object_data.object_id, + packet.object_data.default_pay_price, + packet + .button_data + .into_iter() + .map(|block| block.pay_button) + .collect(), + )?); + Ok(()) + } + + fn handle_object_animation(&self, bytes: &[u8], simulator: Simulator) -> Result<(), Error> { + let packet: crate::packets::ObjectAnimationPacket = decode_packet(bytes)?; + if packet.animation_list.len() > MAX_PACKET_BLOCKS { + return Err(Error::Argument); + } + let object_id = packet.sender.id; + let animations = packet + .animation_list + .into_iter() + .map(|block| Animation { + animation_id: block.anim_id, + animation_sequence: block.anim_sequence_id, + animation_source_object_id: object_id, + }) + .collect::>(); + let local_id = { read(&simulator.global_to_local_id).get(&object_id).copied() }; + let primitive = local_id + .and_then(|local_id| read(&simulator.objects_primitives).get(&local_id).cloned()); + if let Some(mut prim) = primitive { + let local_id = prim.local_id; + prim.signaled_animations = Some(animations.clone()); + write(&simulator.objects_primitives).insert(local_id, prim); + } + self.events + .object_animation + .emit(ObjectAnimationEventArgs::new( + simulator, object_id, animations, + )?); + Ok(()) + } + + fn handle_physics_properties( + &self, + properties: Vec, + simulator: Simulator, + ) { + if properties.len() > MAX_PACKET_BLOCKS { + return; + } + for property in properties { + let primitive = { + read(&simulator.objects_primitives) + .get(&property.local_id) + .cloned() + }; + if let Some(mut prim) = primitive { + prim.physics_props = Some(property.clone()); + write(&simulator.objects_primitives).insert(property.local_id, prim); + } + self.events + .physics_properties + .emit(PhysicsPropertiesEventArgs { + simulator: simulator.clone(), + physics_properties: property, + }); + } + } +} + +impl ObjectManager { + pub(crate) fn native_subscribe_avatar_sit_changed( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.avatar_sit_changed.subscribe(h) + } + pub(crate) fn native_subscribe_avatar_update( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.avatar_update.subscribe(h) + } + pub(crate) fn native_subscribe_kill_object( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.kill_object.subscribe(h) + } + pub(crate) fn native_subscribe_kill_objects( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.kill_objects.subscribe(h) + } + pub(crate) fn native_subscribe_object_animation( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.object_animation.subscribe(h) + } + pub(crate) fn native_subscribe_object_data_block_update( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.object_data_block_update.subscribe(h) + } + pub(crate) fn native_subscribe_object_properties( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.object_properties.subscribe(h) + } + pub(crate) fn native_subscribe_object_properties_family( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.object_properties_family.subscribe(h) + } + pub(crate) fn native_subscribe_object_properties_updated( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.object_properties_updated.subscribe(h) + } + pub(crate) fn native_subscribe_object_update( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.object_update.subscribe(h) + } + pub(crate) fn native_subscribe_particle_update( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.particle_update.subscribe(h) + } + pub(crate) fn native_subscribe_pay_price_reply( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.pay_price_reply.subscribe(h) + } + pub(crate) fn native_subscribe_physics_properties( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.physics_properties.subscribe(h) + } + pub(crate) fn native_subscribe_terse_object_update( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.terse_object_update.subscribe(h) + } + + fn auth(&self) -> Result<(UUID, UUID), Error> { + let network = self.inner.client()?.native_network()?; + Ok((network.native_agent_id(), network.native_session_id())) + } + + fn send( + &self, + simulator: &Simulator, + packet_type: crate::packets::PacketType, + packet: &P, + ) -> Result<(), Error> { + let data = packet.encode_packet()?; + let length = i32::try_from(data.len()).map_err(|_| Error::Argument)?; + let zerocoded = data + .first() + .is_some_and(|flags| flags & crate::Helpers::MSG_ZEROCODED != 0); + simulator.native_send_packet_data(data, length, packet_type, zerocoded) + } + + fn validate_ids(ids: &[u32]) -> Result<(), Error> { + if ids.is_empty() || ids.len() > 255 { + return Err(Error::Argument); + } + Ok(()) + } + + pub(crate) fn native_request_objects( + &self, + simulator: Simulator, + local_ids: Vec, + ) -> Result<(), Error> { + Self::validate_ids(&local_ids)?; + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::RequestMultipleObjectsPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.object_data = local_ids + .into_iter() + .map(|id| { + let mut block = crate::packets::RequestMultipleObjectsPacketObjectDataBlock::new_with_constructor()?; + block.id = id; + block.cache_miss_type = 0; + Ok(block) + }) + .collect::, Error>>()?; + self.send( + &simulator, + crate::packets::PacketType::RequestMultipleObjects, + &packet, + ) + } + + pub(crate) fn native_select_objects( + &self, + simulator: Simulator, + local_ids: Vec, + automatic_deselect: bool, + ) -> Result<(), Error> { + Self::validate_ids(&local_ids)?; + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectSelectPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.object_data = local_ids + .iter() + .map(|id| { + let mut block = + crate::packets::ObjectSelectPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = *id; + Ok(block) + }) + .collect::, Error>>()?; + self.send( + &simulator, + crate::packets::PacketType::ObjectSelect, + &packet, + )?; + if automatic_deselect { + self.native_deselect_objects(simulator, local_ids)?; + } + Ok(()) + } + + pub(crate) fn native_deselect_objects( + &self, + simulator: Simulator, + local_ids: Vec, + ) -> Result<(), Error> { + Self::validate_ids(&local_ids)?; + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectDeselectPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.object_data = local_ids + .into_iter() + .map(|id| { + let mut block = + crate::packets::ObjectDeselectPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = id; + Ok(block) + }) + .collect::, Error>>()?; + self.send( + &simulator, + crate::packets::PacketType::ObjectDeselect, + &packet, + ) + } + + pub(crate) fn native_link_objects( + &self, + simulator: Simulator, + local_ids: Vec, + link: bool, + ) -> Result<(), Error> { + Self::validate_ids(&local_ids)?; + let (agent_id, session_id) = self.auth()?; + if link { + let mut packet = crate::packets::ObjectLinkPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.object_data = local_ids + .into_iter() + .map(|id| { + let mut block = + crate::packets::ObjectLinkPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = id; + Ok(block) + }) + .collect::, Error>>()?; + self.send(&simulator, crate::packets::PacketType::ObjectLink, &packet) + } else { + let mut packet = crate::packets::ObjectDelinkPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.object_data = local_ids + .into_iter() + .map(|id| { + let mut block = + crate::packets::ObjectDelinkPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = id; + Ok(block) + }) + .collect::, Error>>()?; + self.send( + &simulator, + crate::packets::PacketType::ObjectDelink, + &packet, + ) + } + } + + pub(crate) fn native_request_pay_price( + &self, + simulator: Simulator, + object_id: UUID, + ) -> Result<(), Error> { + let mut packet = crate::packets::RequestPayPricePacket::new_with_constructor()?; + packet.object_data.object_id = object_id; + self.send( + &simulator, + crate::packets::PacketType::RequestPayPrice, + &packet, + ) + } + + pub(crate) fn native_request_properties_family( + &self, + simulator: Simulator, + object_id: UUID, + reliable: bool, + ) -> Result<(), Error> { + let (agent_id, session_id) = self.auth()?; + let mut packet = + crate::packets::RequestObjectPropertiesFamilyPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.object_data.object_id = object_id; + packet.object_data.request_flags = 0; + let _ = reliable; // This packet is reliable in the protocol template. + self.send( + &simulator, + crate::packets::PacketType::RequestObjectPropertiesFamily, + &packet, + ) + } + + pub(crate) fn native_set_names( + &self, + simulator: Simulator, + local_ids: Vec, + names: Vec, + ) -> Result<(), Error> { + Self::validate_ids(&local_ids)?; + if local_ids.len() != names.len() || names.iter().any(|name| name.len() > 255) { + return Err(Error::Argument); + } + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectNamePacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.object_data = local_ids + .into_iter() + .zip(names) + .map(|(id, name)| { + let mut block = + crate::packets::ObjectNamePacketObjectDataBlock::new_with_constructor()?; + block.local_id = id; + block.name = name.into_bytes(); + Ok(block) + }) + .collect::, Error>>()?; + self.send(&simulator, crate::packets::PacketType::ObjectName, &packet) + } + + pub(crate) fn native_set_descriptions( + &self, + simulator: Simulator, + local_ids: Vec, + descriptions: Vec, + ) -> Result<(), Error> { + Self::validate_ids(&local_ids)?; + if local_ids.len() != descriptions.len() + || descriptions + .iter() + .any(|description| description.len() > 255) + { + return Err(Error::Argument); + } + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectDescriptionPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.object_data = local_ids + .into_iter() + .zip(descriptions) + .map(|(id, description)| { + let mut block = + crate::packets::ObjectDescriptionPacketObjectDataBlock::new_with_constructor()?; + block.local_id = id; + block.description = description.into_bytes(); + Ok(block) + }) + .collect::, Error>>()?; + self.send( + &simulator, + crate::packets::PacketType::ObjectDescription, + &packet, + ) + } + + pub(crate) fn native_set_flags( + &self, + simulator: Simulator, + local_id: u32, + physical: bool, + temporary: bool, + phantom: bool, + casts_shadow: bool, + physics_type: libremetaverse_types::PhysicsShapeType, + density: f32, + friction: f32, + restitution: f32, + gravity_multiplier: f32, + ) -> Result<(), Error> { + if ![density, friction, restitution, gravity_multiplier] + .iter() + .all(|value| value.is_finite()) + { + return Err(Error::Argument); + } + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectFlagUpdatePacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.agent_data.object_local_id = local_id; + packet.agent_data.use_physics = physical; + packet.agent_data.is_temporary = temporary; + packet.agent_data.is_phantom = phantom; + packet.agent_data.casts_shadows = casts_shadow; + let mut block = + crate::packets::ObjectFlagUpdatePacketExtraPhysicsBlock::new_with_constructor()?; + block.physics_shape_type = physics_type as u8; + block.density = density; + block.friction = friction; + block.restitution = restitution; + block.gravity_multiplier = gravity_multiplier; + packet.extra_physics = vec![block]; + self.send( + &simulator, + crate::packets::PacketType::ObjectFlagUpdate, + &packet, + ) + } + + pub(crate) fn native_set_sale_info( + &self, + simulator: Simulator, + local_ids: Vec, + sale_type: SaleType, + price: i32, + ) -> Result<(), Error> { + Self::validate_ids(&local_ids)?; + if price < 0 { + return Err(Error::Argument); + } + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectSaleInfoPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.object_data = local_ids + .into_iter() + .map(|id| { + let mut block = + crate::packets::ObjectSaleInfoPacketObjectDataBlock::new_with_constructor()?; + block.local_id = id; + block.sale_type = sale_type as u8; + block.sale_price = price; + Ok(block) + }) + .collect::, Error>>()?; + self.send( + &simulator, + crate::packets::PacketType::ObjectSaleInfo, + &packet, + ) + } + + pub(crate) fn native_attach( + &self, + simulator: Simulator, + local_id: u32, + point: libremetaverse_types::AttachmentPoint, + rotation: Quaternion, + ) -> Result<(), Error> { + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectAttachPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.agent_data.attachment_point = point as u8; + let mut block = crate::packets::ObjectAttachPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = local_id; + block.rotation = rotation; + packet.object_data = vec![block]; + self.send( + &simulator, + crate::packets::PacketType::ObjectAttach, + &packet, + ) + } + + pub(crate) fn native_attachment_action( + &self, + simulator: Simulator, + local_ids: Vec, + drop: bool, + ) -> Result<(), Error> { + Self::validate_ids(&local_ids)?; + let (agent_id, session_id) = self.auth()?; + if drop { + let mut packet = crate::packets::ObjectDropPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.object_data = local_ids + .into_iter() + .map(|id| { + let mut block = + crate::packets::ObjectDropPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = id; + Ok(block) + }) + .collect::, Error>>()?; + self.send(&simulator, crate::packets::PacketType::ObjectDrop, &packet) + } else { + let mut packet = crate::packets::ObjectDetachPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.object_data = local_ids + .into_iter() + .map(|id| { + let mut block = + crate::packets::ObjectDetachPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = id; + Ok(block) + }) + .collect::, Error>>()?; + self.send( + &simulator, + crate::packets::PacketType::ObjectDetach, + &packet, + ) + } + } + + pub(crate) fn native_buy_object( + &self, + simulator: Simulator, + local_id: u32, + sale_type: SaleType, + price: i32, + group_id: UUID, + category_id: UUID, + ) -> Result<(), Error> { + if price < 0 { + return Err(Error::Argument); + } + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectBuyPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.agent_data.group_id = group_id; + packet.agent_data.category_id = category_id; + let mut block = crate::packets::ObjectBuyPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = local_id; + block.sale_type = sale_type as u8; + block.sale_price = price; + packet.object_data = vec![block]; + self.send(&simulator, crate::packets::PacketType::ObjectBuy, &packet) + } + + pub(crate) fn native_set_group( + &self, + simulator: Simulator, + local_ids: Vec, + group_id: UUID, + ) -> Result<(), Error> { + Self::validate_ids(&local_ids)?; + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectGroupPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.agent_data.group_id = group_id; + packet.object_data = local_ids + .into_iter() + .map(|id| { + let mut block = + crate::packets::ObjectGroupPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = id; + Ok(block) + }) + .collect::, Error>>()?; + self.send(&simulator, crate::packets::PacketType::ObjectGroup, &packet) + } + + pub(crate) fn native_set_permissions( + &self, + simulator: Simulator, + local_ids: Vec, + who: crate::PermissionWho, + permissions: crate::PermissionMask, + set: bool, + ) -> Result<(), Error> { + Self::validate_ids(&local_ids)?; + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectPermissionsPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.header_data.override_ = false; + packet.object_data = local_ids + .into_iter() + .map(|id| { + let mut block = + crate::packets::ObjectPermissionsPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = id; + block.field = who.0; + block.mask = permissions.0; + block.set = u8::from(set); + Ok(block) + }) + .collect::, Error>>()?; + self.send( + &simulator, + crate::packets::PacketType::ObjectPermissions, + &packet, + ) + } + + pub(crate) fn native_deed_objects( + &self, + simulator: Simulator, + local_ids: Vec, + group_id: UUID, + ) -> Result<(), Error> { + Self::validate_ids(&local_ids)?; + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectOwnerPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.header_data.override_ = false; + packet.header_data.owner_id = UUID::zero(); + packet.header_data.group_id = group_id; + packet.object_data = local_ids + .into_iter() + .map(|id| { + let mut block = + crate::packets::ObjectOwnerPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = id; + Ok(block) + }) + .collect::, Error>>()?; + self.send(&simulator, crate::packets::PacketType::ObjectOwner, &packet) + } + + #[allow(clippy::too_many_arguments)] + pub(crate) async fn native_click_object( + &self, + simulator: Simulator, + local_id: u32, + uv_coord: Vector3, + st_coord: Vector3, + face_index: i32, + position: Vector3, + normal: Vector3, + binormal: Vector3, + cancellation_token: Option, + ) -> Result<(), Error> { + let (agent_id, session_id) = self.auth()?; + let mut grab = crate::packets::ObjectGrabPacket::new_with_constructor()?; + grab.agent_data.agent_id = agent_id; + grab.agent_data.session_id = session_id; + grab.object_data.local_id = local_id; + grab.object_data.grab_offset = Vector3::zero(); + let mut surface = crate::packets::ObjectGrabPacketSurfaceInfoBlock::new_with_constructor()?; + surface.uv_coord = uv_coord; + surface.st_coord = st_coord; + surface.face_index = face_index; + surface.position = position; + surface.normal = normal; + surface.binormal = binormal; + grab.surface_info = vec![surface]; + self.send(&simulator, crate::packets::PacketType::ObjectGrab, &grab)?; + + if let Some(token) = cancellation_token { + tokio::select! { + () = tokio::time::sleep(Duration::from_millis(50)) => {} + () = token.cancelled() => {} + } + } else { + tokio::time::sleep(Duration::from_millis(50)).await; + } + + let mut degrab = crate::packets::ObjectDeGrabPacket::new_with_constructor()?; + degrab.agent_data.agent_id = agent_id; + degrab.agent_data.session_id = session_id; + degrab.object_data.local_id = local_id; + let mut surface = + crate::packets::ObjectDeGrabPacketSurfaceInfoBlock::new_with_constructor()?; + surface.uv_coord = uv_coord; + surface.st_coord = st_coord; + surface.face_index = face_index; + surface.position = position; + surface.normal = normal; + surface.binormal = binormal; + degrab.surface_info = vec![surface]; + self.send( + &simulator, + crate::packets::PacketType::ObjectDeGrab, + °rab, + ) + } + + pub(crate) fn native_add_prim( + &self, + simulator: Simulator, + prim: PrimitiveConstructionData, + group_id: UUID, + position: Vector3, + scale: Vector3, + rotation: Quaternion, + create_flags: PrimFlags, + ) -> Result<(), Error> { + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectAddPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.agent_data.group_id = group_id; + let data = &mut packet.object_data; + data.state = prim.state; + data.add_flags = create_flags.0; + data.p_code = PCode::Prim as u8; + data.material = prim.material as u8; + data.scale = scale; + data.rotation = rotation; + data.path_curve = prim.path_curve as u8; + data.path_begin = Primitive::pack_begin_cut(prim.path_begin)?; + data.path_end = Primitive::pack_end_cut(prim.path_end)?; + data.path_radius_offset = Primitive::pack_path_twist(prim.path_radius_offset)?; + data.path_revolutions = Primitive::pack_path_revolutions(prim.path_revolutions)?; + data.path_scale_x = Primitive::pack_path_scale(prim.path_scale_x)?; + data.path_scale_y = Primitive::pack_path_scale(prim.path_scale_y)?; + data.path_shear_x = Primitive::pack_path_shear(prim.path_shear_x)?.cast_unsigned(); + data.path_shear_y = Primitive::pack_path_shear(prim.path_shear_y)?.cast_unsigned(); + data.path_skew = Primitive::pack_path_twist(prim.path_skew)?; + data.path_taper_x = Primitive::pack_path_taper(prim.path_taper_x)?; + data.path_taper_y = Primitive::pack_path_taper(prim.path_taper_y)?; + data.path_twist = Primitive::pack_path_twist(prim.path_twist)?; + data.path_twist_begin = Primitive::pack_path_twist(prim.path_twist_begin)?; + data.profile_curve = prim.profile_curve_with_field; + data.profile_begin = Primitive::pack_begin_cut(prim.profile_begin)?; + data.profile_end = Primitive::pack_end_cut(prim.profile_end)?; + data.profile_hollow = Primitive::pack_profile_hollow(prim.profile_hollow)?; + data.ray_start = position; + data.ray_end = position; + data.ray_end_is_intersection = 0; + data.ray_target_id = UUID::zero(); + data.bypass_raycast = 1; + self.send(&simulator, crate::packets::PacketType::ObjectAdd, &packet) + } + + #[allow(clippy::too_many_arguments)] + pub(crate) fn native_add_foliage( + &self, + simulator: Simulator, + scale: Vector3, + rotation: Quaternion, + position: Vector3, + state: u8, + group_id: UUID, + p_code: PCode, + ) -> Result<(), Error> { + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectAddPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + packet.agent_data.group_id = group_id; + packet.object_data.bypass_raycast = 1; + packet.object_data.material = Material::Wood as u8; + packet.object_data.path_curve = PathCurve::Line as u8; + packet.object_data.p_code = p_code as u8; + packet.object_data.ray_end = position; + packet.object_data.ray_start = position; + packet.object_data.ray_target_id = UUID::zero(); + packet.object_data.rotation = rotation; + packet.object_data.scale = scale; + packet.object_data.state = state; + self.send(&simulator, crate::packets::PacketType::ObjectAdd, &packet) + } + + pub(crate) fn native_set_textures( + &self, + simulator: Simulator, + local_id: u32, + textures: PrimitiveTextureEntry, + media_url: String, + ) -> Result<(), Error> { + if media_url.len() > 255 { + return Err(Error::Argument); + } + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectImagePacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + let mut block = crate::packets::ObjectImagePacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = local_id; + block.texture_entry = textures.get_bytes()?; + block.media_url = media_url.into_bytes(); + packet.object_data = vec![block]; + self.send(&simulator, crate::packets::PacketType::ObjectImage, &packet) + } + + pub(crate) fn native_set_extra_param( + &self, + simulator: Simulator, + local_id: u32, + type_: libremetaverse_types::ExtraParamType, + in_use: bool, + data: Vec, + ) -> Result<(), Error> { + if data.len() > MAX_FIELD_BYTES { + return Err(Error::Argument); + } + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectExtraParamsPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + let mut block = + crate::packets::ObjectExtraParamsPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = local_id; + block.param_type = type_.0; + block.param_in_use = in_use; + block.param_size = u32::try_from(data.len()).map_err(|_| Error::Argument)?; + block.param_data = data; + packet.object_data = vec![block]; + self.send( + &simulator, + crate::packets::PacketType::ObjectExtraParams, + &packet, + ) + } + + pub(crate) fn native_set_sculpt( + &self, + simulator: Simulator, + local_id: u32, + sculpt: crate::PrimitiveSculptData, + ) -> Result<(), Error> { + self.native_set_extra_param( + simulator.clone(), + local_id, + libremetaverse_types::ExtraParamType::SCULPT, + true, + sculpt.get_bytes()?, + )?; + let shape = PrimitiveConstructionData { + path_curve: PathCurve::Circle, + path_scale_x: 1.0, + path_scale_y: 0.5, + ..PrimitiveConstructionData::default() + }; + self.native_set_shape(simulator, local_id, shape) + } + + pub(crate) fn native_set_shape( + &self, + simulator: Simulator, + local_id: u32, + prim: PrimitiveConstructionData, + ) -> Result<(), Error> { + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectShapePacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + let mut data = crate::packets::ObjectShapePacketObjectDataBlock::new_with_constructor()?; + data.object_local_id = local_id; + data.path_curve = prim.path_curve as u8; + data.path_begin = Primitive::pack_begin_cut(prim.path_begin)?; + data.path_end = Primitive::pack_end_cut(prim.path_end)?; + data.path_scale_x = Primitive::pack_path_scale(prim.path_scale_x)?; + data.path_scale_y = Primitive::pack_path_scale(prim.path_scale_y)?; + data.path_shear_x = Primitive::pack_path_shear(prim.path_shear_x)?.cast_unsigned(); + data.path_shear_y = Primitive::pack_path_shear(prim.path_shear_y)?.cast_unsigned(); + data.path_twist = Primitive::pack_path_twist(prim.path_twist)?; + data.path_twist_begin = Primitive::pack_path_twist(prim.path_twist_begin)?; + data.path_radius_offset = Primitive::pack_path_twist(prim.path_radius_offset)?; + data.path_taper_x = Primitive::pack_path_taper(prim.path_taper_x)?; + data.path_taper_y = Primitive::pack_path_taper(prim.path_taper_y)?; + data.path_revolutions = Primitive::pack_path_revolutions(prim.path_revolutions)?; + data.path_skew = Primitive::pack_path_twist(prim.path_skew)?; + data.profile_curve = prim.profile_curve_with_field; + data.profile_begin = Primitive::pack_begin_cut(prim.profile_begin)?; + data.profile_end = Primitive::pack_end_cut(prim.profile_end)?; + data.profile_hollow = Primitive::pack_profile_hollow(prim.profile_hollow)?; + packet.object_data = vec![data]; + self.send(&simulator, crate::packets::PacketType::ObjectShape, &packet) + } + + pub(crate) fn native_set_material( + &self, + simulator: Simulator, + local_id: u32, + material: Material, + ) -> Result<(), Error> { + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::ObjectMaterialPacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + let mut block = + crate::packets::ObjectMaterialPacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = local_id; + block.material = material as u8; + packet.object_data = vec![block]; + self.send( + &simulator, + crate::packets::PacketType::ObjectMaterial, + &packet, + ) + } + + pub(crate) fn native_basic_shape(type_: PrimType) -> Result { + let mut prim = PrimitiveConstructionData { + p_code: PCode::Prim, + material: Material::Wood, + ..PrimitiveConstructionData::default() + }; + let (profile, path, scale_y) = match type_ { + PrimType::Box => (ProfileCurve::Square, PathCurve::Line, 1.0), + PrimType::Cylinder => (ProfileCurve::Circle, PathCurve::Line, 1.0), + PrimType::Prism => (ProfileCurve::EqualTriangle, PathCurve::Line, 0.0), + PrimType::Ring => (ProfileCurve::EqualTriangle, PathCurve::Circle, 0.25), + PrimType::Sphere => (ProfileCurve::HalfCircle, PathCurve::Circle, 1.0), + PrimType::Torus => (ProfileCurve::Circle, PathCurve::Circle, 0.25), + PrimType::Tube => (ProfileCurve::Square, PathCurve::Circle, 0.25), + PrimType::Sculpt => (ProfileCurve::Circle, PathCurve::Circle, 0.5), + PrimType::Mesh | PrimType::Unknown => return Err(Error::Argument), + }; + prim.profile_curve_with_field = profile as u8; + prim.path_curve = path; + prim.path_scale_y = scale_y; + if type_ == PrimType::Prism { + prim.path_scale_x = 0.0; + } + Ok(prim) + } + + pub(crate) fn native_update_vector( + &self, + simulator: Simulator, + local_id: u32, + data: Vector3, + type_: crate::UpdateType, + ) -> Result<(), Error> { + self.native_update_bytes( + simulator, + local_id, + data.get_bytes()?, + u8::try_from(type_.0).map_err(|_| Error::Argument)?, + ) + } + + pub(crate) fn native_update_rotation( + &self, + simulator: Simulator, + local_id: u32, + rotation: Quaternion, + child_only: bool, + ) -> Result<(), Error> { + let mut type_ = crate::UpdateType::ROTATION.0; + if !child_only { + type_ |= crate::UpdateType::LINKED.0; + } + self.native_update_bytes( + simulator, + local_id, + rotation.get_bytes()?, + u8::try_from(type_).map_err(|_| Error::Argument)?, + ) + } + + fn native_update_bytes( + &self, + simulator: Simulator, + local_id: u32, + data: Vec, + type_: u8, + ) -> Result<(), Error> { + let (agent_id, session_id) = self.auth()?; + let mut packet = crate::packets::MultipleObjectUpdatePacket::new_with_constructor()?; + packet.agent_data.agent_id = agent_id; + packet.agent_data.session_id = session_id; + let mut block = + crate::packets::MultipleObjectUpdatePacketObjectDataBlock::new_with_constructor()?; + block.object_local_id = local_id; + block.type_ = type_; + block.data = data; + packet.object_data = vec![block]; + self.send( + &simulator, + crate::packets::PacketType::MultipleObjectUpdate, + &packet, + ) + } + + pub(crate) fn native_get_primitive( + &self, + simulator: Simulator, + local_id: u32, + full_id: UUID, + create_if_missing: bool, + ) -> Result { + if let Some(value) = read(&simulator.objects_primitives).get(&local_id).cloned() + && (full_id == UUID::zero() || value.id == full_id) + { + return Ok(value); + } + if !create_if_missing { + return Err(Error::InvalidOperation); + } + if read(&simulator.objects_primitives).len() >= MAX_TRACKED_OBJECTS { + return Err(Error::InvalidOperation); + } + let mut prim = Primitive::new_with_constructor()?; + prim.local_id = local_id; + prim.id = full_id; + write(&simulator.objects_primitives).insert(local_id, prim.clone()); + if full_id != UUID::zero() { + write(&simulator.global_to_local_id).insert(full_id, local_id); + self.inner.track(full_id, simulator); + } + Ok(prim) + } + + pub(crate) fn native_track(&self, object_id: UUID, simulator: Simulator) { + self.inner.track(object_id, simulator); + } + + pub(crate) fn native_untrack(&self, object_id: UUID, simulator: &Simulator) { + let mut tracked = write(&self.inner.tracked); + if let Some(simulators) = tracked.get_mut(&object_id) { + simulators.retain(|item| item != simulator); + if simulators.is_empty() { + tracked.remove(&object_id); + } + } + } + + pub(crate) fn native_simulators_for(&self, object_id: UUID) -> Vec { + read(&self.inner.tracked) + .get(&object_id) + .cloned() + .unwrap_or_default() + } + + pub(crate) fn native_cleanup_tracking(&self) { + write(&self.inner.tracked).retain(|_, simulators| { + simulators.retain(Simulator::connected); + !simulators.is_empty() + }); + } + + pub(crate) fn native_is_near_border( + position: Vector3, + region_size_x: u32, + region_size_y: u32, + threshold: Option, + ) -> Result { + let threshold = threshold.unwrap_or(32.0); + if !threshold.is_finite() || threshold < 0.0 { + return Err(Error::Argument); + } + Ok(position.x < threshold + || f64::from(position.x) > f64::from(region_size_x) - f64::from(threshold) + || position.y < threshold + || f64::from(position.y) > f64::from(region_size_y) - f64::from(threshold)) + } + + pub(crate) fn native_dispose(&self) { + if !self.inner.disposed.swap(true, Ordering::AcqRel) { + mutex(&self.inner.raw_subscription).take(); + mutex(&self.inner.caps_subscription).take(); + write(&self.inner.tracked).clear(); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::atomic::AtomicBool; + + fn context() -> (ObjectManager, Simulator) { + let client = GridClient::new().expect("client"); + let manager = ObjectManager::new(Some(Arc::new(client.clone()))).expect("object manager"); + let simulator = Simulator::new( + client, + "127.0.0.1:14069".parse().expect("endpoint"), + 69, + None, + None, + ) + .expect("simulator"); + (manager, simulator) + } + + #[test] + fn movement_decoding_accepts_all_reference_encodings_and_rejects_other_lengths() { + for length in [16, 32, 48, 60, 76] { + let update = decode_movement(&vec![0; length]).expect("valid movement size"); + assert!(update.position.x.is_finite()); + assert!(update.rotation.w.is_finite()); + } + for length in [0, 15, 17, 31, 33, 59, 61, 75, 77] { + assert!(decode_movement(&vec![0; length]).is_err()); + } + } + + #[test] + fn name_values_preserve_values_and_detect_attachments() { + let (values, attachment) = name_values( + b"AttachItemID STRING R S 01234567-89ab-cdef-0123-456789abcdef\nTitle STRING R S Hello world\0", + ) + .expect("name values"); + assert!(attachment); + assert_eq!(values.len(), 2); + assert_eq!(values[1].name, "Title"); + assert_eq!(values[1].value, Some("Hello world".into())); + } + + #[test] + fn malformed_terse_block_never_mutates_tracked_state() { + let (manager, simulator) = context(); + let id = UUID::random().expect("uuid"); + let mut prim = manager + .native_get_primitive(simulator.clone(), 7, id, true) + .expect("tracked primitive"); + prim.position = Vector3 { + x: 1.0, + y: 2.0, + z: 3.0, + }; + write(&simulator.objects_primitives).insert(7, prim.clone()); + + let mut packet = crate::packets::ImprovedTerseObjectUpdatePacket::new_with_constructor() + .expect("packet"); + let mut block = + crate::packets::ImprovedTerseObjectUpdatePacketObjectDataBlock::new_with_constructor() + .expect("block"); + block.data = vec![7, 0, 0]; + packet.object_data = vec![block]; + let bytes = packet.to_bytes_with_method().expect("wire packet"); + manager + .inner + .handle_terse_update(&bytes, simulator.clone()) + .expect("malformed block is isolated"); + assert_eq!( + read(&simulator.objects_primitives) + .get(&7) + .unwrap() + .position, + prim.position + ); + } + + #[test] + fn full_update_decodes_then_emits_preapply_and_postapply_events() { + let (manager, simulator) = context(); + let object_id = UUID::random().expect("uuid"); + let preapply_saw_absent = Arc::new(AtomicBool::new(false)); + let preapply = Arc::clone(&preapply_saw_absent); + let preapply_simulator = simulator.clone(); + let _pre_subscription = + manager.subscribe_object_data_block_update(Arc::new(move |event| { + if event.block().id == 21 { + preapply.store( + !read(&preapply_simulator.objects_primitives).contains_key(&21), + Ordering::Release, + ); + } + })); + let postapply_saw_present = Arc::new(AtomicBool::new(false)); + let postapply = Arc::clone(&postapply_saw_present); + let postapply_simulator = simulator.clone(); + let _post_subscription = manager.subscribe_object_update(Arc::new(move |event| { + if event.prim().local_id == 21 { + postapply.store( + read(&postapply_simulator.objects_primitives).contains_key(&21), + Ordering::Release, + ); + } + })); + + let mut packet = crate::packets::ObjectUpdatePacket::new_with_constructor().unwrap(); + packet.region_data.region_handle = 0x1000; + packet.region_data.time_dilation = 32_768; + let mut block = + crate::packets::ObjectUpdatePacketObjectDataBlock::new_with_constructor().unwrap(); + block.id = 21; + block.full_id = object_id; + block.p_code = PCode::Prim as u8; + block.object_data = vec![0; 60]; + block.scale = Vector3::one(); + packet.object_data = vec![block]; + manager + .inner + .handle_object_update( + &packet.to_bytes_with_method().expect("wire packet"), + simulator.clone(), + ) + .expect("object update"); + + assert!(preapply_saw_absent.load(Ordering::Acquire)); + assert!(postapply_saw_present.load(Ordering::Acquire)); + let prim = read(&simulator.objects_primitives) + .get(&21) + .cloned() + .unwrap(); + assert_eq!(prim.id, object_id); + assert_eq!(prim.region_handle, 0x1000); + assert_eq!( + read(&simulator.global_to_local_id).get(&object_id), + Some(&21) + ); + } + + #[test] + fn property_reply_correlates_uuid_and_updates_tracked_primitive() { + let (manager, simulator) = context(); + let object_id = UUID::random().expect("uuid"); + manager + .native_get_primitive(simulator.clone(), 22, object_id, true) + .expect("primitive"); + let observed = Arc::new(AtomicBool::new(false)); + let captured = Arc::clone(&observed); + let _subscription = manager.subscribe_object_properties_updated(Arc::new(move |event| { + captured.store( + event.prim().local_id == 22 && event.properties().name == "Native object", + Ordering::Release, + ); + })); + let mut packet = crate::packets::ObjectPropertiesPacket::new_with_constructor().unwrap(); + let mut block = + crate::packets::ObjectPropertiesPacketObjectDataBlock::new_with_constructor().unwrap(); + block.object_id = object_id; + block.name = b"Native object".to_vec(); + block.description = b"decoded".to_vec(); + packet.object_data = vec![block]; + manager + .inner + .handle_object_properties( + &packet.to_bytes_with_method().expect("wire packet"), + simulator.clone(), + ) + .expect("properties reply"); + assert!(observed.load(Ordering::Acquire)); + assert_eq!( + read(&simulator.objects_primitives) + .get(&22) + .and_then(|prim| prim.properties.as_ref()) + .map(|properties| properties.description.as_str()), + Some("decoded") + ); + } + + #[test] + fn kill_events_run_before_recursive_collection_removal() { + let (manager, simulator) = context(); + let root_id = UUID::random().expect("root uuid"); + let child_id = UUID::random().expect("child uuid"); + manager + .native_get_primitive(simulator.clone(), 10, root_id, true) + .expect("root"); + let mut child = manager + .native_get_primitive(simulator.clone(), 11, child_id, true) + .expect("child"); + child.parent_id = 10; + write(&simulator.objects_primitives).insert(11, child); + let observed_before_removal = Arc::new(AtomicBool::new(false)); + let observed = Arc::clone(&observed_before_removal); + let event_simulator = simulator.clone(); + let _subscription = manager.subscribe_kill_object(Arc::new(move |event| { + if event.object_local_id() == 10 { + observed.store( + read(&event_simulator.objects_primitives).contains_key(&10), + Ordering::Release, + ); + } + })); + let mut packet = crate::packets::KillObjectPacket::new_with_constructor().expect("packet"); + let mut block = + crate::packets::KillObjectPacketObjectDataBlock::new_with_constructor().expect("block"); + block.id = 10; + packet.object_data = vec![block]; + manager + .inner + .handle_kill_object( + &packet.to_bytes_with_method().expect("wire packet"), + simulator.clone(), + ) + .expect("kill handling"); + assert!(observed_before_removal.load(Ordering::Acquire)); + assert!(read(&simulator.objects_primitives).is_empty()); + assert!(!read(&simulator.global_to_local_id).contains_key(&root_id)); + assert!(!read(&simulator.global_to_local_id).contains_key(&child_id)); + } + + #[test] + fn border_detection_uses_both_region_dimensions() { + assert!( + ObjectManager::native_is_near_border( + Vector3 { + x: 128.0, + y: 250.0, + z: 0.0 + }, + 256, + 256, + Some(8.0), + ) + .unwrap() + ); + assert!( + !ObjectManager::native_is_near_border( + Vector3 { + x: 128.0, + y: 128.0, + z: 0.0 + }, + 256, + 256, + Some(8.0), + ) + .unwrap() + ); + assert!( + ObjectManager::native_is_near_border(Vector3::zero(), 256, 256, Some(-1.0)).is_err() + ); + } + + #[test] + fn grid_client_returns_one_owned_object_manager() { + let client = GridClient::new().expect("client"); + let first = client.objects(); + let second = client.objects(); + assert!(Arc::ptr_eq(&first.inner, &second.inner)); + } + + #[test] + fn concurrent_first_access_returns_one_owned_object_manager() { + let client = GridClient::new().expect("client"); + let barrier = Arc::new(std::sync::Barrier::new(9)); + let handles = (0..8) + .map(|_| { + let client = client.clone(); + let barrier = Arc::clone(&barrier); + std::thread::spawn(move || { + barrier.wait(); + ObjectManager::new(Some(Arc::new(client))) + .expect("object manager") + .inner + }) + }) + .collect::>(); + barrier.wait(); + let managers = handles + .into_iter() + .map(|handle| handle.join().expect("manager thread")) + .collect::>(); + + assert!( + managers + .iter() + .all(|inner| Arc::ptr_eq(inner, &managers[0])) + ); + assert!(Arc::ptr_eq(&client.objects().inner, &managers[0])); + } +} diff --git a/crates/libremetaverse/src/object_material.rs b/crates/libremetaverse/src/object_material.rs index a4d04b0..d136e18 100644 --- a/crates/libremetaverse/src/object_material.rs +++ b/crates/libremetaverse/src/object_material.rs @@ -7,13 +7,61 @@ )] use crate::assets::AssetMaterial; -use crate::{Error, GridClient, Simulator}; +use crate::{Error, GridClient, MediaEntry, Simulator}; +use flate2::Compression; +use flate2::read::ZlibDecoder; +use flate2::write::ZlibEncoder; use libremetaverse_structured_data::{OSD, OSDParser}; use libremetaverse_types::{UUID, compat::CancellationToken}; use std::collections::HashMap; +use std::io::{Read, Write}; use std::sync::Arc; use std::time::{Duration, Instant}; +#[derive(Clone, Debug)] +pub struct ObjectMediaEventArgs { + face_media: Vec, + success: bool, + version: String, +} + +impl ObjectMediaEventArgs { + pub fn new(success: bool, version: String, face_media: Vec) -> Result { + if face_media.len() > 45 || version.len() > 16 * 1024 { + return Err(Error::Argument); + } + Ok(Self { + face_media, + success, + version, + }) + } + + pub fn face_media(&self) -> Vec { + self.face_media.clone() + } + + pub fn set_face_media(&mut self, value: Vec) { + self.face_media = value; + } + + pub const fn success(&self) -> bool { + self.success + } + + pub fn set_success(&mut self, value: bool) { + self.success = value; + } + + pub fn version(&self) -> String { + self.version.clone() + } + + pub fn set_version(&mut self, value: String) { + self.version = value; + } +} + #[derive(Clone, Debug)] pub struct GLTFMaterialUpdate { asset_id: Option>, @@ -57,16 +105,280 @@ impl GLTFMaterialUpdate { } pub struct ObjectManager { - client: crate::client_core::ClientWeakHandle, + pub(crate) inner: Arc, } impl ObjectManager { pub(crate) fn native_new(client: Option>) -> Result { - Ok(Self { - client: client.ok_or(Error::ArgumentNull)?.native_weak_handle(), - }) + crate::object_manager::ObjectManagerInner::new(client) } fn client(&self) -> Result { - self.client.upgrade().ok_or(Error::InvalidOperation) + self.inner.client() + } + + fn capability( + sim: &Simulator, + name: &str, + ) -> Result, Error> { + sim.native_caps() + .map(|caps| caps.capability_uri(name.to_owned())) + .transpose() + .map(Option::flatten) + } + + async fn post_llsd( + &self, + uri: libremetaverse_types::compat::Uri, + body: OSD, + cancellation_token: Option, + ) -> Result<(bool, Vec), Error> { + let payload = OSDParser::serialize_llsd_xml_bytes(body)?; + let (response, bytes) = self + .client()? + .native_http_caps_client() + .post_with_uri_string_bytes_cancellation_token_i_progress( + uri, + "application/llsd+xml".into(), + payload, + cancellation_token.unwrap_or_default(), + None, + ) + .await?; + Ok(((200..300).contains(&response.status_code), bytes)) + } + + pub(crate) async fn native_navigate_object_media( + &self, + prim_id: UUID, + face: i32, + new_url: String, + sim: Simulator, + cancellation_token: Option, + ) -> Result<(), Error> { + if !(0..45).contains(&face) || new_url.len() > 16 * 1024 { + return Err(Error::Argument); + } + let Some(uri) = Self::capability(&sim, "ObjectMediaNavigate")? else { + return Err(Error::InvalidOperation); + }; + let (success, _) = self + .post_llsd( + uri, + OSD::Map(HashMap::from([ + ("current_url".into(), OSD::String(new_url)), + ("object_id".into(), OSD::UUID(prim_id)), + ("texture_index".into(), OSD::Integer(face)), + ])), + cancellation_token, + ) + .await?; + if success { + Ok(()) + } else { + Err(Error::HttpRequest) + } + } + + pub(crate) async fn native_update_object_media( + &self, + prim_id: UUID, + face_media: Vec, + sim: Simulator, + cancellation_token: Option, + ) -> Result<(), Error> { + if face_media.len() > 45 { + return Err(Error::Argument); + } + let Some(uri) = Self::capability(&sim, "ObjectMedia")? else { + return Err(Error::InvalidOperation); + }; + let media = face_media + .iter() + .map(|entry| { + if entry.is_undefined() { + Ok(OSD::Undefined) + } else { + Ok(OSD::Map(entry.get_osd()?.snapshot())) + } + }) + .collect::, Error>>()?; + let (success, _) = self + .post_llsd( + uri, + OSD::Map(HashMap::from([ + ("object_id".into(), OSD::UUID(prim_id)), + ("object_media_data".into(), OSD::Array(media)), + ("verb".into(), OSD::String("UPDATE".into())), + ])), + cancellation_token, + ) + .await?; + if !success { + return Err(Error::HttpRequest); + } + if let Some(local_id) = sim + .global_to_local_id + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get(&prim_id) + .copied() + { + let mut objects = sim + .objects_primitives + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(prim) = objects.get_mut(&local_id) { + prim.face_media = face_media; + } + } + Ok(()) + } + + pub(crate) async fn native_request_object_media( + &self, + prim_id: UUID, + sim: Simulator, + cancellation_token: Option, + ) -> Result<(bool, String, Option>), Error> { + let Some(uri) = Self::capability(&sim, "ObjectMedia")? else { + return Ok((false, String::new(), None)); + }; + let (success, bytes) = self + .post_llsd( + uri, + OSD::Map(HashMap::from([ + ("object_id".into(), OSD::UUID(prim_id)), + ("verb".into(), OSD::String("GET".into())), + ])), + cancellation_token, + ) + .await?; + if !success || bytes.is_empty() || bytes.len() > 16 * 1024 * 1024 { + return Ok((false, String::new(), None)); + } + let OSD::Map(map) = OSDParser::deserialize_with_bytes(bytes)? else { + return Ok((false, String::new(), None)); + }; + let version = map + .get("object_media_version") + .map(OSD::as_string) + .transpose()? + .unwrap_or_default(); + let media = match map.get("object_media_data") { + Some(OSD::Array(values)) if values.len() <= 45 => Some( + values + .iter() + .cloned() + .map(MediaEntry::from_osd) + .collect::, Error>>()?, + ), + Some(_) => return Err(Error::Argument), + None => None, + }; + if let Some(local_id) = sim + .global_to_local_id + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .get(&prim_id) + .copied() + { + let mut objects = sim + .objects_primitives + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(prim) = objects.get_mut(&local_id) { + prim.media_version.clone_from(&version); + prim.face_media = media.clone().unwrap_or_default(); + } + } + Ok((true, version, media)) + } + + fn decode_legacy_materials( + bytes: Vec, + ) -> Result, Error> { + if bytes.is_empty() || bytes.len() > 16 * 1024 * 1024 { + return Ok(Vec::new()); + } + let OSD::Map(map) = OSDParser::deserialize_with_bytes(bytes)? else { + return Err(Error::Argument); + }; + let compressed = map.get("Zipped").ok_or(Error::Argument)?.as_binary()?; + let mut binary = Vec::new(); + ZlibDecoder::new(compressed.as_slice()) + .take(16 * 1024 * 1024 + 1) + .read_to_end(&mut binary) + .map_err(|_| Error::Argument)?; + if binary.len() > 16 * 1024 * 1024 { + return Err(Error::Argument); + } + let decoded = OSDParser::deserialize_llsd_binary_with_bytes(binary)?; + let values = match decoded { + OSD::Array(values) => values, + OSD::Map(values) => values + .get("material_data") + .and_then(|value| match value { + OSD::Array(values) => Some(values.clone()), + _ => None, + }) + .unwrap_or_default(), + _ => return Err(Error::Argument), + }; + if values.len() > 65_535 { + return Err(Error::Argument); + } + values + .into_iter() + .map(|entry| match entry { + OSD::Map(map) => crate::materials::LegacyMaterial::new_with_osd_map( + libremetaverse_structured_data::OSDMap::new_with_dictionary(map)?, + ), + _ => Err(Error::Argument), + }) + .collect() + } + + pub(crate) async fn native_request_materials( + &self, + sim: Simulator, + materials: Option>, + cancellation_token: Option, + ) -> Result>, Error> { + let Some(uri) = Self::capability(&sim, "RenderMaterials")? else { + return Ok(Box::new(std::iter::empty())); + }; + let token = cancellation_token.unwrap_or_default(); + let (response, bytes) = if let Some(materials) = materials { + if materials.is_empty() || materials.len() > 65_535 { + return Err(Error::Argument); + } + let binary = OSDParser::serialize_llsd_binary_with_osd(OSD::Array( + materials.into_iter().map(OSD::UUID).collect(), + ))?; + let mut encoder = ZlibEncoder::new(Vec::new(), Compression::best()); + encoder.write_all(&binary).map_err(|_| Error::Argument)?; + let zipped = encoder.finish().map_err(|_| Error::Argument)?; + let body = OSD::Map(HashMap::from([("Zipped".into(), OSD::Binary(zipped))])); + let payload = OSDParser::serialize_llsd_xml_bytes(body)?; + self.client()? + .native_http_caps_client() + .post_with_uri_string_bytes_cancellation_token_i_progress( + uri, + "application/llsd+xml".into(), + payload, + token, + None, + ) + .await? + } else { + self.client()? + .native_http_caps_client() + .get(uri, token, None) + .await? + }; + if !(200..300).contains(&response.status_code) { + return Ok(Box::new(std::iter::empty())); + } + Ok(Box::new(Self::decode_legacy_materials(bytes)?.into_iter())) } pub(crate) async fn native_send_material_updates( @@ -190,3 +502,62 @@ impl ObjectManager { .await } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn legacy_material_capability_response_is_decoded() { + let id = UUID::random().unwrap(); + let material = OSD::Map(HashMap::from([ + ("ID".into(), OSD::UUID(id)), + ( + "Material".into(), + OSD::Map(HashMap::from([("SpecExp".into(), OSD::Integer(37))])), + ), + ])); + let binary = OSDParser::serialize_llsd_binary_with_osd(OSD::Array(vec![material])).unwrap(); + let mut encoder = ZlibEncoder::new(Vec::new(), Compression::best()); + encoder.write_all(&binary).unwrap(); + let response = OSDParser::serialize_llsd_xml_bytes(OSD::Map(HashMap::from([( + "Zipped".into(), + OSD::Binary(encoder.finish().unwrap()), + )]))) + .unwrap(); + + let decoded = ObjectManager::decode_legacy_materials(response).unwrap(); + assert_eq!(decoded.len(), 1); + assert_eq!(decoded[0].id(), id); + assert_eq!(decoded[0].specular_exponent(), 37); + } + + #[test] + fn legacy_material_capability_rejects_invalid_compression() { + let response = OSDParser::serialize_llsd_xml_bytes(OSD::Map(HashMap::from([( + "Zipped".into(), + OSD::Binary(vec![0xde, 0xad, 0xbe, 0xef]), + )]))) + .unwrap(); + + assert!(ObjectManager::decode_legacy_materials(response).is_err()); + } + + #[test] + fn object_media_event_args_preserve_response_values() { + let mut media = MediaEntry::new().unwrap(); + media.current_url = "https://example.invalid/media".into(); + let mut args = + ObjectMediaEventArgs::new(true, "x-mv:0000000001".into(), vec![media]).unwrap(); + + assert!(args.success()); + assert_eq!(args.version(), "x-mv:0000000001"); + assert_eq!(args.face_media().len(), 1); + args.set_success(false); + args.set_version("x-mv:0000000002".into()); + args.set_face_media(Vec::new()); + assert!(!args.success()); + assert_eq!(args.version(), "x-mv:0000000002"); + assert!(args.face_media().is_empty()); + } +} diff --git a/crates/libremetaverse/src/object_model.rs b/crates/libremetaverse/src/object_model.rs index af77998..163cd10 100644 --- a/crates/libremetaverse/src/object_model.rs +++ b/crates/libremetaverse/src/object_model.rs @@ -3022,7 +3022,7 @@ impl LegacyMaterial { } } -const fn profile_curve_from_u8(value: u8) -> ProfileCurve { +pub(crate) const fn profile_curve_from_u8(value: u8) -> ProfileCurve { match value { 1 => ProfileCurve::Square, 2 => ProfileCurve::IsoTriangle, @@ -3032,7 +3032,7 @@ const fn profile_curve_from_u8(value: u8) -> ProfileCurve { _ => ProfileCurve::Circle, } } -const fn material_from_i32(value: i32) -> Material { +pub(crate) const fn material_from_i32(value: i32) -> Material { match value { 1 => Material::Metal, 2 => Material::Glass, @@ -3044,7 +3044,7 @@ const fn material_from_i32(value: i32) -> Material { _ => Material::Stone, } } -const fn pcode_from_i32(value: i32) -> PCode { +pub(crate) const fn pcode_from_i32(value: i32) -> PCode { match value { 9 => PCode::Prim, 47 => PCode::Avatar, @@ -3055,7 +3055,7 @@ const fn pcode_from_i32(value: i32) -> PCode { _ => PCode::None, } } -const fn path_curve_from_i32(value: i32) -> PathCurve { +pub(crate) const fn path_curve_from_i32(value: i32) -> PathCurve { match value { 32 => PathCurve::Circle, 48 => PathCurve::Circle2, diff --git a/tools/generate_api_shims.py b/tools/generate_api_shims.py index deadfa1..30a2709 100644 --- a/tools/generate_api_shims.py +++ b/tools/generate_api_shims.py @@ -209,6 +209,22 @@ NATIVE_TYPES = { "T:LibreMetaverse.Primitive.TextureEntry": "crate::object_model::PrimitiveTextureEntry", "T:LibreMetaverse.Primitive.TextureEntryFace": "crate::object_model::PrimitiveTextureEntryFace", "T:LibreMetaverse.Materials.LegacyMaterial": "crate::object_model::LegacyMaterial", + "T:LibreMetaverse.ObjectMediaEventArgs": "crate::object_material::ObjectMediaEventArgs", + "T:LibreMetaverse.AvatarSitChangedEventArgs": "crate::object_manager::AvatarSitChangedEventArgs", + "T:LibreMetaverse.AvatarUpdateEventArgs": "crate::object_manager::AvatarUpdateEventArgs", + "T:LibreMetaverse.KillObjectEventArgs": "crate::object_manager::KillObjectEventArgs", + "T:LibreMetaverse.KillObjectsEventArgs": "crate::object_manager::KillObjectsEventArgs", + "T:LibreMetaverse.ObjectAnimationEventArgs": "crate::object_manager::ObjectAnimationEventArgs", + "T:LibreMetaverse.ObjectDataBlockUpdateEventArgs": "crate::object_manager::ObjectDataBlockUpdateEventArgs", + "T:LibreMetaverse.ObjectMovementUpdate": "crate::object_manager::ObjectMovementUpdate", + "T:LibreMetaverse.ObjectPropertiesEventArgs": "crate::object_manager::ObjectPropertiesEventArgs", + "T:LibreMetaverse.ObjectPropertiesFamilyEventArgs": "crate::object_manager::ObjectPropertiesFamilyEventArgs", + "T:LibreMetaverse.ObjectPropertiesUpdatedEventArgs": "crate::object_manager::ObjectPropertiesUpdatedEventArgs", + "T:LibreMetaverse.ParticleUpdateEventArgs": "crate::object_manager::ParticleUpdateEventArgs", + "T:LibreMetaverse.PayPriceReplyEventArgs": "crate::object_manager::PayPriceReplyEventArgs", + "T:LibreMetaverse.PhysicsPropertiesEventArgs": "crate::object_manager::PhysicsPropertiesEventArgs", + "T:LibreMetaverse.PrimEventArgs": "crate::object_manager::PrimEventArgs", + "T:LibreMetaverse.TerseObjectUpdateEventArgs": "crate::object_manager::TerseObjectUpdateEventArgs", "T:LibreMetaverse.ArchetypeParam": "crate::genepool_catalog::ArchetypeParam", "T:LibreMetaverse.AttentionData": "crate::attention_catalog::AttentionData", "T:LibreMetaverse.AttentionSet": "crate::attention_catalog::AttentionSet", @@ -342,6 +358,34 @@ NATIVE_DECLARATIONS = { } NATIVE_MEMBER_BODIES = { + "E:LibreMetaverse.ObjectManager.AvatarSitChanged": + "self.native_subscribe_avatar_sit_changed(handler)", + "E:LibreMetaverse.ObjectManager.AvatarUpdate": + "self.native_subscribe_avatar_update(handler)", + "E:LibreMetaverse.ObjectManager.KillObject": + "self.native_subscribe_kill_object(handler)", + "E:LibreMetaverse.ObjectManager.KillObjects": + "self.native_subscribe_kill_objects(handler)", + "E:LibreMetaverse.ObjectManager.ObjectAnimation": + "self.native_subscribe_object_animation(handler)", + "E:LibreMetaverse.ObjectManager.ObjectDataBlockUpdate": + "self.native_subscribe_object_data_block_update(handler)", + "E:LibreMetaverse.ObjectManager.ObjectProperties": + "self.native_subscribe_object_properties(handler)", + "E:LibreMetaverse.ObjectManager.ObjectPropertiesFamily": + "self.native_subscribe_object_properties_family(handler)", + "E:LibreMetaverse.ObjectManager.ObjectPropertiesUpdated": + "self.native_subscribe_object_properties_updated(handler)", + "E:LibreMetaverse.ObjectManager.ObjectUpdate": + "self.native_subscribe_object_update(handler)", + "E:LibreMetaverse.ObjectManager.ParticleUpdate": + "self.native_subscribe_particle_update(handler)", + "E:LibreMetaverse.ObjectManager.PayPriceReply": + "self.native_subscribe_pay_price_reply(handler)", + "E:LibreMetaverse.ObjectManager.PhysicsProperties": + "self.native_subscribe_physics_properties(handler)", + "E:LibreMetaverse.ObjectManager.TerseObjectUpdate": + "self.native_subscribe_terse_object_update(handler)", "M:LibreMetaverse.Helpers.TEGlowByte(System.Single)": "Ok(crate::object_model::te_glow_byte(glow))", "M:LibreMetaverse.Helpers.TEGlowFloat(System.Byte[],System.Int32)": @@ -909,9 +953,9 @@ NATIVE_MEMBER_BODIES = { "P:LibreMetaverse.GridClient.Assets#set": "self.native_set_assets(value)", "P:LibreMetaverse.GridClient.Objects": - "crate::object_material::ObjectManager::native_new(Some(std::sync::Arc::new(self.clone()))).unwrap_or_else(|_| panic!(\"failed to construct ObjectManager\"))", + "self.native_objects().unwrap_or_else(|_| panic!(\"failed to construct ObjectManager\"))", "P:LibreMetaverse.GridClient.Objects#set": - "{ let _ = value; }", + "self.native_set_objects(value)", "P:LibreMetaverse.GridClient.Inventory": "self.native_inventory().unwrap_or_else(|_| panic!(\"failed to construct InventoryManager\"))", "P:LibreMetaverse.GridClient.Inventory#set": @@ -954,6 +998,130 @@ NATIVE_MEMBER_BODIES = { "crate::inventory_ais::InventoryAISClient::native_new(client)", "M:LibreMetaverse.ObjectManager.#ctor(LibreMetaverse.GridClient)": "crate::object_material::ObjectManager::native_new(client)", + "M:LibreMetaverse.ObjectManager.CleanupObjectTracking": + "{ self.native_cleanup_tracking(); Ok(()) }", + "M:LibreMetaverse.ObjectManager.Dispose": + "{ self.native_dispose(); Ok(()) }", + "M:LibreMetaverse.ObjectManager.RequestObject(LibreMetaverse.Simulator,System.UInt32)": + "self.native_request_objects(simulator, vec![local_id])", + "M:LibreMetaverse.ObjectManager.RequestObjects(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})": + "self.native_request_objects(simulator, local_i_ds)", + "M:LibreMetaverse.ObjectManager.SelectObject(LibreMetaverse.Simulator,System.UInt32)": + "self.native_select_objects(simulator, vec![local_id], true)", + "M:LibreMetaverse.ObjectManager.SelectObject(LibreMetaverse.Simulator,System.UInt32,System.Boolean)": + "self.native_select_objects(simulator, vec![local_id], automatic_deselect)", + "M:LibreMetaverse.ObjectManager.SelectObjects(LibreMetaverse.Simulator,System.UInt32[])": + "self.native_select_objects(simulator, local_i_ds, true)", + "M:LibreMetaverse.ObjectManager.SelectObjects(LibreMetaverse.Simulator,System.UInt32[],System.Boolean)": + "self.native_select_objects(simulator, local_i_ds, automatic_deselect)", + "M:LibreMetaverse.ObjectManager.DeselectObject(LibreMetaverse.Simulator,System.UInt32)": + "self.native_deselect_objects(simulator, vec![local_id])", + "M:LibreMetaverse.ObjectManager.DeselectObjects(LibreMetaverse.Simulator,System.UInt32[])": + "self.native_deselect_objects(simulator, local_i_ds)", + "M:LibreMetaverse.ObjectManager.LinkPrims(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})": + "self.native_link_objects(simulator, local_i_ds, true)", + "M:LibreMetaverse.ObjectManager.DelinkPrims(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})": + "self.native_link_objects(simulator, local_i_ds, false)", + "M:LibreMetaverse.ObjectManager.RequestPayPrice(LibreMetaverse.Simulator,LibreMetaverse.UUID)": + "self.native_request_pay_price(simulator, object_id)", + "M:LibreMetaverse.ObjectManager.RequestObjectPropertiesFamily(LibreMetaverse.Simulator,LibreMetaverse.UUID)": + "self.native_request_properties_family(simulator, object_id, true)", + "M:LibreMetaverse.ObjectManager.RequestObjectPropertiesFamily(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Boolean)": + "self.native_request_properties_family(simulator, object_id, reliable)", + "M:LibreMetaverse.ObjectManager.SetName(LibreMetaverse.Simulator,System.UInt32,System.String)": + "self.native_set_names(simulator, vec![local_id], vec![name])", + "M:LibreMetaverse.ObjectManager.SetNames(LibreMetaverse.Simulator,System.UInt32[],System.String[])": + "self.native_set_names(simulator, local_i_ds, names)", + "M:LibreMetaverse.ObjectManager.SetDescription(LibreMetaverse.Simulator,System.UInt32,System.String)": + "self.native_set_descriptions(simulator, vec![local_id], vec![description])", + "M:LibreMetaverse.ObjectManager.SetDescriptions(LibreMetaverse.Simulator,System.UInt32[],System.String[])": + "self.native_set_descriptions(simulator, local_i_ds, descriptions)", + "M:LibreMetaverse.ObjectManager.UpdateObject(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3,LibreMetaverse.UpdateType)": + "self.native_update_vector(simulator, local_id, data, type_)", + "M:LibreMetaverse.ObjectManager.SetPosition(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3)": + "self.native_update_vector(simulator, local_id, position, crate::UpdateType(crate::UpdateType::POSITION.0 | crate::UpdateType::LINKED.0))", + "M:LibreMetaverse.ObjectManager.SetPosition(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3,System.Boolean)": + "self.native_update_vector(simulator, local_id, position, crate::UpdateType(crate::UpdateType::POSITION.0 | if child_only { 0 } else { crate::UpdateType::LINKED.0 }))", + "M:LibreMetaverse.ObjectManager.SetScale(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3,System.Boolean,System.Boolean)": + "self.native_update_vector(simulator, local_id, scale, crate::UpdateType(crate::UpdateType::SCALE.0 | if child_only { 0 } else { crate::UpdateType::LINKED.0 } | if uniform { crate::UpdateType::UNIFORM.0 } else { 0 }))", + "M:LibreMetaverse.ObjectManager.SetRotation(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Quaternion)": + "self.native_update_rotation(simulator, local_id, rotation, false)", + "M:LibreMetaverse.ObjectManager.SetRotation(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Quaternion,System.Boolean)": + "self.native_update_rotation(simulator, local_id, quat, child_only)", + "M:LibreMetaverse.ObjectManager.GetPrimitive(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.UUID,System.Boolean)": + "self.native_get_primitive(simulator, local_id, full_id, create_if_missing)", + "M:LibreMetaverse.ObjectManager.TrackObjectInSimulator(LibreMetaverse.UUID,LibreMetaverse.Simulator)": + "{ self.native_track(object_id, sim); Ok(()) }", + "M:LibreMetaverse.ObjectManager.UntrackObjectInSimulator(LibreMetaverse.UUID,LibreMetaverse.Simulator)": + "{ self.native_untrack(object_id, &sim); Ok(()) }", + "M:LibreMetaverse.ObjectManager.GetSimulatorsForObject(LibreMetaverse.UUID)": + "Ok(self.native_simulators_for(object_id))", + "M:LibreMetaverse.ObjectManager.IsNearBorder(LibreMetaverse.Vector3,System.UInt32,System.UInt32,System.Single)": + "crate::object_material::ObjectManager::native_is_near_border(position, region_size_x, region_size_y, threshold)", + "M:LibreMetaverse.ObjectManager.BuyObject(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.SaleType,System.Int32,LibreMetaverse.UUID,LibreMetaverse.UUID)": + "self.native_buy_object(simulator, local_id, sale_type, price, group_id, category_id)", + "M:LibreMetaverse.ObjectManager.AttachObject(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.AttachmentPoint,LibreMetaverse.Quaternion)": + "self.native_attach(simulator, local_id, attach_point, rotation)", + "M:LibreMetaverse.ObjectManager.DropObject(LibreMetaverse.Simulator,System.UInt32)": + "self.native_attachment_action(simulator, vec![local_id], true)", + "M:LibreMetaverse.ObjectManager.DetachObjects(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32})": + "self.native_attachment_action(simulator, local_i_ds, false)", + "M:LibreMetaverse.ObjectManager.SetObjectsGroup(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.UUID)": + "self.native_set_group(simulator, local_ids, group_id)", + "M:LibreMetaverse.ObjectManager.SetPermissions(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.PermissionWho,LibreMetaverse.PermissionMask,System.Boolean)": + "self.native_set_permissions(simulator, local_i_ds, who, permissions, set)", + "M:LibreMetaverse.ObjectManager.DeedObject(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.UUID)": + "self.native_deed_objects(simulator, vec![local_id], group_owner)", + "M:LibreMetaverse.ObjectManager.DeedObjects(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.UUID)": + "self.native_deed_objects(simulator, local_i_ds, group_owner)", + "M:LibreMetaverse.ObjectManager.SetSaleInfo(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},LibreMetaverse.SaleType,System.Int32)": + "self.native_set_sale_info(simulator, local_i_ds, sale_type, price)", + "M:LibreMetaverse.ObjectManager.SetSaleInfo(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.SaleType,System.Int32)": + "self.native_set_sale_info(simulator, vec![local_id], sale_type, price)", + "M:LibreMetaverse.ObjectManager.SetFlags(LibreMetaverse.Simulator,System.UInt32,System.Boolean,System.Boolean,System.Boolean,System.Boolean)": + "self.native_set_flags(simulator, local_id, physical, temporary, phantom, casts_shadow, libremetaverse_types::PhysicsShapeType::Prim, 1000.0, 0.6, 0.5, 1.0)", + "M:LibreMetaverse.ObjectManager.SetFlags(LibreMetaverse.Simulator,System.UInt32,System.Boolean,System.Boolean,System.Boolean,System.Boolean,LibreMetaverse.PhysicsShapeType,System.Single,System.Single,System.Single,System.Single)": + "self.native_set_flags(simulator, local_id, physical, temporary, phantom, casts_shadow, physics_type, density, friction, restitution, gravity_multiplier)", + "M:LibreMetaverse.ObjectManager.ClickObjectAsync(LibreMetaverse.Simulator,System.UInt32,System.Threading.CancellationToken)": + "self.native_click_object(simulator, local_id, libremetaverse_types::Vector3::zero(), libremetaverse_types::Vector3::zero(), 0, libremetaverse_types::Vector3::zero(), libremetaverse_types::Vector3::zero(), libremetaverse_types::Vector3::zero(), cancellation_token).await", + "M:LibreMetaverse.ObjectManager.ClickObjectAsync(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Vector3,LibreMetaverse.Vector3,System.Int32,LibreMetaverse.Vector3,LibreMetaverse.Vector3,LibreMetaverse.Vector3,System.Threading.CancellationToken)": + "self.native_click_object(simulator, local_id, uv_coord, st_coord, face_index, position, normal, binormal, cancellation_token).await", + "M:LibreMetaverse.ObjectManager.AddPrim(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ConstructionData,LibreMetaverse.UUID,LibreMetaverse.Vector3,LibreMetaverse.Vector3,LibreMetaverse.Quaternion)": + "self.native_add_prim(simulator, prim, group_id, position, scale, rotation, libremetaverse_types::PrimFlags::CREATE_SELECTED)", + "M:LibreMetaverse.ObjectManager.AddPrim(LibreMetaverse.Simulator,LibreMetaverse.Primitive.ConstructionData,LibreMetaverse.UUID,LibreMetaverse.Vector3,LibreMetaverse.Vector3,LibreMetaverse.Quaternion,LibreMetaverse.PrimFlags)": + "self.native_add_prim(simulator, prim, group_id, position, scale, rotation, create_flags)", + "M:LibreMetaverse.ObjectManager.AddTree(LibreMetaverse.Simulator,LibreMetaverse.Vector3,LibreMetaverse.Quaternion,LibreMetaverse.Vector3,LibreMetaverse.Tree,LibreMetaverse.UUID,System.Boolean)": + "self.native_add_foliage(simulator, scale, rotation, position, tree_type as u8, group_owner, if new_tree { libremetaverse_types::PCode::NewTree } else { libremetaverse_types::PCode::Tree })", + "M:LibreMetaverse.ObjectManager.AddGrass(LibreMetaverse.Simulator,LibreMetaverse.Vector3,LibreMetaverse.Quaternion,LibreMetaverse.Vector3,LibreMetaverse.Grass,LibreMetaverse.UUID)": + "self.native_add_foliage(simulator, scale, rotation, position, grass_type as u8, group_owner, libremetaverse_types::PCode::Grass)", + "M:LibreMetaverse.ObjectManager.BuildBasicShape(LibreMetaverse.PrimType)": + "crate::object_material::ObjectManager::native_basic_shape(type_)", + "M:LibreMetaverse.ObjectManager.SetTextures(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.TextureEntry)": + "self.native_set_textures(simulator, local_id, textures, String::new())", + "M:LibreMetaverse.ObjectManager.SetTextures(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.TextureEntry,System.String)": + "self.native_set_textures(simulator, local_id, textures, media_url)", + "M:LibreMetaverse.ObjectManager.SetLight(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.LightData)": + "{ let in_use = light.intensity != 0.0; let data = light.get_bytes()?; self.native_set_extra_param(simulator, local_id, libremetaverse_types::ExtraParamType::LIGHT, in_use, data) }", + "M:LibreMetaverse.ObjectManager.SetFlexible(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.FlexibleData)": + "{ let data = flexible.get_bytes()?; self.native_set_extra_param(simulator, local_id, libremetaverse_types::ExtraParamType::FLEXIBLE, true, data) }", + "M:LibreMetaverse.ObjectManager.SetSculpt(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.SculptData)": + "self.native_set_sculpt(simulator, local_id, sculpt)", + "M:LibreMetaverse.ObjectManager.SetExtraParamOff(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.ExtraParamType)": + "self.native_set_extra_param(simulator, local_id, type_, false, Vec::new())", + "M:LibreMetaverse.ObjectManager.SetShape(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Primitive.ConstructionData)": + "self.native_set_shape(simulator, local_id, prim)", + "M:LibreMetaverse.ObjectManager.SetMaterial(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.Material)": + "self.native_set_material(simulator, local_id, material)", + "M:LibreMetaverse.ObjectManager.NavigateObjectMediaAsync(LibreMetaverse.UUID,System.Int32,System.String,LibreMetaverse.Simulator,System.Threading.CancellationToken)": + "self.native_navigate_object_media(prim_id, face, new_url, sim, cancellation_token).await", + "M:LibreMetaverse.ObjectManager.UpdateObjectMediaAsync(LibreMetaverse.UUID,LibreMetaverse.MediaEntry[],LibreMetaverse.Simulator,System.Threading.CancellationToken)": + "self.native_update_object_media(prim_id, face_media, sim, cancellation_token).await", + "M:LibreMetaverse.ObjectManager.RequestObjectMediaAsync(LibreMetaverse.UUID,LibreMetaverse.Simulator,System.Threading.CancellationToken)": + "self.native_request_object_media(prim_id, sim, cancellation_token).await", + "M:LibreMetaverse.ObjectManager.RequestMaterialsAsync(LibreMetaverse.Simulator,System.Threading.CancellationToken)": + "self.native_request_materials(sim, None, cancellation_token).await", + "M:LibreMetaverse.ObjectManager.RequestMaterialsAsync(LibreMetaverse.Simulator,System.Collections.Generic.IEnumerable{LibreMetaverse.UUID},System.Threading.CancellationToken)": + "self.native_request_materials(sim, Some(materials.collect()), cancellation_token).await", "M:LibreMetaverse.ObjectManager.SendMaterialUpdatesAsync(LibreMetaverse.Simulator,System.Collections.Generic.IEnumerable{LibreMetaverse.GLTFMaterialUpdate},System.Threading.CancellationToken)": "self.native_send_material_updates(sim, updates, cancellation_token).await", "M:LibreMetaverse.ObjectManager.SetMaterialOverrideAsync(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Int32,LibreMetaverse.Assets.AssetMaterial,System.Threading.CancellationToken)": @@ -2427,6 +2595,11 @@ PRIVATE_LAYOUTS = { "T:LibreMetaverse.StructuredData.OSDUri": [("value", "libremetaverse_types::compat::Uri")], } VALUE_DERIVES = { + "T:LibreMetaverse.Avatar": "Clone", + "T:LibreMetaverse.Avatar.Attachment": "Clone", + "T:LibreMetaverse.Avatar.AvatarProperties": "Clone", + "T:LibreMetaverse.Avatar.Interests": "Clone", + "T:LibreMetaverse.Avatar.Statistics": "Clone", "T:LibreMetaverse.AgentSettings": "Clone, Debug, Eq, PartialEq", "T:LibreMetaverse.AppearanceManager.WearableData": "Clone, Debug", "T:LibreMetaverse.AssetCacheSettings": "Clone, Debug, Eq, PartialEq", @@ -2437,6 +2610,8 @@ VALUE_DERIVES = { "T:LibreMetaverse.InventoryBase": "Clone, Debug", "T:LibreMetaverse.InventoryFolder": "Clone, Debug", "T:LibreMetaverse.Matrix4": "Clone, Copy, Debug", + "T:LibreMetaverse.NameValue": "Clone, Debug", + "T:LibreMetaverse.Packets.ObjectUpdatePacket.ObjectDataBlock": "Clone, Debug", "T:LibreMetaverse.Quaternion": "Clone, Copy, Debug", "T:LibreMetaverse.PacketSettings": "Clone, Debug, Eq, PartialEq", "T:LibreMetaverse.ParcelSettings": "Clone, Debug, Eq, PartialEq", diff --git a/tools/generate_rust_mapping.py b/tools/generate_rust_mapping.py index f481efa..0c37c19 100644 --- a/tools/generate_rust_mapping.py +++ b/tools/generate_rust_mapping.py @@ -1014,6 +1014,7 @@ def validate_generated_shims() -> None: "crate::message_decoder::", "crate::NameValue {", "crate::object_material::", + "crate::object_manager::", "crate::object_model::", "crate::notation::", "crate::packet_catalog::",