diff --git a/README.md b/README.md index f62f3f1..c4c65d8 100644 --- a/README.md +++ b/README.md @@ -403,3 +403,13 @@ library copy and transactional COF application with monotonic progress, cancellation, and compensation. Exact mappings, hashes, generated visual data, pixels, packet/event payloads, and recovery behavior are covered by focused tests and documented in [`docs/appearance.md`](docs/appearance.md). + +Native land services now correlate parcel overlays, properties, access lists, +owners, dwell, remote lookups, and bounded script-resource capabilities while +maintaining the simulator's four-metre parcel map. The 16×16 terrain codec +handles normal and rectangular large-region patches, wind layers, and four-slot +PBR material overrides. Extended and legacy environment LLSD paths preserve +parcel-versus-region URI rules, and sound packets retain identity, gain, +position, and queue flags. Packet/capability selection, state-before-event +ordering, cancellation, limits, and live-operation safety are documented in +[`docs/land.md`](docs/land.md). diff --git a/api/SHIM-COVERAGE.md b/api/SHIM-COVERAGE.md index ac220da..b36c7f2 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: 253 types / 15,906 members; remaining surface is callable failure-only shims | +| `LibreMetaverse` | 2,711 | 27,281 | native implementation: 290 types / 16,219 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 2b7442a..a111167 100644 --- a/crates/libremetaverse/src/client_core.rs +++ b/crates/libremetaverse/src/client_core.rs @@ -119,6 +119,10 @@ struct ClientRuntime { network_manager: Mutex>, agent_manager: Mutex>, object_manager: Mutex>>, + environment_manager: Mutex>>, + terrain_manager: Mutex>>, + sound_manager: Mutex>>, + parcel_manager: Mutex>>, appearance_manager: Mutex>>, avatar_manager: Mutex>>, animesh_manager: Mutex>>, @@ -154,6 +158,10 @@ impl ClientRuntime { network_manager: Mutex::new(std::sync::Weak::new()), agent_manager: Mutex::new(std::sync::Weak::new()), object_manager: Mutex::new(None), + environment_manager: Mutex::new(None), + terrain_manager: Mutex::new(None), + sound_manager: Mutex::new(None), + parcel_manager: Mutex::new(None), appearance_manager: Mutex::new(None), avatar_manager: Mutex::new(None), animesh_manager: Mutex::new(None), @@ -684,6 +692,159 @@ impl GridClient { self.cache_object_manager_inner(&value.inner); } + pub(crate) fn install_environment_manager_inner( + &self, + candidate: Arc, + ) -> Arc { + let mut cached = self + .runtime + .environment_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_environment(&self) -> Result { + if let Some(inner) = self + .runtime + .environment_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() + { + return Ok(crate::EnvironmentManager { inner }); + } + crate::environment_manager::EnvironmentManager::native_new(Arc::new(self.clone())) + } + + #[allow(clippy::needless_pass_by_value)] + pub(crate) fn native_set_environment(&self, value: crate::EnvironmentManager) { + *self + .runtime + .environment_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(value.inner); + } + + pub(crate) fn cached_terrain_manager_inner( + &self, + ) -> Option> { + self.runtime + .terrain_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() + } + pub(crate) fn install_terrain_manager_inner( + &self, + candidate: Arc, + ) -> Arc { + let mut cached = self + .runtime + .terrain_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_terrain(&self) -> Result { + if let Some(inner) = self.cached_terrain_manager_inner() { + Ok(crate::TerrainManager { inner }) + } else { + crate::terrain_manager::TerrainManager::native_new(Arc::new(self.clone())) + } + } + pub(crate) fn native_set_terrain(&self, value: crate::TerrainManager) { + *self + .runtime + .terrain_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(value.inner); + } + pub(crate) fn cached_sound_manager_inner( + &self, + ) -> Option> { + self.runtime + .sound_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() + } + pub(crate) fn install_sound_manager_inner( + &self, + candidate: Arc, + ) -> Arc { + let mut cached = self + .runtime + .sound_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_sound(&self) -> Result { + if let Some(inner) = self.cached_sound_manager_inner() { + Ok(crate::SoundManager { inner }) + } else { + crate::sound_manager::SoundManager::native_new(Arc::new(self.clone())) + } + } + pub(crate) fn native_set_sound(&self, value: crate::SoundManager) { + *self + .runtime + .sound_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(value.inner); + } + pub(crate) fn cached_parcel_manager_inner( + &self, + ) -> Option> { + self.runtime + .parcel_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() + } + pub(crate) fn install_parcel_manager_inner( + &self, + candidate: Arc, + ) -> Arc { + let mut cached = self + .runtime + .parcel_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_parcels(&self) -> Result { + if let Some(inner) = self.cached_parcel_manager_inner() { + Ok(crate::ParcelManager { inner }) + } else { + crate::parcel_manager::ParcelManager::native_new(Arc::new(self.clone())) + } + } + pub(crate) fn native_set_parcels(&self, value: crate::ParcelManager) { + *self + .runtime + .parcel_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(value.inner); + } + pub(crate) fn native_set_caps_rate_limiter( &mut self, value: crate::caps_http::CapsRateLimiter, diff --git a/crates/libremetaverse/src/environment_manager.rs b/crates/libremetaverse/src/environment_manager.rs new file mode 100644 index 0000000..24e3842 --- /dev/null +++ b/crates/libremetaverse/src/environment_manager.rs @@ -0,0 +1,651 @@ +//! Extended and legacy environment capability support. + +#![allow(clippy::missing_errors_doc)] +#![allow(clippy::must_use_candidate)] +#![allow(clippy::needless_pass_by_value)] + +use crate::agent_manager::EventRegistry; +use crate::{Error, GridClient}; +use futures_channel::oneshot; +use futures_util::future::{Either, select}; +use futures_util::pin_mut; +use libremetaverse_structured_data::{OSD, OSDMap, OSDParser}; +use libremetaverse_types::compat::{CancellationToken, EventHandler, Subscription, Uri}; +use std::collections::HashMap; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, RwLock}; + +const MAX_ENVIRONMENT_BYTES: usize = 16 * 1024 * 1024; + +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 integer(map: &OSDMap, key: &str, default: i32) -> i32 { + map.get(key) + .and_then(|value| value.as_integer().ok()) + .unwrap_or(default) +} + +fn boolean(map: &OSDMap, key: &str, default: bool) -> bool { + map.get(key) + .and_then(|value| value.as_boolean().ok()) + .unwrap_or(default) +} + +fn string(map: &OSDMap, key: &str) -> String { + map.get(key) + .and_then(|value| value.as_string().ok()) + .unwrap_or_default() +} + +#[derive(Clone, Debug, PartialEq)] +pub struct EnvironmentData { + day_cycle: Option, + day_length: i32, + day_offset: i32, + flags: crate::messages::linden::EnvironmentFlags, + is_default: bool, +} + +impl EnvironmentData { + pub fn new() -> Result { + Ok(Self { + day_cycle: None, + day_length: 14_400, + day_offset: 57_600, + flags: crate::messages::linden::EnvironmentFlags::NONE, + is_default: false, + }) + } + + pub fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> { + self.day_length = integer(&map, "day_length", 14_400); + self.day_offset = integer(&map, "day_offset", 57_600); + self.flags = crate::messages::linden::EnvironmentFlags( + u32::try_from(integer(&map, "flags", 0)).unwrap_or_default(), + ); + self.is_default = boolean(&map, "is_default", false); + self.day_cycle = map + .get("day_cycle") + .filter(|value| !matches!(value, OSD::Undefined)); + Ok(()) + } + + pub fn serialize(&self) -> Result { + let mut values = HashMap::from([ + ("day_length".into(), OSD::Integer(self.day_length)), + ("day_offset".into(), OSD::Integer(self.day_offset)), + ( + "flags".into(), + OSD::Integer(i32::try_from(self.flags.0).unwrap_or(i32::MAX)), + ), + ]); + if let Some(day_cycle) = self.day_cycle.clone() { + values.insert("day_cycle".into(), day_cycle); + } + OSDMap::new_with_dictionary(values) + } + + pub fn day_cycle(&self) -> Option { + self.day_cycle.clone() + } + pub fn set_day_cycle(&mut self, value: Option) { + self.day_cycle = value; + } + pub fn day_length(&self) -> i32 { + self.day_length + } + pub fn set_day_length(&mut self, value: i32) { + self.day_length = value; + } + pub fn day_offset(&self) -> i32 { + self.day_offset + } + pub fn set_day_offset(&mut self, value: i32) { + self.day_offset = value; + } + pub fn flags(&self) -> crate::messages::linden::EnvironmentFlags { + self.flags + } + pub fn set_flags(&mut self, value: crate::messages::linden::EnvironmentFlags) { + self.flags = value; + } + pub fn is_default(&self) -> bool { + self.is_default + } + pub fn set_is_default(&mut self, value: bool) { + self.is_default = value; + } +} + +impl crate::interfaces::IMessage for EnvironmentData { + fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> { + self.deserialize(map) + } + fn serialize(&self) -> Result { + self.serialize() + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ExtEnvironmentMessage { + environment: Option, + message: String, + parcel_id: i32, + success: bool, + version: i32, +} + +impl ExtEnvironmentMessage { + pub fn new() -> Result { + Ok(Self { + environment: None, + message: String::new(), + parcel_id: -1, + success: false, + version: 0, + }) + } + pub fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> { + self.success = boolean(&map, "success", false); + self.parcel_id = integer(&map, "parcel_id", -1); + self.version = integer(&map, "version", 0); + self.message = string(&map, "message"); + self.environment = match map.get("environment") { + Some(OSD::Map(values)) => { + let mut value = EnvironmentData::new()?; + value.deserialize(OSDMap::new_with_dictionary(values)?)?; + Some(value) + } + _ => None, + }; + Ok(()) + } + pub fn serialize(&self) -> Result { + let mut values = HashMap::new(); + values.insert("parcel_id".into(), OSD::Integer(self.parcel_id)); + if let Some(environment) = &self.environment { + values.insert( + "environment".into(), + OSD::Map(environment.serialize()?.snapshot()), + ); + } + if self.version != 0 { + values.insert("version".into(), OSD::Integer(self.version)); + } + OSDMap::new_with_dictionary(values) + } + pub fn environment(&self) -> Option { + self.environment.clone() + } + pub fn set_environment(&mut self, value: Option) { + self.environment = value; + } + pub fn message(&self) -> String { + self.message.clone() + } + pub fn set_message(&mut self, value: String) { + self.message = value; + } + pub fn parcel_id(&self) -> i32 { + self.parcel_id + } + pub fn set_parcel_id(&mut self, value: i32) { + self.parcel_id = value; + } + pub fn success(&self) -> bool { + self.success + } + pub fn set_success(&mut self, value: bool) { + self.success = value; + } + pub fn version(&self) -> i32 { + self.version + } + pub fn set_version(&mut self, value: i32) { + self.version = value; + } +} + +impl crate::interfaces::IMessage for ExtEnvironmentMessage { + fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> { + self.deserialize(map) + } + fn serialize(&self) -> Result { + self.serialize() + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct LegacyEnvironmentMessage { + settings: Option, +} +impl LegacyEnvironmentMessage { + pub fn new() -> Result { + Ok(Self { settings: None }) + } + pub fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> { + self.settings = Some(OSD::Map(map.snapshot())); + Ok(()) + } + pub fn serialize(&self) -> Result { + match &self.settings { + Some(OSD::Map(values)) => OSDMap::new_with_dictionary(values.clone()), + _ => OSDMap::new_with_constructor(), + } + } + pub fn settings(&self) -> Option { + self.settings.clone() + } + pub fn set_settings(&mut self, value: Option) { + self.settings = value; + } +} +impl crate::interfaces::IMessage for LegacyEnvironmentMessage { + fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> { + self.deserialize(map) + } + fn serialize(&self) -> Result { + self.serialize() + } +} + +#[derive(Clone)] +pub struct RegionEnvironmentEventArgs { + environment: ExtEnvironmentMessage, +} +impl RegionEnvironmentEventArgs { + pub fn new(environment: ExtEnvironmentMessage) -> Result { + Ok(Self { environment }) + } + pub fn environment(&self) -> ExtEnvironmentMessage { + self.environment.clone() + } +} +#[derive(Clone)] +pub struct ParcelEnvironmentEventArgs { + parcel_id: i32, + environment: ExtEnvironmentMessage, +} +impl ParcelEnvironmentEventArgs { + pub fn new(parcel_id: i32, environment: ExtEnvironmentMessage) -> Result { + Ok(Self { + parcel_id, + environment, + }) + } + pub fn parcel_id(&self) -> i32 { + self.parcel_id + } + pub fn environment(&self) -> ExtEnvironmentMessage { + self.environment.clone() + } +} +#[derive(Clone)] +pub struct LegacyEnvironmentEventArgs { + environment: LegacyEnvironmentMessage, +} +impl LegacyEnvironmentEventArgs { + pub fn new(environment: LegacyEnvironmentMessage) -> Result { + Ok(Self { environment }) + } + pub fn environment(&self) -> LegacyEnvironmentMessage { + self.environment.clone() + } +} + +pub(crate) struct EnvironmentManagerInner { + client: Arc, + disposed: AtomicBool, + region: RwLock>, + legacy: RwLock>, + region_events: EventRegistry, + parcel_events: EventRegistry, + legacy_events: EventRegistry, +} + +#[derive(Clone)] +pub struct EnvironmentManager { + pub(crate) inner: Arc, +} + +impl EnvironmentManager { + pub fn new(client: GridClient) -> Result { + Self::native_new(Arc::new(client)) + } + #[allow(clippy::unnecessary_wraps)] // The mapped public constructor has a fixed fallible shape. + pub(crate) fn native_new(client: Arc) -> Result { + let candidate = Arc::new(EnvironmentManagerInner { + client: Arc::clone(&client), + disposed: AtomicBool::new(false), + region: RwLock::new(None), + legacy: RwLock::new(None), + region_events: EventRegistry::default(), + parcel_events: EventRegistry::default(), + legacy_events: EventRegistry::default(), + }); + Ok(Self { + inner: client.install_environment_manager_inner(candidate), + }) + } + async fn capability( + &self, + name: &str, + token: &CancellationToken, + ) -> Result, Error> { + if self.inner.disposed.load(Ordering::Acquire) { + return Err(Error::InvalidOperation); + } + let Some(caps) = self + .inner + .client + .network() + .current_sim() + .and_then(|sim| sim.native_caps()) + else { + return Ok(None); + }; + if let Some(uri) = caps.capability_uri(name.to_owned())? { + return Ok(Some(uri)); + } + if caps.seed_request_finished() { + return Ok(None); + } + let (sender, receiver) = oneshot::channel(); + let sender = Arc::new(std::sync::Mutex::new(Some(sender))); + let wake = Arc::clone(&sender); + let _subscription = caps.subscribe_capabilities_received(Some(Arc::new(move |_| { + if let Some(sender) = wake + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .take() + { + let _ = sender.send(()); + } + }))); + if let Some(uri) = caps.capability_uri(name.to_owned())? { + return Ok(Some(uri)); + } + let cancellation = token.cancelled(); + pin_mut!(receiver, cancellation); + match select(receiver, cancellation).await { + Either::Left(_) => caps.capability_uri(name.to_owned()), + Either::Right(_) => Err(Error::Cancelled), + } + } + async fn request( + &self, + method: &str, + uri: Uri, + body: Vec, + token: Option, + ) -> Result>, Error> { + if body.len() > MAX_ENVIRONMENT_BYTES { + return Err(Error::Argument); + } + let http = self.inner.client.native_http_caps_client(); + let token = token.unwrap_or_default(); + let (response, bytes) = match method { + "GET" => http.get(uri, token, None).await?, + "POST" => { + http.post_with_uri_string_bytes_cancellation_token_i_progress( + uri, + "application/llsd+xml".into(), + body, + token, + None, + ) + .await? + } + "DELETE" => { + http.delete_with_uri_string_bytes_cancellation_token_i_progress( + uri, + "application/llsd+xml".into(), + body, + token, + None, + ) + .await? + } + _ => return Err(Error::Argument), + }; + if bytes.len() > MAX_ENVIRONMENT_BYTES { + return Err(Error::Argument); + } + Ok((200..300).contains(&response.status_code).then_some(bytes)) + } + fn parcel_uri(uri: Uri, parcel_id: i32) -> Uri { + let separator = if uri.0.contains('?') { '&' } else { '?' }; + Uri(format!("{}{separator}parcelid={parcel_id}", uri.0)) + } + fn decode_ext(bytes: Vec) -> Result { + let OSD::Map(values) = OSDParser::deserialize_with_bytes(bytes)? else { + return Err(Error::Argument); + }; + let mut message = ExtEnvironmentMessage::new()?; + message.deserialize(OSDMap::new_with_dictionary(values)?)?; + Ok(message) + } + pub async fn get_region_environment( + &self, + token: Option, + ) -> Result, Error> { + let resolved = token.clone().unwrap_or_default(); + let Some(uri) = self.capability("ExtEnvironment", &resolved).await? else { + return Ok(None); + }; + let Some(bytes) = self.request("GET", uri, Vec::new(), token).await? else { + return Ok(None); + }; + let message = Self::decode_ext(bytes)?; + *write(&self.inner.region) = Some(message.clone()); + self.inner + .region_events + .emit(RegionEnvironmentEventArgs::new(message.clone())?); + Ok(Some(message)) + } + pub async fn get_parcel_environment( + &self, + parcel_id: i32, + token: Option, + ) -> Result, Error> { + let resolved = token.clone().unwrap_or_default(); + let Some(uri) = self.capability("ExtEnvironment", &resolved).await? else { + return Ok(None); + }; + let Some(bytes) = self + .request("GET", Self::parcel_uri(uri, parcel_id), Vec::new(), token) + .await? + else { + return Ok(None); + }; + let message = Self::decode_ext(bytes)?; + self.inner + .parcel_events + .emit(ParcelEnvironmentEventArgs::new(parcel_id, message.clone())?); + Ok(Some(message)) + } + async fn set_environment( + &self, + parcel_id: Option, + environment: EnvironmentData, + token: Option, + ) -> Result, Error> { + let resolved = token.clone().unwrap_or_default(); + let Some(mut uri) = self.capability("ExtEnvironment", &resolved).await? else { + return Ok(None); + }; + if let Some(parcel_id) = parcel_id { + uri = Self::parcel_uri(uri, parcel_id); + } + let body = OSDParser::serialize_llsd_xml_bytes(OSD::Map(HashMap::from([( + "environment".into(), + OSD::Map(environment.serialize()?.snapshot()), + )])))?; + let Some(bytes) = self.request("POST", uri, body, token).await? else { + return Ok(None); + }; + Self::decode_ext(bytes).map(Some) + } + pub async fn set_parcel_environment( + &self, + parcel_id: i32, + environment: EnvironmentData, + token: Option, + ) -> Result, Error> { + self.set_environment(Some(parcel_id), environment, token) + .await + } + pub async fn set_region_environment_with_environment_data_cancellation_token( + &self, + environment: EnvironmentData, + token: Option, + ) -> Result, Error> { + self.set_environment(None, environment, token).await + } + async fn reset_environment( + &self, + parcel_id: Option, + token: Option, + ) -> Result { + let resolved = token.clone().unwrap_or_default(); + let Some(mut uri) = self.capability("ExtEnvironment", &resolved).await? else { + return Ok(false); + }; + if let Some(parcel_id) = parcel_id { + uri = Self::parcel_uri(uri, parcel_id); + } + let Some(bytes) = self.request("DELETE", uri, Vec::new(), token).await? else { + return Ok(false); + }; + Ok(Self::decode_ext(bytes)?.success()) + } + pub async fn reset_parcel_environment( + &self, + parcel_id: i32, + token: Option, + ) -> Result { + self.reset_environment(Some(parcel_id), token).await + } + pub async fn reset_region_environment( + &self, + token: Option, + ) -> Result { + self.reset_environment(None, token).await + } + pub async fn get_legacy_environment( + &self, + token: Option, + ) -> Result, Error> { + let resolved = token.clone().unwrap_or_default(); + let Some(uri) = self.capability("EnvironmentSettings", &resolved).await? else { + return Ok(None); + }; + let Some(bytes) = self.request("GET", uri, Vec::new(), token).await? else { + return Ok(None); + }; + let OSD::Map(values) = OSDParser::deserialize_with_bytes(bytes)? else { + return Err(Error::Argument); + }; + let mut message = LegacyEnvironmentMessage::new()?; + message.deserialize(OSDMap::new_with_dictionary(values)?)?; + *write(&self.inner.legacy) = Some(message.clone()); + self.inner + .legacy_events + .emit(LegacyEnvironmentEventArgs::new(message.clone())?); + Ok(Some(message)) + } + pub async fn set_legacy_environment_with_osd_cancellation_token( + &self, + settings: OSD, + token: Option, + ) -> Result { + let resolved = token.clone().unwrap_or_default(); + let Some(uri) = self.capability("EnvironmentSettings", &resolved).await? else { + return Ok(false); + }; + let body = OSDParser::serialize_llsd_xml_bytes(settings)?; + let Some(bytes) = self.request("POST", uri, body, token).await? else { + return Ok(false); + }; + let OSD::Map(values) = OSDParser::deserialize_with_bytes(bytes)? else { + return Ok(false); + }; + Ok(values + .get("success") + .and_then(|value| value.as_boolean().ok()) + .unwrap_or(true)) + } + pub fn region_environment(&self) -> Option { + read(&self.inner.region).clone() + } + pub fn set_region_environment(&mut self, value: Option) { + *write(&self.inner.region) = value; + } + pub fn legacy_environment(&self) -> Option { + read(&self.inner.legacy).clone() + } + pub fn set_legacy_environment(&mut self, value: Option) { + *write(&self.inner.legacy) = value; + } + pub fn subscribe_region_environment_updated( + &self, + handler: EventHandler, + ) -> Subscription { + self.inner.region_events.subscribe(handler) + } + pub fn subscribe_parcel_environment_updated( + &self, + handler: EventHandler, + ) -> Subscription { + self.inner.parcel_events.subscribe(handler) + } + pub fn subscribe_legacy_environment_updated( + &self, + handler: EventHandler, + ) -> Subscription { + self.inner.legacy_events.subscribe(handler) + } + pub fn dispose(&self) -> Result<(), Error> { + self.inner.disposed.store(true, Ordering::Release); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn environment_defaults_and_wire_shape_match_eep() { + let mut value = EnvironmentData::new().unwrap(); + assert_eq!(value.day_length(), 14_400); + assert_eq!(value.day_offset(), 57_600); + value.set_is_default(true); + let map = value.serialize().unwrap(); + assert!(!map.contains_key("is_default".into()).unwrap()); + assert!(!map.contains_key("day_cycle".into()).unwrap()); + } + + #[test] + fn ext_environment_round_trip_preserves_correlation_fields() { + let mut value = ExtEnvironmentMessage::new().unwrap(); + value.set_parcel_id(17); + value.set_version(9); + value.set_environment(Some(EnvironmentData::new().unwrap())); + let mut decoded = ExtEnvironmentMessage::new().unwrap(); + decoded.deserialize(value.serialize().unwrap()).unwrap(); + assert_eq!(decoded.parcel_id(), 17); + assert_eq!(decoded.version(), 9); + assert!(decoded.environment().is_some()); + } +} diff --git a/crates/libremetaverse/src/generated.rs b/crates/libremetaverse/src/generated.rs index d6776f3..83e17b6 100644 --- a/crates/libremetaverse/src/generated.rs +++ b/crates/libremetaverse/src/generated.rs @@ -3552,81 +3552,21 @@ pub use crate::transfers::AssetUpload; pub use crate::transfers::AssetUploadEventArgs; /// C# type: `T:LibreMetaverse.AttachedSoundEventArgs`. -pub struct AttachedSoundEventArgs; -impl AttachedSoundEventArgs { - /// C# member: `M:LibreMetaverse.AttachedSoundEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID,System.Single,LibreMetaverse.SoundFlags)`. - pub fn new( - sim: libremetaverse::Simulator, - sound_id: libremetaverse_types::UUID, - owner_id: libremetaverse_types::UUID, - object_id: libremetaverse_types::UUID, - gain: f32, - flags: libremetaverse_types::SoundFlags, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.AttachedSoundEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID,System.Single,LibreMetaverse.SoundFlags)", - ) - } - /// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.Flags`. - pub fn flags(&self) -> libremetaverse_types::SoundFlags { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttachedSoundEventArgs.Flags") - } - /// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.Gain`. - pub fn gain(&self) -> f32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttachedSoundEventArgs.Gain") - } - /// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.ObjectID`. - pub fn object_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttachedSoundEventArgs.ObjectID") - } - /// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.OwnerID`. - pub fn owner_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttachedSoundEventArgs.OwnerID") - } - /// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AttachedSoundEventArgs.Simulator" - ) - } - /// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.SoundID`. - pub fn sound_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttachedSoundEventArgs.SoundID") - } -} +/// C# member: `M:LibreMetaverse.AttachedSoundEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID,System.Single,LibreMetaverse.SoundFlags)`. +/// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.Flags`. +/// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.Gain`. +/// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.ObjectID`. +/// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.OwnerID`. +/// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.AttachedSoundEventArgs.SoundID`. +pub use crate::sound_manager::AttachedSoundEventArgs; /// C# type: `T:LibreMetaverse.AttachedSoundGainChangeEventArgs`. -pub struct AttachedSoundGainChangeEventArgs; -impl AttachedSoundGainChangeEventArgs { - /// C# member: `M:LibreMetaverse.AttachedSoundGainChangeEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Single)`. - pub fn new( - sim: libremetaverse::Simulator, - object_id: libremetaverse_types::UUID, - gain: f32, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.AttachedSoundGainChangeEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Single)", - ) - } - /// C# member: `P:LibreMetaverse.AttachedSoundGainChangeEventArgs.Gain`. - pub fn gain(&self) -> f32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AttachedSoundGainChangeEventArgs.Gain" - ) - } - /// C# member: `P:LibreMetaverse.AttachedSoundGainChangeEventArgs.ObjectID`. - pub fn object_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AttachedSoundGainChangeEventArgs.ObjectID" - ) - } - /// C# member: `P:LibreMetaverse.AttachedSoundGainChangeEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AttachedSoundGainChangeEventArgs.Simulator" - ) - } -} +/// C# member: `M:LibreMetaverse.AttachedSoundGainChangeEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Single)`. +/// C# member: `P:LibreMetaverse.AttachedSoundGainChangeEventArgs.Gain`. +/// C# member: `P:LibreMetaverse.AttachedSoundGainChangeEventArgs.ObjectID`. +/// C# member: `P:LibreMetaverse.AttachedSoundGainChangeEventArgs.Simulator`. +pub use crate::sound_manager::AttachedSoundGainChangeEventArgs; /// C# type: `T:LibreMetaverse.AttentionData`. /// C# member: `P:LibreMetaverse.AttentionData.Priority`. @@ -6609,165 +6549,22 @@ pub enum EffectType { } /// C# type: `T:LibreMetaverse.EnvironmentManager`. -pub struct EnvironmentManager; -impl EnvironmentManager { - /// C# member: `E:LibreMetaverse.EnvironmentManager.LegacyEnvironmentUpdated`. - pub fn subscribe_legacy_environment_updated( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::LegacyEnvironmentEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.EnvironmentManager.LegacyEnvironmentUpdated" - ) - } - /// C# member: `E:LibreMetaverse.EnvironmentManager.ParcelEnvironmentUpdated`. - pub fn subscribe_parcel_environment_updated( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::ParcelEnvironmentEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.EnvironmentManager.ParcelEnvironmentUpdated" - ) - } - /// C# member: `E:LibreMetaverse.EnvironmentManager.RegionEnvironmentUpdated`. - pub fn subscribe_region_environment_updated( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::RegionEnvironmentEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.EnvironmentManager.RegionEnvironmentUpdated" - ) - } - /// C# member: `M:LibreMetaverse.EnvironmentManager.#ctor(LibreMetaverse.GridClient)`. - pub fn new(client: libremetaverse::GridClient) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.EnvironmentManager.#ctor(LibreMetaverse.GridClient)", - ) - } - /// C# member: `M:LibreMetaverse.EnvironmentManager.Dispose`. - pub fn dispose(&self) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented("M:LibreMetaverse.EnvironmentManager.Dispose") - } - /// C# member: `M:LibreMetaverse.EnvironmentManager.GetLegacyEnvironmentAsync(System.Threading.CancellationToken)`. - pub async fn get_legacy_environment( - &self, - cancellation_token: Option, - ) -> Result, crate::Error> - { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.EnvironmentManager.GetLegacyEnvironmentAsync(System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.EnvironmentManager.GetParcelEnvironmentAsync(System.Int32,System.Threading.CancellationToken)`. - pub async fn get_parcel_environment( - &self, - parcel_id: i32, - cancellation_token: Option, - ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.EnvironmentManager.GetParcelEnvironmentAsync(System.Int32,System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.EnvironmentManager.GetRegionEnvironmentAsync(System.Threading.CancellationToken)`. - pub async fn get_region_environment( - &self, - cancellation_token: Option, - ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.EnvironmentManager.GetRegionEnvironmentAsync(System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.EnvironmentManager.ResetParcelEnvironmentAsync(System.Int32,System.Threading.CancellationToken)`. - pub async fn reset_parcel_environment( - &self, - parcel_id: i32, - cancellation_token: Option, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.EnvironmentManager.ResetParcelEnvironmentAsync(System.Int32,System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.EnvironmentManager.ResetRegionEnvironmentAsync(System.Threading.CancellationToken)`. - pub async fn reset_region_environment( - &self, - cancellation_token: Option, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.EnvironmentManager.ResetRegionEnvironmentAsync(System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.EnvironmentManager.SetLegacyEnvironmentAsync(LibreMetaverse.StructuredData.OSD,System.Threading.CancellationToken)`. - pub async fn set_legacy_environment_with_osd_cancellation_token( - &self, - settings: libremetaverse_structured_data::OSD, - cancellation_token: Option, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.EnvironmentManager.SetLegacyEnvironmentAsync(LibreMetaverse.StructuredData.OSD,System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.EnvironmentManager.SetParcelEnvironmentAsync(System.Int32,LibreMetaverse.Messages.Linden.EnvironmentData,System.Threading.CancellationToken)`. - pub async fn set_parcel_environment( - &self, - parcel_id: i32, - environment: libremetaverse::messages::linden::EnvironmentData, - cancellation_token: Option, - ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.EnvironmentManager.SetParcelEnvironmentAsync(System.Int32,LibreMetaverse.Messages.Linden.EnvironmentData,System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.EnvironmentManager.SetRegionEnvironmentAsync(LibreMetaverse.Messages.Linden.EnvironmentData,System.Threading.CancellationToken)`. - pub async fn set_region_environment_with_environment_data_cancellation_token( - &self, - environment: libremetaverse::messages::linden::EnvironmentData, - cancellation_token: Option, - ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.EnvironmentManager.SetRegionEnvironmentAsync(LibreMetaverse.Messages.Linden.EnvironmentData,System.Threading.CancellationToken)", - ) - } - /// C# member: `P:LibreMetaverse.EnvironmentManager.LegacyEnvironment`. - pub fn legacy_environment( - &self, - ) -> Option { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.EnvironmentManager.LegacyEnvironment" - ) - } - /// Setter for C# member: `P:LibreMetaverse.EnvironmentManager.LegacyEnvironment`. - pub fn set_legacy_environment( - &mut self, - value: Option, - ) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.EnvironmentManager.LegacyEnvironment" - ) - } - /// C# member: `P:LibreMetaverse.EnvironmentManager.RegionEnvironment`. - pub fn region_environment( - &self, - ) -> Option { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.EnvironmentManager.RegionEnvironment" - ) - } - /// Setter for C# member: `P:LibreMetaverse.EnvironmentManager.RegionEnvironment`. - pub fn set_region_environment( - &mut self, - value: Option, - ) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.EnvironmentManager.RegionEnvironment" - ) - } -} +/// C# member: `E:LibreMetaverse.EnvironmentManager.LegacyEnvironmentUpdated`. +/// C# member: `E:LibreMetaverse.EnvironmentManager.ParcelEnvironmentUpdated`. +/// C# member: `E:LibreMetaverse.EnvironmentManager.RegionEnvironmentUpdated`. +/// C# member: `M:LibreMetaverse.EnvironmentManager.#ctor(LibreMetaverse.GridClient)`. +/// C# member: `M:LibreMetaverse.EnvironmentManager.Dispose`. +/// C# member: `M:LibreMetaverse.EnvironmentManager.GetLegacyEnvironmentAsync(System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.EnvironmentManager.GetParcelEnvironmentAsync(System.Int32,System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.EnvironmentManager.GetRegionEnvironmentAsync(System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.EnvironmentManager.ResetParcelEnvironmentAsync(System.Int32,System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.EnvironmentManager.ResetRegionEnvironmentAsync(System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.EnvironmentManager.SetLegacyEnvironmentAsync(LibreMetaverse.StructuredData.OSD,System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.EnvironmentManager.SetParcelEnvironmentAsync(System.Int32,LibreMetaverse.Messages.Linden.EnvironmentData,System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.EnvironmentManager.SetRegionEnvironmentAsync(LibreMetaverse.Messages.Linden.EnvironmentData,System.Threading.CancellationToken)`. +/// C# member: `P:LibreMetaverse.EnvironmentManager.LegacyEnvironment`. +/// C# member: `P:LibreMetaverse.EnvironmentManager.RegionEnvironment`. +pub use crate::environment_manager::EnvironmentManager; /// C# type: `T:LibreMetaverse.EnvironmentTrack`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -7863,37 +7660,11 @@ pub use crate::inventory_manager::FindObjectByPathReplyEventArgs; pub use crate::inventory_manager::FolderUpdatedEventArgs; /// C# type: `T:LibreMetaverse.ForceSelectObjectsReplyEventArgs`. -pub struct ForceSelectObjectsReplyEventArgs; -impl ForceSelectObjectsReplyEventArgs { - /// C# member: `M:LibreMetaverse.ForceSelectObjectsReplyEventArgs.#ctor(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},System.Boolean)`. - pub fn new( - simulator: libremetaverse::Simulator, - object_i_ds: Vec, - reset_list: bool, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ForceSelectObjectsReplyEventArgs.#ctor(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},System.Boolean)", - ) - } - /// C# member: `P:LibreMetaverse.ForceSelectObjectsReplyEventArgs.ObjectIDs`. - pub fn object_i_ds(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ForceSelectObjectsReplyEventArgs.ObjectIDs" - ) - } - /// C# member: `P:LibreMetaverse.ForceSelectObjectsReplyEventArgs.ResetList`. - pub fn reset_list(&self) -> bool { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ForceSelectObjectsReplyEventArgs.ResetList" - ) - } - /// C# member: `P:LibreMetaverse.ForceSelectObjectsReplyEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ForceSelectObjectsReplyEventArgs.Simulator" - ) - } -} +/// C# member: `M:LibreMetaverse.ForceSelectObjectsReplyEventArgs.#ctor(LibreMetaverse.Simulator,System.Collections.Generic.List{System.UInt32},System.Boolean)`. +/// C# member: `P:LibreMetaverse.ForceSelectObjectsReplyEventArgs.ObjectIDs`. +/// C# member: `P:LibreMetaverse.ForceSelectObjectsReplyEventArgs.ResetList`. +/// C# member: `P:LibreMetaverse.ForceSelectObjectsReplyEventArgs.Simulator`. +pub use crate::parcel_manager::ForceSelectObjectsReplyEventArgs; /// C# type: `T:LibreMetaverse.FriendFoundReplyEventArgs`. pub struct FriendFoundReplyEventArgs; @@ -8595,11 +8366,14 @@ impl GridClient { } /// C# member: `P:LibreMetaverse.GridClient.Environment`. pub fn environment(&self) -> libremetaverse::EnvironmentManager { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Environment") + /* native client-core implementation */ + self.native_environment() + .unwrap_or_else(|_| panic!("failed to construct EnvironmentManager")) } /// Setter for C# member: `P:LibreMetaverse.GridClient.Environment`. pub fn set_environment(&mut self, value: libremetaverse::EnvironmentManager) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Environment") + /* native client-core implementation */ + self.native_set_environment(value) } /// C# member: `P:LibreMetaverse.GridClient.Estate`. pub fn estate(&self) -> libremetaverse::EstateTools { @@ -8694,11 +8468,14 @@ impl GridClient { } /// C# member: `P:LibreMetaverse.GridClient.Parcels`. pub fn parcels(&self) -> libremetaverse::ParcelManager { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Parcels") + /* native client-core implementation */ + self.native_parcels() + .unwrap_or_else(|_| panic!("failed to construct ParcelManager")) } /// Setter for C# member: `P:LibreMetaverse.GridClient.Parcels`. pub fn set_parcels(&mut self, value: libremetaverse::ParcelManager) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Parcels") + /* native client-core implementation */ + self.native_set_parcels(value) } /// C# member: `P:LibreMetaverse.GridClient.Self`. pub fn self_(&mut self) -> &mut libremetaverse::AgentManager { @@ -8712,11 +8489,14 @@ impl GridClient { } /// C# member: `P:LibreMetaverse.GridClient.Sound`. pub fn sound(&self) -> libremetaverse::SoundManager { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Sound") + /* native client-core implementation */ + self.native_sound() + .unwrap_or_else(|_| panic!("failed to construct SoundManager")) } /// Setter for C# member: `P:LibreMetaverse.GridClient.Sound`. pub fn set_sound(&mut self, value: libremetaverse::SoundManager) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Sound") + /* native client-core implementation */ + self.native_set_sound(value) } /// C# member: `P:LibreMetaverse.GridClient.Stats`. pub fn stats(&self) -> libremetaverse::stats::UtilizationStatistics { @@ -8728,11 +8508,14 @@ impl GridClient { } /// C# member: `P:LibreMetaverse.GridClient.Terrain`. pub fn terrain(&self) -> libremetaverse::TerrainManager { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Terrain") + /* native client-core implementation */ + self.native_terrain() + .unwrap_or_else(|_| panic!("failed to construct TerrainManager")) } /// Setter for C# member: `P:LibreMetaverse.GridClient.Terrain`. pub fn set_terrain(&mut self, value: libremetaverse::TerrainManager) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Terrain") + /* native client-core implementation */ + self.native_set_terrain(value) } /// C# member: `P:LibreMetaverse.GridClient.Throttle`. pub fn throttle(&self) -> libremetaverse::AgentThrottle { @@ -13044,47 +12827,13 @@ pub use crate::object_manager::KillObjectEventArgs; pub use crate::object_manager::KillObjectsEventArgs; /// C# type: `T:LibreMetaverse.LandPatchReceivedEventArgs`. -pub struct LandPatchReceivedEventArgs; -impl LandPatchReceivedEventArgs { - /// C# member: `M:LibreMetaverse.LandPatchReceivedEventArgs.#ctor(LibreMetaverse.Simulator,System.Int32,System.Int32,System.Int32,System.Single[])`. - pub fn new( - simulator: libremetaverse::Simulator, - x: i32, - y: i32, - patch_size: i32, - height_map: Vec, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LandPatchReceivedEventArgs.#ctor(LibreMetaverse.Simulator,System.Int32,System.Int32,System.Int32,System.Single[])", - ) - } - /// C# member: `P:LibreMetaverse.LandPatchReceivedEventArgs.HeightMap`. - pub fn height_map(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LandPatchReceivedEventArgs.HeightMap" - ) - } - /// C# member: `P:LibreMetaverse.LandPatchReceivedEventArgs.PatchSize`. - pub fn patch_size(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LandPatchReceivedEventArgs.PatchSize" - ) - } - /// C# member: `P:LibreMetaverse.LandPatchReceivedEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LandPatchReceivedEventArgs.Simulator" - ) - } - /// C# member: `P:LibreMetaverse.LandPatchReceivedEventArgs.X`. - pub fn x(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LandPatchReceivedEventArgs.X") - } - /// C# member: `P:LibreMetaverse.LandPatchReceivedEventArgs.Y`. - pub fn y(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LandPatchReceivedEventArgs.Y") - } -} +/// C# member: `M:LibreMetaverse.LandPatchReceivedEventArgs.#ctor(LibreMetaverse.Simulator,System.Int32,System.Int32,System.Int32,System.Single[])`. +/// C# member: `P:LibreMetaverse.LandPatchReceivedEventArgs.HeightMap`. +/// C# member: `P:LibreMetaverse.LandPatchReceivedEventArgs.PatchSize`. +/// C# member: `P:LibreMetaverse.LandPatchReceivedEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.LandPatchReceivedEventArgs.X`. +/// C# member: `P:LibreMetaverse.LandPatchReceivedEventArgs.Y`. +pub use crate::terrain_manager::LandPatchReceivedEventArgs; /// C# type: `T:LibreMetaverse.LandingType`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -13117,23 +12866,9 @@ pub enum LastExecStatus { } /// C# type: `T:LibreMetaverse.LegacyEnvironmentEventArgs`. -pub struct LegacyEnvironmentEventArgs; -impl LegacyEnvironmentEventArgs { - /// C# member: `M:LibreMetaverse.LegacyEnvironmentEventArgs.#ctor(LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage)`. - pub fn new( - environment: libremetaverse::messages::linden::LegacyEnvironmentMessage, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LegacyEnvironmentEventArgs.#ctor(LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage)", - ) - } - /// C# member: `P:LibreMetaverse.LegacyEnvironmentEventArgs.Environment`. - pub fn environment(&self) -> libremetaverse::messages::linden::LegacyEnvironmentMessage { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LegacyEnvironmentEventArgs.Environment" - ) - } -} +/// C# member: `M:LibreMetaverse.LegacyEnvironmentEventArgs.#ctor(LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage)`. +/// C# member: `P:LibreMetaverse.LegacyEnvironmentEventArgs.Environment`. +pub use crate::environment_manager::LegacyEnvironmentEventArgs; /// C# type: `T:LibreMetaverse.LindenAttentions`. /// C# member: `F:LibreMetaverse.LindenAttentions.Default`. @@ -16429,138 +16164,61 @@ impl PacketSettings { } /// C# type: `T:LibreMetaverse.Parcel`. -pub struct Parcel { - /// C# member: `F:LibreMetaverse.Parcel.AABBMax`. - pub aabb_max: libremetaverse_types::Vector3, - /// C# member: `F:LibreMetaverse.Parcel.AABBMin`. - pub aabb_min: libremetaverse_types::Vector3, - /// C# member: `F:LibreMetaverse.Parcel.AccessBlackList`. - pub access_black_list: Vec, - /// C# member: `F:LibreMetaverse.Parcel.AccessWhiteList`. - pub access_white_list: Vec, - /// C# member: `F:LibreMetaverse.Parcel.AnyAVSounds`. - pub any_av_sounds: bool, - /// C# member: `F:LibreMetaverse.Parcel.Area`. - pub area: i32, - /// C# member: `F:LibreMetaverse.Parcel.AuctionID`. - pub auction_id: u32, - /// C# member: `F:LibreMetaverse.Parcel.AuthBuyerID`. - pub auth_buyer_id: libremetaverse_types::UUID, - /// C# member: `F:LibreMetaverse.Parcel.Bitmap`. - pub bitmap: Vec, - /// C# member: `F:LibreMetaverse.Parcel.Category`. - pub category: libremetaverse::ParcelCategory, - /// C# member: `F:LibreMetaverse.Parcel.ClaimDate`. - pub claim_date: std::time::SystemTime, - /// C# member: `F:LibreMetaverse.Parcel.ClaimPrice`. - pub claim_price: i32, - /// C# member: `F:LibreMetaverse.Parcel.Desc`. - pub desc: String, - /// C# member: `F:LibreMetaverse.Parcel.Dwell`. - pub dwell: f32, - /// C# member: `F:LibreMetaverse.Parcel.Flags`. - pub flags: libremetaverse::ParcelFlags, - /// C# member: `F:LibreMetaverse.Parcel.GroupAVSounds`. - pub group_av_sounds: bool, - /// C# member: `F:LibreMetaverse.Parcel.GroupID`. - pub group_id: libremetaverse_types::UUID, - /// C# member: `F:LibreMetaverse.Parcel.GroupPrims`. - pub group_prims: i32, - /// C# member: `F:LibreMetaverse.Parcel.IsGroupOwned`. - pub is_group_owned: bool, - /// C# member: `F:LibreMetaverse.Parcel.Landing`. - pub landing: libremetaverse::LandingType, - /// C# member: `F:LibreMetaverse.Parcel.LocalID`. - pub local_id: i32, - /// C# member: `F:LibreMetaverse.Parcel.MaxPrims`. - pub max_prims: i32, - /// C# member: `F:LibreMetaverse.Parcel.Media`. - pub media: libremetaverse::ParcelMedia, - /// C# member: `F:LibreMetaverse.Parcel.MusicURL`. - pub music_url: String, - /// C# member: `F:LibreMetaverse.Parcel.Name`. - pub name: String, - /// C# member: `F:LibreMetaverse.Parcel.ObscureMedia`. - pub obscure_media: bool, - /// C# member: `F:LibreMetaverse.Parcel.ObscureMusic`. - pub obscure_music: bool, - /// C# member: `F:LibreMetaverse.Parcel.OtherCleanTime`. - pub other_clean_time: i32, - /// C# member: `F:LibreMetaverse.Parcel.OtherCount`. - pub other_count: i32, - /// C# member: `F:LibreMetaverse.Parcel.OtherPrims`. - pub other_prims: i32, - /// C# member: `F:LibreMetaverse.Parcel.OwnerID`. - pub owner_id: libremetaverse_types::UUID, - /// C# member: `F:LibreMetaverse.Parcel.OwnerPrims`. - pub owner_prims: i32, - /// C# member: `F:LibreMetaverse.Parcel.ParcelPrimBonus`. - pub parcel_prim_bonus: f32, - /// C# member: `F:LibreMetaverse.Parcel.PassHours`. - pub pass_hours: f32, - /// C# member: `F:LibreMetaverse.Parcel.PassPrice`. - pub pass_price: i32, - /// C# member: `F:LibreMetaverse.Parcel.PublicCount`. - pub public_count: i32, - /// C# member: `F:LibreMetaverse.Parcel.RegionDenyAgeUnverified`. - pub region_deny_age_unverified: bool, - /// C# member: `F:LibreMetaverse.Parcel.RegionDenyAnonymous`. - pub region_deny_anonymous: bool, - /// C# member: `F:LibreMetaverse.Parcel.RegionPushOverride`. - pub region_push_override: bool, - /// C# member: `F:LibreMetaverse.Parcel.RentPrice`. - pub rent_price: i32, - /// C# member: `F:LibreMetaverse.Parcel.SalePrice`. - pub sale_price: i32, - /// C# member: `F:LibreMetaverse.Parcel.SeeAVs`. - pub see_a_vs: bool, - /// C# member: `F:LibreMetaverse.Parcel.SelfCount`. - pub self_count: i32, - /// C# member: `F:LibreMetaverse.Parcel.SimWideMaxPrims`. - pub sim_wide_max_prims: i32, - /// C# member: `F:LibreMetaverse.Parcel.SimWideTotalPrims`. - pub sim_wide_total_prims: i32, - /// C# member: `F:LibreMetaverse.Parcel.SnapshotID`. - pub snapshot_id: libremetaverse_types::UUID, - /// C# member: `F:LibreMetaverse.Parcel.Status`. - pub status: libremetaverse::ParcelStatus, - /// C# member: `F:LibreMetaverse.Parcel.TotalPrims`. - pub total_prims: i32, - /// C# member: `F:LibreMetaverse.Parcel.UserLocation`. - pub user_location: libremetaverse_types::Vector3, - /// C# member: `F:LibreMetaverse.Parcel.UserLookAt`. - pub user_look_at: libremetaverse_types::Vector3, -} -impl Parcel { - /// C# member: `M:LibreMetaverse.Parcel.#ctor(System.Int32)`. - pub fn new(local_id: i32) -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.Parcel.#ctor(System.Int32)") - } - /// C# member: `M:LibreMetaverse.Parcel.ToString`. - pub fn to_string(&self) -> String { - libremetaverse_types::unimplemented_api!("M:LibreMetaverse.Parcel.ToString") - } - /// C# member: `M:LibreMetaverse.Parcel.Update(LibreMetaverse.GridClient,LibreMetaverse.Simulator,System.Boolean)`. - pub fn update( - &self, - client: libremetaverse::GridClient, - simulator: libremetaverse::Simulator, - want_reply: bool, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Parcel.Update(LibreMetaverse.GridClient,LibreMetaverse.Simulator,System.Boolean)", - ) - } - /// C# member: `M:LibreMetaverse.Parcel.UpdateOtherCleanTime(LibreMetaverse.Simulator)`. - pub fn update_other_clean_time( - &self, - simulator: libremetaverse::Simulator, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Parcel.UpdateOtherCleanTime(LibreMetaverse.Simulator)", - ) - } -} +/// C# member: `F:LibreMetaverse.Parcel.AABBMax`. +/// C# member: `F:LibreMetaverse.Parcel.AABBMin`. +/// C# member: `F:LibreMetaverse.Parcel.AccessBlackList`. +/// C# member: `F:LibreMetaverse.Parcel.AccessWhiteList`. +/// C# member: `F:LibreMetaverse.Parcel.AnyAVSounds`. +/// C# member: `F:LibreMetaverse.Parcel.Area`. +/// C# member: `F:LibreMetaverse.Parcel.AuctionID`. +/// C# member: `F:LibreMetaverse.Parcel.AuthBuyerID`. +/// C# member: `F:LibreMetaverse.Parcel.Bitmap`. +/// C# member: `F:LibreMetaverse.Parcel.Category`. +/// C# member: `F:LibreMetaverse.Parcel.ClaimDate`. +/// C# member: `F:LibreMetaverse.Parcel.ClaimPrice`. +/// C# member: `F:LibreMetaverse.Parcel.Desc`. +/// C# member: `F:LibreMetaverse.Parcel.Dwell`. +/// C# member: `F:LibreMetaverse.Parcel.Flags`. +/// C# member: `F:LibreMetaverse.Parcel.GroupAVSounds`. +/// C# member: `F:LibreMetaverse.Parcel.GroupID`. +/// C# member: `F:LibreMetaverse.Parcel.GroupPrims`. +/// C# member: `F:LibreMetaverse.Parcel.IsGroupOwned`. +/// C# member: `F:LibreMetaverse.Parcel.Landing`. +/// C# member: `F:LibreMetaverse.Parcel.LocalID`. +/// C# member: `F:LibreMetaverse.Parcel.MaxPrims`. +/// C# member: `F:LibreMetaverse.Parcel.Media`. +/// C# member: `F:LibreMetaverse.Parcel.MusicURL`. +/// C# member: `F:LibreMetaverse.Parcel.Name`. +/// C# member: `F:LibreMetaverse.Parcel.ObscureMedia`. +/// C# member: `F:LibreMetaverse.Parcel.ObscureMusic`. +/// C# member: `F:LibreMetaverse.Parcel.OtherCleanTime`. +/// C# member: `F:LibreMetaverse.Parcel.OtherCount`. +/// C# member: `F:LibreMetaverse.Parcel.OtherPrims`. +/// C# member: `F:LibreMetaverse.Parcel.OwnerID`. +/// C# member: `F:LibreMetaverse.Parcel.OwnerPrims`. +/// C# member: `F:LibreMetaverse.Parcel.ParcelPrimBonus`. +/// C# member: `F:LibreMetaverse.Parcel.PassHours`. +/// C# member: `F:LibreMetaverse.Parcel.PassPrice`. +/// C# member: `F:LibreMetaverse.Parcel.PublicCount`. +/// C# member: `F:LibreMetaverse.Parcel.RegionDenyAgeUnverified`. +/// C# member: `F:LibreMetaverse.Parcel.RegionDenyAnonymous`. +/// C# member: `F:LibreMetaverse.Parcel.RegionPushOverride`. +/// C# member: `F:LibreMetaverse.Parcel.RentPrice`. +/// C# member: `F:LibreMetaverse.Parcel.SalePrice`. +/// C# member: `F:LibreMetaverse.Parcel.SeeAVs`. +/// C# member: `F:LibreMetaverse.Parcel.SelfCount`. +/// C# member: `F:LibreMetaverse.Parcel.SimWideMaxPrims`. +/// C# member: `F:LibreMetaverse.Parcel.SimWideTotalPrims`. +/// C# member: `F:LibreMetaverse.Parcel.SnapshotID`. +/// C# member: `F:LibreMetaverse.Parcel.Status`. +/// C# member: `F:LibreMetaverse.Parcel.TotalPrims`. +/// C# member: `F:LibreMetaverse.Parcel.UserLocation`. +/// C# member: `F:LibreMetaverse.Parcel.UserLookAt`. +/// C# member: `M:LibreMetaverse.Parcel.#ctor(System.Int32)`. +/// C# member: `M:LibreMetaverse.Parcel.ToString`. +/// C# member: `M:LibreMetaverse.Parcel.Update(LibreMetaverse.GridClient,LibreMetaverse.Simulator,System.Boolean)`. +/// C# member: `M:LibreMetaverse.Parcel.UpdateOtherCleanTime(LibreMetaverse.Simulator)`. +pub use crate::parcel_manager::Parcel; /// C# type: `T:LibreMetaverse.ParcelAccessFlags`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -16573,51 +16231,13 @@ pub enum ParcelAccessFlags { } /// C# type: `T:LibreMetaverse.ParcelAccessListReplyEventArgs`. -pub struct ParcelAccessListReplyEventArgs; -impl ParcelAccessListReplyEventArgs { - /// C# member: `M:LibreMetaverse.ParcelAccessListReplyEventArgs.#ctor(LibreMetaverse.Simulator,System.Int32,System.Int32,System.UInt32,System.Collections.Generic.List{LibreMetaverse.ParcelManager.ParcelAccessEntry})`. - pub fn new( - simulator: libremetaverse::Simulator, - sequence_id: i32, - local_id: i32, - flags: u32, - access_entries: Vec, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelAccessListReplyEventArgs.#ctor(LibreMetaverse.Simulator,System.Int32,System.Int32,System.UInt32,System.Collections.Generic.List{LibreMetaverse.ParcelManager.ParcelAccessEntry})", - ) - } - /// C# member: `P:LibreMetaverse.ParcelAccessListReplyEventArgs.AccessList`. - pub fn access_list(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelAccessListReplyEventArgs.AccessList" - ) - } - /// C# member: `P:LibreMetaverse.ParcelAccessListReplyEventArgs.Flags`. - pub fn flags(&self) -> u32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelAccessListReplyEventArgs.Flags" - ) - } - /// C# member: `P:LibreMetaverse.ParcelAccessListReplyEventArgs.LocalID`. - pub fn local_id(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelAccessListReplyEventArgs.LocalID" - ) - } - /// C# member: `P:LibreMetaverse.ParcelAccessListReplyEventArgs.SequenceID`. - pub fn sequence_id(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelAccessListReplyEventArgs.SequenceID" - ) - } - /// C# member: `P:LibreMetaverse.ParcelAccessListReplyEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelAccessListReplyEventArgs.Simulator" - ) - } -} +/// C# member: `M:LibreMetaverse.ParcelAccessListReplyEventArgs.#ctor(LibreMetaverse.Simulator,System.Int32,System.Int32,System.UInt32,System.Collections.Generic.List{LibreMetaverse.ParcelManager.ParcelAccessEntry})`. +/// C# member: `P:LibreMetaverse.ParcelAccessListReplyEventArgs.AccessList`. +/// C# member: `P:LibreMetaverse.ParcelAccessListReplyEventArgs.Flags`. +/// C# member: `P:LibreMetaverse.ParcelAccessListReplyEventArgs.LocalID`. +/// C# member: `P:LibreMetaverse.ParcelAccessListReplyEventArgs.SequenceID`. +/// C# member: `P:LibreMetaverse.ParcelAccessListReplyEventArgs.Simulator`. +pub use crate::parcel_manager::ParcelAccessListReplyEventArgs; /// C# type: `T:LibreMetaverse.ParcelCategory`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -16656,61 +16276,17 @@ pub enum ParcelCategory { } /// C# type: `T:LibreMetaverse.ParcelDwellReplyEventArgs`. -pub struct ParcelDwellReplyEventArgs; -impl ParcelDwellReplyEventArgs { - /// C# member: `M:LibreMetaverse.ParcelDwellReplyEventArgs.#ctor(LibreMetaverse.UUID,System.Int32,System.Single)`. - pub fn new( - parcel_id: libremetaverse_types::UUID, - local_id: i32, - dwell: f32, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelDwellReplyEventArgs.#ctor(LibreMetaverse.UUID,System.Int32,System.Single)", - ) - } - /// C# member: `P:LibreMetaverse.ParcelDwellReplyEventArgs.Dwell`. - pub fn dwell(&self) -> f32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ParcelDwellReplyEventArgs.Dwell") - } - /// C# member: `P:LibreMetaverse.ParcelDwellReplyEventArgs.LocalID`. - pub fn local_id(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelDwellReplyEventArgs.LocalID" - ) - } - /// C# member: `P:LibreMetaverse.ParcelDwellReplyEventArgs.ParcelID`. - pub fn parcel_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelDwellReplyEventArgs.ParcelID" - ) - } -} +/// C# member: `M:LibreMetaverse.ParcelDwellReplyEventArgs.#ctor(LibreMetaverse.UUID,System.Int32,System.Single)`. +/// C# member: `P:LibreMetaverse.ParcelDwellReplyEventArgs.Dwell`. +/// C# member: `P:LibreMetaverse.ParcelDwellReplyEventArgs.LocalID`. +/// C# member: `P:LibreMetaverse.ParcelDwellReplyEventArgs.ParcelID`. +pub use crate::parcel_manager::ParcelDwellReplyEventArgs; /// C# type: `T:LibreMetaverse.ParcelEnvironmentEventArgs`. -pub struct ParcelEnvironmentEventArgs; -impl ParcelEnvironmentEventArgs { - /// C# member: `M:LibreMetaverse.ParcelEnvironmentEventArgs.#ctor(System.Int32,LibreMetaverse.Messages.Linden.ExtEnvironmentMessage)`. - pub fn new( - parcel_id: i32, - environment: libremetaverse::messages::linden::ExtEnvironmentMessage, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelEnvironmentEventArgs.#ctor(System.Int32,LibreMetaverse.Messages.Linden.ExtEnvironmentMessage)", - ) - } - /// C# member: `P:LibreMetaverse.ParcelEnvironmentEventArgs.Environment`. - pub fn environment(&self) -> libremetaverse::messages::linden::ExtEnvironmentMessage { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelEnvironmentEventArgs.Environment" - ) - } - /// C# member: `P:LibreMetaverse.ParcelEnvironmentEventArgs.ParcelId`. - pub fn parcel_id(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelEnvironmentEventArgs.ParcelId" - ) - } -} +/// C# member: `M:LibreMetaverse.ParcelEnvironmentEventArgs.#ctor(System.Int32,LibreMetaverse.Messages.Linden.ExtEnvironmentMessage)`. +/// C# member: `P:LibreMetaverse.ParcelEnvironmentEventArgs.Environment`. +/// C# member: `P:LibreMetaverse.ParcelEnvironmentEventArgs.ParcelId`. +pub use crate::environment_manager::ParcelEnvironmentEventArgs; /// C# type: `T:LibreMetaverse.ParcelFlags`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -16784,517 +16360,93 @@ impl ParcelFlags { } /// C# type: `T:LibreMetaverse.ParcelInfo`. -pub struct ParcelInfo { - /// C# member: `F:LibreMetaverse.ParcelInfo.ActualArea`. - pub actual_area: i32, - /// C# member: `F:LibreMetaverse.ParcelInfo.AuctionID`. - pub auction_id: i32, - /// C# member: `F:LibreMetaverse.ParcelInfo.BillableArea`. - pub billable_area: i32, - /// C# member: `F:LibreMetaverse.ParcelInfo.Description`. - pub description: String, - /// C# member: `F:LibreMetaverse.ParcelInfo.Dwell`. - pub dwell: f32, - /// C# member: `F:LibreMetaverse.ParcelInfo.GlobalX`. - pub global_x: f32, - /// C# member: `F:LibreMetaverse.ParcelInfo.GlobalY`. - pub global_y: f32, - /// C# member: `F:LibreMetaverse.ParcelInfo.GlobalZ`. - pub global_z: f32, - /// C# member: `F:LibreMetaverse.ParcelInfo.ID`. - pub id: libremetaverse_types::UUID, - /// C# member: `F:LibreMetaverse.ParcelInfo.Mature`. - pub mature: bool, - /// C# member: `F:LibreMetaverse.ParcelInfo.Name`. - pub name: String, - /// C# member: `F:LibreMetaverse.ParcelInfo.OwnerID`. - pub owner_id: libremetaverse_types::UUID, - /// C# member: `F:LibreMetaverse.ParcelInfo.SalePrice`. - pub sale_price: i32, - /// C# member: `F:LibreMetaverse.ParcelInfo.SimName`. - pub sim_name: String, - /// C# member: `F:LibreMetaverse.ParcelInfo.SnapshotID`. - pub snapshot_id: libremetaverse_types::UUID, -} -impl ParcelInfo {} +/// C# member: `F:LibreMetaverse.ParcelInfo.ActualArea`. +/// C# member: `F:LibreMetaverse.ParcelInfo.AuctionID`. +/// C# member: `F:LibreMetaverse.ParcelInfo.BillableArea`. +/// C# member: `F:LibreMetaverse.ParcelInfo.Description`. +/// C# member: `F:LibreMetaverse.ParcelInfo.Dwell`. +/// C# member: `F:LibreMetaverse.ParcelInfo.GlobalX`. +/// C# member: `F:LibreMetaverse.ParcelInfo.GlobalY`. +/// C# member: `F:LibreMetaverse.ParcelInfo.GlobalZ`. +/// C# member: `F:LibreMetaverse.ParcelInfo.ID`. +/// C# member: `F:LibreMetaverse.ParcelInfo.Mature`. +/// C# member: `F:LibreMetaverse.ParcelInfo.Name`. +/// C# member: `F:LibreMetaverse.ParcelInfo.OwnerID`. +/// C# member: `F:LibreMetaverse.ParcelInfo.SalePrice`. +/// C# member: `F:LibreMetaverse.ParcelInfo.SimName`. +/// C# member: `F:LibreMetaverse.ParcelInfo.SnapshotID`. +pub use crate::parcel_manager::ParcelInfo; /// C# type: `T:LibreMetaverse.ParcelInfoReplyEventArgs`. -pub struct ParcelInfoReplyEventArgs; -impl ParcelInfoReplyEventArgs { - /// C# member: `M:LibreMetaverse.ParcelInfoReplyEventArgs.#ctor(LibreMetaverse.ParcelInfo)`. - pub fn new(parcel: libremetaverse::ParcelInfo) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelInfoReplyEventArgs.#ctor(LibreMetaverse.ParcelInfo)", - ) - } - /// C# member: `P:LibreMetaverse.ParcelInfoReplyEventArgs.Parcel`. - pub fn parcel(&self) -> libremetaverse::ParcelInfo { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ParcelInfoReplyEventArgs.Parcel") - } -} +/// C# member: `M:LibreMetaverse.ParcelInfoReplyEventArgs.#ctor(LibreMetaverse.ParcelInfo)`. +/// C# member: `P:LibreMetaverse.ParcelInfoReplyEventArgs.Parcel`. +pub use crate::parcel_manager::ParcelInfoReplyEventArgs; /// C# type: `T:LibreMetaverse.ParcelManager`. -pub struct ParcelManager; -impl ParcelManager { - /// C# member: `E:LibreMetaverse.ParcelManager.ForceSelectObjectsReply`. - pub fn subscribe_force_select_objects_reply( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::ForceSelectObjectsReplyEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.ParcelManager.ForceSelectObjectsReply" - ) - } - /// C# member: `E:LibreMetaverse.ParcelManager.ParcelAccessListReply`. - pub fn subscribe_parcel_access_list_reply( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::ParcelAccessListReplyEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.ParcelManager.ParcelAccessListReply" - ) - } - /// C# member: `E:LibreMetaverse.ParcelManager.ParcelDwellReply`. - pub fn subscribe_parcel_dwell_reply( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::ParcelDwellReplyEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ParcelManager.ParcelDwellReply") - } - /// C# member: `E:LibreMetaverse.ParcelManager.ParcelInfoReply`. - pub fn subscribe_parcel_info_reply( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::ParcelInfoReplyEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ParcelManager.ParcelInfoReply") - } - /// C# member: `E:LibreMetaverse.ParcelManager.ParcelMediaCommand`. - pub fn subscribe_parcel_media_command( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::ParcelMediaCommandEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.ParcelManager.ParcelMediaCommand" - ) - } - /// C# member: `E:LibreMetaverse.ParcelManager.ParcelMediaUpdateReply`. - pub fn subscribe_parcel_media_update_reply( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::ParcelMediaUpdateReplyEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.ParcelManager.ParcelMediaUpdateReply" - ) - } - /// C# member: `E:LibreMetaverse.ParcelManager.ParcelObjectOwnersReply`. - pub fn subscribe_parcel_object_owners_reply( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::ParcelObjectOwnersReplyEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.ParcelManager.ParcelObjectOwnersReply" - ) - } - /// C# member: `E:LibreMetaverse.ParcelManager.ParcelOverlaysDownloaded`. - pub fn subscribe_parcel_overlays_downloaded( - &self, - handler: libremetaverse_types::compat::EventHandler, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.ParcelManager.ParcelOverlaysDownloaded" - ) - } - /// C# member: `E:LibreMetaverse.ParcelManager.ParcelProperties`. - pub fn subscribe_parcel_properties( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::ParcelPropertiesEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.ParcelManager.ParcelProperties") - } - /// C# member: `E:LibreMetaverse.ParcelManager.SimParcelsDownloaded`. - pub fn subscribe_sim_parcels_downloaded( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::SimParcelsDownloadedEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.ParcelManager.SimParcelsDownloaded" - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.#ctor(LibreMetaverse.GridClient)`. - pub fn new(client: libremetaverse::GridClient) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.#ctor(LibreMetaverse.GridClient)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.Buy(LibreMetaverse.Simulator,System.Int32,System.Boolean,LibreMetaverse.UUID,System.Boolean,System.Int32,System.Int32)`. - pub fn buy( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - for_group: bool, - group_id: libremetaverse_types::UUID, - remove_contribution: bool, - parcel_area: i32, - parcel_price: i32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.Buy(LibreMetaverse.Simulator,System.Int32,System.Boolean,LibreMetaverse.UUID,System.Boolean,System.Int32,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.DeedToGroup(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.UUID)`. - pub fn deed_to_group( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - group_id: libremetaverse_types::UUID, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.DeedToGroup(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.UUID)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.Dispose`. - pub fn dispose(&self) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented("M:LibreMetaverse.ParcelManager.Dispose") - } - /// C# member: `M:LibreMetaverse.ParcelManager.EjectUser(LibreMetaverse.UUID,System.Boolean)`. - pub fn eject_user( - &self, - target_id: libremetaverse_types::UUID, - ban: bool, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.EjectUser(LibreMetaverse.UUID,System.Boolean)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.FreezeUser(LibreMetaverse.UUID,System.Boolean)`. - pub fn freeze_user( - &self, - target_id: libremetaverse_types::UUID, - freeze: bool, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.FreezeUser(LibreMetaverse.UUID,System.Boolean)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.GetParcelLocalID(LibreMetaverse.Simulator,LibreMetaverse.Vector3)`. - pub fn get_parcel_local_id( - &self, - simulator: libremetaverse::Simulator, - position: libremetaverse_types::Vector3, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.GetParcelLocalID(LibreMetaverse.Simulator,LibreMetaverse.Vector3)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.GetParcelResourcesAsync(LibreMetaverse.UUID,System.Boolean,System.Threading.CancellationToken)`. - pub async fn get_parcel_resources( - &self, - parcel_id: libremetaverse_types::UUID, - get_details: bool, - cancellation_token: Option, - ) -> Result<(bool, libremetaverse::messages::linden::LandResourcesInfo), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.GetParcelResourcesAsync(LibreMetaverse.UUID,System.Boolean,System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.ParcelJoin(LibreMetaverse.Simulator,System.Single,System.Single,System.Single,System.Single)`. - pub fn parcel_join( - &self, - simulator: libremetaverse::Simulator, - west: f32, - south: f32, - east: f32, - north: f32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.ParcelJoin(LibreMetaverse.Simulator,System.Single,System.Single,System.Single,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.ParcelSubdivide(LibreMetaverse.Simulator,System.Single,System.Single,System.Single,System.Single)`. - pub fn parcel_subdivide( - &self, - simulator: libremetaverse::Simulator, - west: f32, - south: f32, - east: f32, - north: f32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.ParcelSubdivide(LibreMetaverse.Simulator,System.Single,System.Single,System.Single,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.Reclaim(LibreMetaverse.Simulator,System.Int32)`. - pub fn reclaim( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.Reclaim(LibreMetaverse.Simulator,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.ReleaseParcel(LibreMetaverse.Simulator,System.Int32)`. - pub fn release_parcel( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.ReleaseParcel(LibreMetaverse.Simulator,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.RequestAllSimParcelsAsync(LibreMetaverse.Simulator,System.Boolean,System.TimeSpan,System.Threading.CancellationToken)`. - pub async fn request_all_sim_parcels_with_simulator_boolean_time_span_cancellation_token( - &self, - simulator: libremetaverse::Simulator, - refresh: bool, - delay: std::time::Duration, - cancellation_token: Option, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.RequestAllSimParcelsAsync(LibreMetaverse.Simulator,System.Boolean,System.TimeSpan,System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.RequestAllSimParcelsAsync(LibreMetaverse.Simulator,System.Threading.CancellationToken)`. - pub async fn request_all_sim_parcels_with_simulator_cancellation_token( - &self, - simulator: libremetaverse::Simulator, - cancellation_token: Option, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.RequestAllSimParcelsAsync(LibreMetaverse.Simulator,System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.RequestDwell(LibreMetaverse.Simulator,System.Int32)`. - pub fn request_dwell( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.RequestDwell(LibreMetaverse.Simulator,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.RequestObjectOwners(LibreMetaverse.Simulator,System.Int32)`. - pub fn request_object_owners( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.RequestObjectOwners(LibreMetaverse.Simulator,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.RequestParcelAccessList(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.AccessList,System.Int32)`. - pub fn request_parcel_access_list( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - flags: libremetaverse::AccessList, - sequence_id: i32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.RequestParcelAccessList(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.AccessList,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.RequestParcelInfo(LibreMetaverse.UUID)`. - pub fn request_parcel_info( - &self, - parcel_id: libremetaverse_types::UUID, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.RequestParcelInfo(LibreMetaverse.UUID)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.RequestParcelProperties(LibreMetaverse.Simulator,System.Int32,System.Int32)`. - pub fn request_parcel_properties_with_simulator_int32_int32( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - sequence_id: i32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.RequestParcelProperties(LibreMetaverse.Simulator,System.Int32,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.RequestParcelProperties(LibreMetaverse.Simulator,System.Single,System.Single,System.Single,System.Single,System.Int32,System.Boolean)`. - pub fn request_parcel_properties_with_simulator_single_single_single_single_int32_boolean( - &self, - simulator: libremetaverse::Simulator, - north: f32, - east: f32, - south: f32, - west: f32, - sequence_id: i32, - snap_selection: bool, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.RequestParcelProperties(LibreMetaverse.Simulator,System.Single,System.Single,System.Single,System.Single,System.Int32,System.Boolean)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.RequestRemoteParcelIDAsync(LibreMetaverse.Vector3,System.UInt64,LibreMetaverse.UUID,System.Threading.CancellationToken)`. - pub async fn request_remote_parcel_id( - &self, - location: libremetaverse_types::Vector3, - region_handle: u64, - region_id: libremetaverse_types::UUID, - cancellation_token: Option, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.RequestRemoteParcelIDAsync(LibreMetaverse.Vector3,System.UInt64,LibreMetaverse.UUID,System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.RequestSelectObjects(System.Int32,LibreMetaverse.ObjectReturnType,LibreMetaverse.UUID)`. - pub fn request_select_objects( - &self, - local_id: i32, - select_type: libremetaverse::ObjectReturnType, - owner_id: libremetaverse_types::UUID, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.RequestSelectObjects(System.Int32,LibreMetaverse.ObjectReturnType,LibreMetaverse.UUID)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.ReturnObjects(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.ObjectReturnType,System.Collections.Generic.List{LibreMetaverse.UUID})`. - pub fn return_objects( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - type_: libremetaverse::ObjectReturnType, - owner_i_ds: Vec, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.ReturnObjects(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.ObjectReturnType,System.Collections.Generic.List{LibreMetaverse.UUID})", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.StartAuction(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.UUID)`. - pub fn start_auction( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - snapshot_id: libremetaverse_types::UUID, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.StartAuction(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.UUID)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.Terraform(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.TerraformAction,LibreMetaverse.TerraformBrushSize)`. - pub fn terraform_with_simulator_int32_terraform_action_terraform_brush_size( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - action: libremetaverse::TerraformAction, - brush_size: libremetaverse::TerraformBrushSize, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.Terraform(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.TerraformAction,LibreMetaverse.TerraformBrushSize)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.Terraform(LibreMetaverse.Simulator,System.Int32,System.Single,System.Single,System.Single,System.Single,LibreMetaverse.TerraformAction,LibreMetaverse.TerraformBrushSize,System.Int32)`. - pub fn terraform_with_simulator_int32_single_single_single_single_terraform_action_terraform_brush_size_int32( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - west: f32, - south: f32, - east: f32, - north: f32, - action: libremetaverse::TerraformAction, - brush_size: libremetaverse::TerraformBrushSize, - seconds: Option, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.Terraform(LibreMetaverse.Simulator,System.Int32,System.Single,System.Single,System.Single,System.Single,LibreMetaverse.TerraformAction,LibreMetaverse.TerraformBrushSize,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.ParcelManager.Terraform(LibreMetaverse.Simulator,System.Int32,System.Single,System.Single,System.Single,System.Single,LibreMetaverse.TerraformAction,LibreMetaverse.TerraformBrushSize,System.Int32,System.Single)`. - pub fn terraform_with_simulator_int32_single_single_single_single_terraform_action_terraform_brush_size_int32_single( - &self, - simulator: libremetaverse::Simulator, - local_id: i32, - west: f32, - south: f32, - east: f32, - north: f32, - action: libremetaverse::TerraformAction, - brush_size: libremetaverse::TerraformBrushSize, - seconds: i32, - height: f32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelManager.Terraform(LibreMetaverse.Simulator,System.Int32,System.Single,System.Single,System.Single,System.Single,LibreMetaverse.TerraformAction,LibreMetaverse.TerraformBrushSize,System.Int32,System.Single)", - ) - } - /// C# member: `P:LibreMetaverse.ParcelManager.CurrentParcel`. - pub fn current_parcel(&self) -> Option { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ParcelManager.CurrentParcel") - } -} +/// C# member: `E:LibreMetaverse.ParcelManager.ForceSelectObjectsReply`. +/// C# member: `E:LibreMetaverse.ParcelManager.ParcelAccessListReply`. +/// C# member: `E:LibreMetaverse.ParcelManager.ParcelDwellReply`. +/// C# member: `E:LibreMetaverse.ParcelManager.ParcelInfoReply`. +/// C# member: `E:LibreMetaverse.ParcelManager.ParcelMediaCommand`. +/// C# member: `E:LibreMetaverse.ParcelManager.ParcelMediaUpdateReply`. +/// C# member: `E:LibreMetaverse.ParcelManager.ParcelObjectOwnersReply`. +/// C# member: `E:LibreMetaverse.ParcelManager.ParcelOverlaysDownloaded`. +/// C# member: `E:LibreMetaverse.ParcelManager.ParcelProperties`. +/// C# member: `E:LibreMetaverse.ParcelManager.SimParcelsDownloaded`. +/// C# member: `M:LibreMetaverse.ParcelManager.#ctor(LibreMetaverse.GridClient)`. +/// C# member: `M:LibreMetaverse.ParcelManager.Buy(LibreMetaverse.Simulator,System.Int32,System.Boolean,LibreMetaverse.UUID,System.Boolean,System.Int32,System.Int32)`. +/// C# member: `M:LibreMetaverse.ParcelManager.DeedToGroup(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.UUID)`. +/// C# member: `M:LibreMetaverse.ParcelManager.Dispose`. +/// C# member: `M:LibreMetaverse.ParcelManager.EjectUser(LibreMetaverse.UUID,System.Boolean)`. +/// C# member: `M:LibreMetaverse.ParcelManager.FreezeUser(LibreMetaverse.UUID,System.Boolean)`. +/// C# member: `M:LibreMetaverse.ParcelManager.GetParcelLocalID(LibreMetaverse.Simulator,LibreMetaverse.Vector3)`. +/// C# member: `M:LibreMetaverse.ParcelManager.GetParcelResourcesAsync(LibreMetaverse.UUID,System.Boolean,System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.ParcelManager.ParcelJoin(LibreMetaverse.Simulator,System.Single,System.Single,System.Single,System.Single)`. +/// C# member: `M:LibreMetaverse.ParcelManager.ParcelSubdivide(LibreMetaverse.Simulator,System.Single,System.Single,System.Single,System.Single)`. +/// C# member: `M:LibreMetaverse.ParcelManager.Reclaim(LibreMetaverse.Simulator,System.Int32)`. +/// C# member: `M:LibreMetaverse.ParcelManager.ReleaseParcel(LibreMetaverse.Simulator,System.Int32)`. +/// C# member: `M:LibreMetaverse.ParcelManager.RequestAllSimParcelsAsync(LibreMetaverse.Simulator,System.Boolean,System.TimeSpan,System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.ParcelManager.RequestAllSimParcelsAsync(LibreMetaverse.Simulator,System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.ParcelManager.RequestDwell(LibreMetaverse.Simulator,System.Int32)`. +/// C# member: `M:LibreMetaverse.ParcelManager.RequestObjectOwners(LibreMetaverse.Simulator,System.Int32)`. +/// C# member: `M:LibreMetaverse.ParcelManager.RequestParcelAccessList(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.AccessList,System.Int32)`. +/// C# member: `M:LibreMetaverse.ParcelManager.RequestParcelInfo(LibreMetaverse.UUID)`. +/// C# member: `M:LibreMetaverse.ParcelManager.RequestParcelProperties(LibreMetaverse.Simulator,System.Int32,System.Int32)`. +/// C# member: `M:LibreMetaverse.ParcelManager.RequestParcelProperties(LibreMetaverse.Simulator,System.Single,System.Single,System.Single,System.Single,System.Int32,System.Boolean)`. +/// C# member: `M:LibreMetaverse.ParcelManager.RequestRemoteParcelIDAsync(LibreMetaverse.Vector3,System.UInt64,LibreMetaverse.UUID,System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.ParcelManager.RequestSelectObjects(System.Int32,LibreMetaverse.ObjectReturnType,LibreMetaverse.UUID)`. +/// C# member: `M:LibreMetaverse.ParcelManager.ReturnObjects(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.ObjectReturnType,System.Collections.Generic.List{LibreMetaverse.UUID})`. +/// C# member: `M:LibreMetaverse.ParcelManager.StartAuction(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.UUID)`. +/// C# member: `M:LibreMetaverse.ParcelManager.Terraform(LibreMetaverse.Simulator,System.Int32,LibreMetaverse.TerraformAction,LibreMetaverse.TerraformBrushSize)`. +/// C# member: `M:LibreMetaverse.ParcelManager.Terraform(LibreMetaverse.Simulator,System.Int32,System.Single,System.Single,System.Single,System.Single,LibreMetaverse.TerraformAction,LibreMetaverse.TerraformBrushSize,System.Int32)`. +/// C# member: `M:LibreMetaverse.ParcelManager.Terraform(LibreMetaverse.Simulator,System.Int32,System.Single,System.Single,System.Single,System.Single,LibreMetaverse.TerraformAction,LibreMetaverse.TerraformBrushSize,System.Int32,System.Single)`. +/// C# member: `P:LibreMetaverse.ParcelManager.CurrentParcel`. +pub use crate::parcel_manager::ParcelManager; /// C# type: `T:LibreMetaverse.ParcelManager.ParcelAccessEntry`. -pub struct ParcelManagerParcelAccessEntry { - /// C# member: `F:LibreMetaverse.ParcelManager.ParcelAccessEntry.AgentID`. - pub agent_id: libremetaverse_types::UUID, - /// C# member: `F:LibreMetaverse.ParcelManager.ParcelAccessEntry.Flags`. - pub flags: libremetaverse::AccessList, - /// C# member: `F:LibreMetaverse.ParcelManager.ParcelAccessEntry.Time`. - pub time: std::time::SystemTime, -} -impl ParcelManagerParcelAccessEntry {} +/// C# member: `F:LibreMetaverse.ParcelManager.ParcelAccessEntry.AgentID`. +/// C# member: `F:LibreMetaverse.ParcelManager.ParcelAccessEntry.Flags`. +/// C# member: `F:LibreMetaverse.ParcelManager.ParcelAccessEntry.Time`. +pub use crate::parcel_manager::ParcelManagerParcelAccessEntry; /// C# type: `T:LibreMetaverse.ParcelManager.ParcelPrimOwners`. -pub struct ParcelManagerParcelPrimOwners { - /// C# member: `F:LibreMetaverse.ParcelManager.ParcelPrimOwners.Count`. - pub count: i32, - /// C# member: `F:LibreMetaverse.ParcelManager.ParcelPrimOwners.IsGroupOwned`. - pub is_group_owned: bool, - /// C# member: `F:LibreMetaverse.ParcelManager.ParcelPrimOwners.NewestPrim`. - pub newest_prim: std::time::SystemTime, - /// C# member: `F:LibreMetaverse.ParcelManager.ParcelPrimOwners.OnlineStatus`. - pub online_status: bool, - /// C# member: `F:LibreMetaverse.ParcelManager.ParcelPrimOwners.OwnerID`. - pub owner_id: libremetaverse_types::UUID, -} -impl ParcelManagerParcelPrimOwners {} +/// C# member: `F:LibreMetaverse.ParcelManager.ParcelPrimOwners.Count`. +/// C# member: `F:LibreMetaverse.ParcelManager.ParcelPrimOwners.IsGroupOwned`. +/// C# member: `F:LibreMetaverse.ParcelManager.ParcelPrimOwners.NewestPrim`. +/// C# member: `F:LibreMetaverse.ParcelManager.ParcelPrimOwners.OnlineStatus`. +/// C# member: `F:LibreMetaverse.ParcelManager.ParcelPrimOwners.OwnerID`. +pub use crate::parcel_manager::ParcelManagerParcelPrimOwners; /// C# type: `T:LibreMetaverse.ParcelMedia`. -pub struct ParcelMedia { - /// C# member: `F:LibreMetaverse.ParcelMedia.MediaAutoScale`. - pub media_auto_scale: bool, - /// C# member: `F:LibreMetaverse.ParcelMedia.MediaDesc`. - pub media_desc: String, - /// C# member: `F:LibreMetaverse.ParcelMedia.MediaHeight`. - pub media_height: i32, - /// C# member: `F:LibreMetaverse.ParcelMedia.MediaID`. - pub media_id: libremetaverse_types::UUID, - /// C# member: `F:LibreMetaverse.ParcelMedia.MediaLoop`. - pub media_loop: bool, - /// C# member: `F:LibreMetaverse.ParcelMedia.MediaType`. - pub media_type: String, - /// C# member: `F:LibreMetaverse.ParcelMedia.MediaURL`. - pub media_url: String, - /// C# member: `F:LibreMetaverse.ParcelMedia.MediaWidth`. - pub media_width: i32, -} -impl ParcelMedia {} +/// C# member: `F:LibreMetaverse.ParcelMedia.MediaAutoScale`. +/// C# member: `F:LibreMetaverse.ParcelMedia.MediaDesc`. +/// C# member: `F:LibreMetaverse.ParcelMedia.MediaHeight`. +/// C# member: `F:LibreMetaverse.ParcelMedia.MediaID`. +/// C# member: `F:LibreMetaverse.ParcelMedia.MediaLoop`. +/// C# member: `F:LibreMetaverse.ParcelMedia.MediaType`. +/// C# member: `F:LibreMetaverse.ParcelMedia.MediaURL`. +/// C# member: `F:LibreMetaverse.ParcelMedia.MediaWidth`. +pub use crate::parcel_manager::ParcelMedia; /// C# type: `T:LibreMetaverse.ParcelMediaCommand`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -17329,120 +16481,30 @@ pub enum ParcelMediaCommand { } /// C# type: `T:LibreMetaverse.ParcelMediaCommandEventArgs`. -pub struct ParcelMediaCommandEventArgs; -impl ParcelMediaCommandEventArgs { - /// C# member: `M:LibreMetaverse.ParcelMediaCommandEventArgs.#ctor(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.ParcelFlags,LibreMetaverse.ParcelMediaCommand,System.Single)`. - pub fn new( - simulator: libremetaverse::Simulator, - sequence: u32, - flags: libremetaverse::ParcelFlags, - command: libremetaverse::ParcelMediaCommand, - time: f32, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelMediaCommandEventArgs.#ctor(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.ParcelFlags,LibreMetaverse.ParcelMediaCommand,System.Single)", - ) - } - /// C# member: `P:LibreMetaverse.ParcelMediaCommandEventArgs.MediaCommand`. - pub fn media_command(&self) -> libremetaverse::ParcelMediaCommand { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelMediaCommandEventArgs.MediaCommand" - ) - } - /// C# member: `P:LibreMetaverse.ParcelMediaCommandEventArgs.ParcelFlags`. - pub fn parcel_flags(&self) -> libremetaverse::ParcelFlags { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelMediaCommandEventArgs.ParcelFlags" - ) - } - /// C# member: `P:LibreMetaverse.ParcelMediaCommandEventArgs.Sequence`. - pub fn sequence(&self) -> u32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelMediaCommandEventArgs.Sequence" - ) - } - /// C# member: `P:LibreMetaverse.ParcelMediaCommandEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelMediaCommandEventArgs.Simulator" - ) - } - /// C# member: `P:LibreMetaverse.ParcelMediaCommandEventArgs.Time`. - pub fn time(&self) -> f32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelMediaCommandEventArgs.Time" - ) - } -} +/// C# member: `M:LibreMetaverse.ParcelMediaCommandEventArgs.#ctor(LibreMetaverse.Simulator,System.UInt32,LibreMetaverse.ParcelFlags,LibreMetaverse.ParcelMediaCommand,System.Single)`. +/// C# member: `P:LibreMetaverse.ParcelMediaCommandEventArgs.MediaCommand`. +/// C# member: `P:LibreMetaverse.ParcelMediaCommandEventArgs.ParcelFlags`. +/// C# member: `P:LibreMetaverse.ParcelMediaCommandEventArgs.Sequence`. +/// C# member: `P:LibreMetaverse.ParcelMediaCommandEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.ParcelMediaCommandEventArgs.Time`. +pub use crate::parcel_manager::ParcelMediaCommandEventArgs; /// C# type: `T:LibreMetaverse.ParcelMediaUpdateReplyEventArgs`. -pub struct ParcelMediaUpdateReplyEventArgs; -impl ParcelMediaUpdateReplyEventArgs { - /// C# member: `M:LibreMetaverse.ParcelMediaUpdateReplyEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.ParcelMedia)`. - pub fn new( - simulator: libremetaverse::Simulator, - media: libremetaverse::ParcelMedia, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelMediaUpdateReplyEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.ParcelMedia)", - ) - } - /// C# member: `P:LibreMetaverse.ParcelMediaUpdateReplyEventArgs.Media`. - pub fn media(&self) -> libremetaverse::ParcelMedia { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelMediaUpdateReplyEventArgs.Media" - ) - } - /// C# member: `P:LibreMetaverse.ParcelMediaUpdateReplyEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelMediaUpdateReplyEventArgs.Simulator" - ) - } -} +/// C# member: `M:LibreMetaverse.ParcelMediaUpdateReplyEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.ParcelMedia)`. +/// C# member: `P:LibreMetaverse.ParcelMediaUpdateReplyEventArgs.Media`. +/// C# member: `P:LibreMetaverse.ParcelMediaUpdateReplyEventArgs.Simulator`. +pub use crate::parcel_manager::ParcelMediaUpdateReplyEventArgs; /// C# type: `T:LibreMetaverse.ParcelObjectOwnersReplyEventArgs`. -pub struct ParcelObjectOwnersReplyEventArgs; -impl ParcelObjectOwnersReplyEventArgs { - /// C# member: `M:LibreMetaverse.ParcelObjectOwnersReplyEventArgs.#ctor(LibreMetaverse.Simulator,System.Collections.Generic.List{LibreMetaverse.ParcelManager.ParcelPrimOwners})`. - pub fn new( - simulator: libremetaverse::Simulator, - prim_owners: Vec, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelObjectOwnersReplyEventArgs.#ctor(LibreMetaverse.Simulator,System.Collections.Generic.List{LibreMetaverse.ParcelManager.ParcelPrimOwners})", - ) - } - /// C# member: `P:LibreMetaverse.ParcelObjectOwnersReplyEventArgs.PrimOwners`. - pub fn prim_owners(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelObjectOwnersReplyEventArgs.PrimOwners" - ) - } - /// C# member: `P:LibreMetaverse.ParcelObjectOwnersReplyEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelObjectOwnersReplyEventArgs.Simulator" - ) - } -} +/// C# member: `M:LibreMetaverse.ParcelObjectOwnersReplyEventArgs.#ctor(LibreMetaverse.Simulator,System.Collections.Generic.List{LibreMetaverse.ParcelManager.ParcelPrimOwners})`. +/// C# member: `P:LibreMetaverse.ParcelObjectOwnersReplyEventArgs.PrimOwners`. +/// C# member: `P:LibreMetaverse.ParcelObjectOwnersReplyEventArgs.Simulator`. +pub use crate::parcel_manager::ParcelObjectOwnersReplyEventArgs; /// C# type: `T:LibreMetaverse.ParcelOverlayEventArgs`. -pub struct ParcelOverlayEventArgs; -impl ParcelOverlayEventArgs { - /// C# member: `M:LibreMetaverse.ParcelOverlayEventArgs.#ctor(LibreMetaverse.Simulator)`. - pub fn new(simulator: libremetaverse::Simulator) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelOverlayEventArgs.#ctor(LibreMetaverse.Simulator)", - ) - } - /// C# member: `P:LibreMetaverse.ParcelOverlayEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelOverlayEventArgs.Simulator" - ) - } -} +/// C# member: `M:LibreMetaverse.ParcelOverlayEventArgs.#ctor(LibreMetaverse.Simulator)`. +/// C# member: `P:LibreMetaverse.ParcelOverlayEventArgs.Simulator`. +pub use crate::parcel_manager::ParcelOverlayEventArgs; /// C# type: `T:LibreMetaverse.ParcelOverlayType`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -17470,58 +16532,14 @@ impl ParcelOverlayType { } /// C# type: `T:LibreMetaverse.ParcelPropertiesEventArgs`. -pub struct ParcelPropertiesEventArgs; -impl ParcelPropertiesEventArgs { - /// C# member: `M:LibreMetaverse.ParcelPropertiesEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Parcel,LibreMetaverse.ParcelResult,System.Int32,System.Int32,System.Boolean)`. - pub fn new( - simulator: libremetaverse::Simulator, - parcel: libremetaverse::Parcel, - result: libremetaverse::ParcelResult, - selected_prims: i32, - sequence_id: i32, - snap_selection: bool, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.ParcelPropertiesEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Parcel,LibreMetaverse.ParcelResult,System.Int32,System.Int32,System.Boolean)", - ) - } - /// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.Parcel`. - pub fn parcel(&self) -> libremetaverse::Parcel { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelPropertiesEventArgs.Parcel" - ) - } - /// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.Result`. - pub fn result(&self) -> libremetaverse::ParcelResult { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelPropertiesEventArgs.Result" - ) - } - /// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.SelectedPrims`. - pub fn selected_prims(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelPropertiesEventArgs.SelectedPrims" - ) - } - /// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.SequenceID`. - pub fn sequence_id(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelPropertiesEventArgs.SequenceID" - ) - } - /// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelPropertiesEventArgs.Simulator" - ) - } - /// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.SnapSelection`. - pub fn snap_selection(&self) -> bool { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.ParcelPropertiesEventArgs.SnapSelection" - ) - } -} +/// C# member: `M:LibreMetaverse.ParcelPropertiesEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Parcel,LibreMetaverse.ParcelResult,System.Int32,System.Int32,System.Boolean)`. +/// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.Parcel`. +/// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.Result`. +/// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.SelectedPrims`. +/// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.SequenceID`. +/// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.ParcelPropertiesEventArgs.SnapSelection`. +pub use crate::parcel_manager::ParcelPropertiesEventArgs; /// C# type: `T:LibreMetaverse.ParcelPropertiesStatus`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -17910,36 +16928,12 @@ impl PositionHelper { } /// C# type: `T:LibreMetaverse.PreloadSoundEventArgs`. -pub struct PreloadSoundEventArgs; -impl PreloadSoundEventArgs { - /// C# member: `M:LibreMetaverse.PreloadSoundEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID)`. - pub fn new( - sim: libremetaverse::Simulator, - sound_id: libremetaverse_types::UUID, - owner_id: libremetaverse_types::UUID, - object_id: libremetaverse_types::UUID, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.PreloadSoundEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID)", - ) - } - /// C# member: `P:LibreMetaverse.PreloadSoundEventArgs.ObjectID`. - pub fn object_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PreloadSoundEventArgs.ObjectID") - } - /// C# member: `P:LibreMetaverse.PreloadSoundEventArgs.OwnerID`. - pub fn owner_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PreloadSoundEventArgs.OwnerID") - } - /// C# member: `P:LibreMetaverse.PreloadSoundEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PreloadSoundEventArgs.Simulator") - } - /// C# member: `P:LibreMetaverse.PreloadSoundEventArgs.SoundID`. - pub fn sound_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.PreloadSoundEventArgs.SoundID") - } -} +/// C# member: `M:LibreMetaverse.PreloadSoundEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID)`. +/// C# member: `P:LibreMetaverse.PreloadSoundEventArgs.ObjectID`. +/// C# member: `P:LibreMetaverse.PreloadSoundEventArgs.OwnerID`. +/// C# member: `P:LibreMetaverse.PreloadSoundEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.PreloadSoundEventArgs.SoundID`. +pub use crate::sound_manager::PreloadSoundEventArgs; /// C# type: `T:LibreMetaverse.PrimEventArgs`. /// C# member: `M:LibreMetaverse.PrimEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,System.UInt16,System.Boolean,System.Boolean)`. @@ -18525,23 +17519,9 @@ pub use crate::agent_movement::RegionCrossedEventArgs; pub use crate::agent_movement::RegionCrossingPredictionEventArgs; /// C# type: `T:LibreMetaverse.RegionEnvironmentEventArgs`. -pub struct RegionEnvironmentEventArgs; -impl RegionEnvironmentEventArgs { - /// C# member: `M:LibreMetaverse.RegionEnvironmentEventArgs.#ctor(LibreMetaverse.Messages.Linden.ExtEnvironmentMessage)`. - pub fn new( - environment: libremetaverse::messages::linden::ExtEnvironmentMessage, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.RegionEnvironmentEventArgs.#ctor(LibreMetaverse.Messages.Linden.ExtEnvironmentMessage)", - ) - } - /// C# member: `P:LibreMetaverse.RegionEnvironmentEventArgs.Environment`. - pub fn environment(&self) -> libremetaverse::messages::linden::ExtEnvironmentMessage { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.RegionEnvironmentEventArgs.Environment" - ) - } -} +/// C# member: `M:LibreMetaverse.RegionEnvironmentEventArgs.#ctor(LibreMetaverse.Messages.Linden.ExtEnvironmentMessage)`. +/// C# member: `P:LibreMetaverse.RegionEnvironmentEventArgs.Environment`. +pub use crate::environment_manager::RegionEnvironmentEventArgs; /// C# type: `T:LibreMetaverse.RegionExperiencesEventArgs`. /// C# member: `M:LibreMetaverse.RegionExperiencesEventArgs.#ctor(LibreMetaverse.Messages.Linden.RegionExperiencesMessage)`. @@ -19263,39 +18243,11 @@ pub use crate::network_manager::SimConnectingEventArgs; pub use crate::network_manager::SimDisconnectedEventArgs; /// C# type: `T:LibreMetaverse.SimParcelsDownloadedEventArgs`. -pub struct SimParcelsDownloadedEventArgs; -impl SimParcelsDownloadedEventArgs { - /// C# member: `M:LibreMetaverse.SimParcelsDownloadedEventArgs.#ctor(LibreMetaverse.Simulator,System.Collections.Concurrent.ConcurrentDictionary{System.Int32,LibreMetaverse.Parcel},System.Int32[0:,0:])`. - pub fn new( - simulator: libremetaverse::Simulator, - sim_parcels: std::sync::RwLock>, - parcel_map: Vec, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.SimParcelsDownloadedEventArgs.#ctor(LibreMetaverse.Simulator,System.Collections.Concurrent.ConcurrentDictionary{System.Int32,LibreMetaverse.Parcel},System.Int32[0:,0:])", - ) - } - /// C# member: `P:LibreMetaverse.SimParcelsDownloadedEventArgs.ParcelMap`. - pub fn parcel_map(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.SimParcelsDownloadedEventArgs.ParcelMap" - ) - } - /// C# member: `P:LibreMetaverse.SimParcelsDownloadedEventArgs.Parcels`. - pub fn parcels( - &self, - ) -> std::sync::RwLock> { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.SimParcelsDownloadedEventArgs.Parcels" - ) - } - /// C# member: `P:LibreMetaverse.SimParcelsDownloadedEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.SimParcelsDownloadedEventArgs.Simulator" - ) - } -} +/// C# member: `M:LibreMetaverse.SimParcelsDownloadedEventArgs.#ctor(LibreMetaverse.Simulator,System.Collections.Concurrent.ConcurrentDictionary{System.Int32,LibreMetaverse.Parcel},System.Int32[0:,0:])`. +/// C# member: `P:LibreMetaverse.SimParcelsDownloadedEventArgs.ParcelMap`. +/// C# member: `P:LibreMetaverse.SimParcelsDownloadedEventArgs.Parcels`. +/// C# member: `P:LibreMetaverse.SimParcelsDownloadedEventArgs.Simulator`. +pub use crate::parcel_manager::SimParcelsDownloadedEventArgs; /// C# type: `T:LibreMetaverse.Simulator`. pub use crate::network_manager::Simulator; @@ -19407,7 +18359,8 @@ impl Simulator { } /// C# member: `M:LibreMetaverse.Simulator.IsParcelMapFull`. pub fn is_parcel_map_full(&self) -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.Simulator.IsParcelMapFull") + /* native network implementation */ + Ok(self.native_is_parcel_map_full()) } /// C# member: `M:LibreMetaverse.Simulator.Pause`. pub fn pause(&self) -> Result<(), crate::Error> { @@ -19456,9 +18409,8 @@ impl Simulator { y: i32, height: &mut f32, ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Simulator.TerrainHeightAtPoint(System.Int32,System.Int32,System.Single@)", - ) + /* native network implementation */ + self.native_terrain_height_at_point(x, y, height) } /// C# member: `M:LibreMetaverse.Simulator.ToString`. pub fn to_string(&self) -> String { @@ -19511,13 +18463,15 @@ impl Simulator { } /// C# member: `P:LibreMetaverse.Simulator.ParcelMap`. pub fn parcel_map(&self) -> Vec { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Simulator.ParcelMap") + /* native network implementation */ + self.native_parcel_map_snapshot() } /// C# member: `P:LibreMetaverse.Simulator.Parcels`. pub fn parcels( &self, ) -> std::sync::RwLock> { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Simulator.Parcels") + /* native network implementation */ + std::sync::RwLock::new(self.native_parcels_snapshot()) } /// C# member: `P:LibreMetaverse.Simulator.PreyID`. pub fn prey_id(&self) -> libremetaverse_types::UUID { @@ -19899,156 +18853,30 @@ impl SlurlParser { } /// C# type: `T:LibreMetaverse.SoundManager`. -pub struct SoundManager; -impl SoundManager { - /// C# member: `E:LibreMetaverse.SoundManager.AttachedSound`. - pub fn subscribe_attached_sound( - &self, - handler: libremetaverse_types::compat::EventHandler, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.SoundManager.AttachedSound") - } - /// C# member: `E:LibreMetaverse.SoundManager.AttachedSoundGainChange`. - pub fn subscribe_attached_sound_gain_change( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::AttachedSoundGainChangeEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.SoundManager.AttachedSoundGainChange" - ) - } - /// C# member: `E:LibreMetaverse.SoundManager.PreloadSound`. - pub fn subscribe_preload_sound( - &self, - handler: libremetaverse_types::compat::EventHandler, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.SoundManager.PreloadSound") - } - /// C# member: `E:LibreMetaverse.SoundManager.SoundTrigger`. - pub fn subscribe_sound_trigger( - &self, - handler: libremetaverse_types::compat::EventHandler, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.SoundManager.SoundTrigger") - } - /// C# member: `M:LibreMetaverse.SoundManager.#ctor(LibreMetaverse.GridClient)`. - pub fn new(client: libremetaverse::GridClient) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.SoundManager.#ctor(LibreMetaverse.GridClient)", - ) - } - /// C# member: `M:LibreMetaverse.SoundManager.Dispose`. - pub fn dispose(&self) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented("M:LibreMetaverse.SoundManager.Dispose") - } - /// C# member: `M:LibreMetaverse.SoundManager.PlaySound(LibreMetaverse.UUID)`. - pub fn play_sound(&self, sound_id: libremetaverse_types::UUID) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.SoundManager.PlaySound(LibreMetaverse.UUID)", - ) - } - /// C# member: `M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,LibreMetaverse.Simulator,LibreMetaverse.Vector3,System.Single)`. - pub fn send_sound_trigger_with_uuid_simulator_vector3_single( - &self, - sound_id: libremetaverse_types::UUID, - sim: libremetaverse::Simulator, - position: libremetaverse_types::Vector3, - gain: f32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,LibreMetaverse.Simulator,LibreMetaverse.Vector3,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,LibreMetaverse.Vector3)`. - pub fn send_sound_trigger_with_uuid_vector3( - &self, - sound_id: libremetaverse_types::UUID, - position: libremetaverse_types::Vector3, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,LibreMetaverse.Vector3)", - ) - } - /// C# member: `M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,LibreMetaverse.Vector3,System.Single)`. - pub fn send_sound_trigger_with_uuid_vector3_single( - &self, - sound_id: libremetaverse_types::UUID, - position: libremetaverse_types::Vector3, - gain: f32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,LibreMetaverse.Vector3,System.Single)", - ) - } - /// C# member: `M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,System.UInt64,LibreMetaverse.Vector3,System.Single)`. - pub fn send_sound_trigger_with_uuid_u_int64_vector3_single( - &self, - sound_id: libremetaverse_types::UUID, - handle: u64, - position: libremetaverse_types::Vector3, - gain: f32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,System.UInt64,LibreMetaverse.Vector3,System.Single)", - ) - } -} +/// C# member: `E:LibreMetaverse.SoundManager.AttachedSound`. +/// C# member: `E:LibreMetaverse.SoundManager.AttachedSoundGainChange`. +/// C# member: `E:LibreMetaverse.SoundManager.PreloadSound`. +/// C# member: `E:LibreMetaverse.SoundManager.SoundTrigger`. +/// C# member: `M:LibreMetaverse.SoundManager.#ctor(LibreMetaverse.GridClient)`. +/// C# member: `M:LibreMetaverse.SoundManager.Dispose`. +/// C# member: `M:LibreMetaverse.SoundManager.PlaySound(LibreMetaverse.UUID)`. +/// C# member: `M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,LibreMetaverse.Simulator,LibreMetaverse.Vector3,System.Single)`. +/// C# member: `M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,LibreMetaverse.Vector3)`. +/// C# member: `M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,LibreMetaverse.Vector3,System.Single)`. +/// C# member: `M:LibreMetaverse.SoundManager.SendSoundTrigger(LibreMetaverse.UUID,System.UInt64,LibreMetaverse.Vector3,System.Single)`. +pub use crate::sound_manager::SoundManager; /// C# type: `T:LibreMetaverse.SoundTriggerEventArgs`. -pub struct SoundTriggerEventArgs; -impl SoundTriggerEventArgs { - /// C# member: `M:LibreMetaverse.SoundTriggerEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID,System.Single,System.UInt64,LibreMetaverse.Vector3)`. - pub fn new( - sim: libremetaverse::Simulator, - sound_id: libremetaverse_types::UUID, - owner_id: libremetaverse_types::UUID, - object_id: libremetaverse_types::UUID, - parent_id: libremetaverse_types::UUID, - gain: f32, - region_handle: u64, - position: libremetaverse_types::Vector3, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.SoundTriggerEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID,System.Single,System.UInt64,LibreMetaverse.Vector3)", - ) - } - /// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.Gain`. - pub fn gain(&self) -> f32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.SoundTriggerEventArgs.Gain") - } - /// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.ObjectID`. - pub fn object_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.SoundTriggerEventArgs.ObjectID") - } - /// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.OwnerID`. - pub fn owner_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.SoundTriggerEventArgs.OwnerID") - } - /// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.ParentID`. - pub fn parent_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.SoundTriggerEventArgs.ParentID") - } - /// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.Position`. - pub fn position(&self) -> libremetaverse_types::Vector3 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.SoundTriggerEventArgs.Position") - } - /// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.RegionHandle`. - pub fn region_handle(&self) -> u64 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.SoundTriggerEventArgs.RegionHandle" - ) - } - /// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.Simulator`. - pub fn simulator(&self) -> libremetaverse::Simulator { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.SoundTriggerEventArgs.Simulator") - } - /// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.SoundID`. - pub fn sound_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.SoundTriggerEventArgs.SoundID") - } -} +/// C# member: `M:LibreMetaverse.SoundTriggerEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.UUID,System.Single,System.UInt64,LibreMetaverse.Vector3)`. +/// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.Gain`. +/// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.ObjectID`. +/// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.OwnerID`. +/// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.ParentID`. +/// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.Position`. +/// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.RegionHandle`. +/// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.Simulator`. +/// C# member: `P:LibreMetaverse.SoundTriggerEventArgs.SoundID`. +pub use crate::sound_manager::SoundTriggerEventArgs; /// C# type: `T:LibreMetaverse.Sounds`. pub struct Sounds; @@ -20410,255 +19238,55 @@ pub enum TerraformBrushSize { } /// C# type: `T:LibreMetaverse.TerrainCompressor`. -pub struct TerrainCompressor; -impl TerrainCompressor { - /// C# member: `F:LibreMetaverse.TerrainCompressor.END_OF_PATCHES`. - pub const END_OF_PATCHES: i32 = 97; - /// C# member: `F:LibreMetaverse.TerrainCompressor.PATCHES_PER_EDGE`. - pub const PATCHES_PER_EDGE: i32 = 16; - /// C# member: `M:LibreMetaverse.TerrainCompressor.CreateLandPacket(System.Single[0:,0:],System.Int32,System.Int32)`. - pub fn create_land_packet_with_single_array_int32_int32( - patch_data: Vec, - x: i32, - y: i32, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.CreateLandPacket(System.Single[0:,0:],System.Int32,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainCompressor.CreateLandPacket(System.Single[],System.Int32,System.Int32)`. - pub fn create_land_packet_with_single_array_int32_int32_80090bdf( - patch_data: Vec, - x: i32, - y: i32, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.CreateLandPacket(System.Single[],System.Int32,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainCompressor.CreateLandPacket(System.Single[],System.Int32[])`. - pub fn create_land_packet_with_single_array_int32_array( - heightmap: Vec, - patches: Vec, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.CreateLandPacket(System.Single[],System.Int32[])", - ) - } - /// C# member: `M:LibreMetaverse.TerrainCompressor.CreateLayerDataPacket(LibreMetaverse.TerrainPatch[],LibreMetaverse.TerrainPatch.LayerType)`. - pub fn create_layer_data_packet( - patches: Vec, - type_: libremetaverse::TerrainPatchLayerType, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.CreateLayerDataPacket(LibreMetaverse.TerrainPatch[],LibreMetaverse.TerrainPatch.LayerType)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainCompressor.CreatePatch(LibreMetaverse.BitPack,System.Single[0:,0:],System.Int32,System.Int32)`. - pub fn create_patch_with_bit_pack_single_array_int32_int32( - output: libremetaverse::BitPack, - patch_data: Vec, - x: i32, - y: i32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.CreatePatch(LibreMetaverse.BitPack,System.Single[0:,0:],System.Int32,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainCompressor.CreatePatch(LibreMetaverse.BitPack,System.Single[0:,0:],System.Int32,System.Int32,System.Boolean)`. - pub fn create_patch_with_bit_pack_single_array_int32_int32_boolean( - output: libremetaverse::BitPack, - patch_data: Vec, - x: i32, - y: i32, - large_region: bool, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.CreatePatch(LibreMetaverse.BitPack,System.Single[0:,0:],System.Int32,System.Int32,System.Boolean)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainCompressor.CreatePatchFromHeightmap(LibreMetaverse.BitPack,System.Single[],System.Int32,System.Int32)`. - pub fn create_patch_from_heightmap_with_bit_pack_single_array_int32_int32( - output: libremetaverse::BitPack, - heightmap: Vec, - x: i32, - y: i32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.CreatePatchFromHeightmap(LibreMetaverse.BitPack,System.Single[],System.Int32,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainCompressor.CreatePatchFromHeightmap(LibreMetaverse.BitPack,System.Single[],System.Int32,System.Int32,System.Boolean)`. - pub fn create_patch_from_heightmap_with_bit_pack_single_array_int32_int32_boolean( - output: libremetaverse::BitPack, - heightmap: Vec, - x: i32, - y: i32, - large_region: bool, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.CreatePatchFromHeightmap(LibreMetaverse.BitPack,System.Single[],System.Int32,System.Int32,System.Boolean)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainCompressor.DecodePatch(System.Int32[],LibreMetaverse.BitPack,LibreMetaverse.TerrainPatch.Header,System.Int32)`. - pub fn decode_patch( - patches: Vec, - bitpack: libremetaverse::BitPack, - header: libremetaverse::TerrainPatchHeader, - size: i32, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.DecodePatch(System.Int32[],LibreMetaverse.BitPack,LibreMetaverse.TerrainPatch.Header,System.Int32)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainCompressor.DecodePatchHeader(LibreMetaverse.BitPack)`. - pub fn decode_patch_header_with_bit_pack( - bitpack: libremetaverse::BitPack, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.DecodePatchHeader(LibreMetaverse.BitPack)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainCompressor.DecodePatchHeader(LibreMetaverse.BitPack,System.Boolean)`. - pub fn decode_patch_header_with_bit_pack_boolean( - bitpack: libremetaverse::BitPack, - large_region: bool, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.DecodePatchHeader(LibreMetaverse.BitPack,System.Boolean)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainCompressor.DecompressPatch(System.Int32[],LibreMetaverse.TerrainPatch.Header,LibreMetaverse.TerrainPatch.GroupHeader)`. - pub fn decompress_patch( - patches: Vec, - header: libremetaverse::TerrainPatchHeader, - group: libremetaverse::TerrainPatchGroupHeader, - ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainCompressor.DecompressPatch(System.Int32[],LibreMetaverse.TerrainPatch.Header,LibreMetaverse.TerrainPatch.GroupHeader)", - ) - } -} +/// C# member: `F:LibreMetaverse.TerrainCompressor.END_OF_PATCHES`. +/// C# member: `F:LibreMetaverse.TerrainCompressor.PATCHES_PER_EDGE`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.CreateLandPacket(System.Single[0:,0:],System.Int32,System.Int32)`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.CreateLandPacket(System.Single[],System.Int32,System.Int32)`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.CreateLandPacket(System.Single[],System.Int32[])`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.CreateLayerDataPacket(LibreMetaverse.TerrainPatch[],LibreMetaverse.TerrainPatch.LayerType)`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.CreatePatch(LibreMetaverse.BitPack,System.Single[0:,0:],System.Int32,System.Int32)`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.CreatePatch(LibreMetaverse.BitPack,System.Single[0:,0:],System.Int32,System.Int32,System.Boolean)`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.CreatePatchFromHeightmap(LibreMetaverse.BitPack,System.Single[],System.Int32,System.Int32)`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.CreatePatchFromHeightmap(LibreMetaverse.BitPack,System.Single[],System.Int32,System.Int32,System.Boolean)`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.DecodePatch(System.Int32[],LibreMetaverse.BitPack,LibreMetaverse.TerrainPatch.Header,System.Int32)`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.DecodePatchHeader(LibreMetaverse.BitPack)`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.DecodePatchHeader(LibreMetaverse.BitPack,System.Boolean)`. +/// C# member: `M:LibreMetaverse.TerrainCompressor.DecompressPatch(System.Int32[],LibreMetaverse.TerrainPatch.Header,LibreMetaverse.TerrainPatch.GroupHeader)`. +pub use crate::terrain_codec::TerrainCompressor; /// C# type: `T:LibreMetaverse.TerrainManager`. -pub struct TerrainManager; -impl TerrainManager { - /// C# member: `E:LibreMetaverse.TerrainManager.LandPatchReceived`. - pub fn subscribe_land_patch_received( - &self, - handler: libremetaverse_types::compat::EventHandler< - libremetaverse::LandPatchReceivedEventArgs, - >, - ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!( - "E:LibreMetaverse.TerrainManager.LandPatchReceived" - ) - } - /// C# member: `F:LibreMetaverse.TerrainManager.TerrainMaterialSlotCount`. - pub const TERRAIN_MATERIAL_SLOT_COUNT: i32 = 4; - /// C# member: `M:LibreMetaverse.TerrainManager.#ctor(LibreMetaverse.GridClient)`. - pub fn new(client: libremetaverse::GridClient) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainManager.#ctor(LibreMetaverse.GridClient)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainManager.Dispose`. - pub fn dispose(&self) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented("M:LibreMetaverse.TerrainManager.Dispose") - } - /// C# member: `M:LibreMetaverse.TerrainManager.GetTerrainMaterialOverridesAsync(System.Threading.CancellationToken)`. - pub async fn get_terrain_material_overrides( - &self, - cancellation_token: Option, - ) -> Result>>, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainManager.GetTerrainMaterialOverridesAsync(System.Threading.CancellationToken)", - ) - } - /// C# member: `M:LibreMetaverse.TerrainManager.SetTerrainMaterialOverridesAsync(LibreMetaverse.Assets.AssetMaterial[],System.Threading.CancellationToken)`. - pub async fn set_terrain_material_overrides( - &self, - overrides: Vec>, - cancellation_token: Option, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainManager.SetTerrainMaterialOverridesAsync(LibreMetaverse.Assets.AssetMaterial[],System.Threading.CancellationToken)", - ) - } -} +/// C# member: `E:LibreMetaverse.TerrainManager.LandPatchReceived`. +/// C# member: `F:LibreMetaverse.TerrainManager.TerrainMaterialSlotCount`. +/// C# member: `M:LibreMetaverse.TerrainManager.#ctor(LibreMetaverse.GridClient)`. +/// C# member: `M:LibreMetaverse.TerrainManager.Dispose`. +/// C# member: `M:LibreMetaverse.TerrainManager.GetTerrainMaterialOverridesAsync(System.Threading.CancellationToken)`. +/// C# member: `M:LibreMetaverse.TerrainManager.SetTerrainMaterialOverridesAsync(LibreMetaverse.Assets.AssetMaterial[],System.Threading.CancellationToken)`. +pub use crate::terrain_manager::TerrainManager; /// C# type: `T:LibreMetaverse.TerrainPatch`. -pub struct TerrainPatch { - /// C# member: `F:LibreMetaverse.TerrainPatch.Data`. - pub data: Vec, - /// C# member: `F:LibreMetaverse.TerrainPatch.X`. - pub x: i32, - /// C# member: `F:LibreMetaverse.TerrainPatch.Y`. - pub y: i32, -} -impl TerrainPatch { - /// C# member: `M:LibreMetaverse.TerrainPatch.#ctor`. - pub fn new() -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.TerrainPatch.#ctor") - } -} +/// C# member: `F:LibreMetaverse.TerrainPatch.Data`. +/// C# member: `F:LibreMetaverse.TerrainPatch.X`. +/// C# member: `F:LibreMetaverse.TerrainPatch.Y`. +/// C# member: `M:LibreMetaverse.TerrainPatch.#ctor`. +pub use crate::terrain_codec::TerrainPatch; /// C# type: `T:LibreMetaverse.TerrainPatch.GroupHeader`. -pub struct TerrainPatchGroupHeader { - /// C# member: `F:LibreMetaverse.TerrainPatch.GroupHeader.PatchSize`. - pub patch_size: i32, - /// C# member: `F:LibreMetaverse.TerrainPatch.GroupHeader.Stride`. - pub stride: i32, - /// C# member: `F:LibreMetaverse.TerrainPatch.GroupHeader.Type`. - pub type_: libremetaverse::TerrainPatchLayerType, -} -impl TerrainPatchGroupHeader {} +/// C# member: `F:LibreMetaverse.TerrainPatch.GroupHeader.PatchSize`. +/// C# member: `F:LibreMetaverse.TerrainPatch.GroupHeader.Stride`. +/// C# member: `F:LibreMetaverse.TerrainPatch.GroupHeader.Type`. +pub use crate::terrain_codec::TerrainPatchGroupHeader; /// C# type: `T:LibreMetaverse.TerrainPatch.Header`. -pub struct TerrainPatchHeader { - /// C# member: `F:LibreMetaverse.TerrainPatch.Header.DCOffset`. - pub dc_offset: f32, - /// C# member: `F:LibreMetaverse.TerrainPatch.Header.PatchIDs`. - pub patch_i_ds: i32, - /// C# member: `F:LibreMetaverse.TerrainPatch.Header.QuantWBits`. - pub quant_w_bits: i32, - /// C# member: `F:LibreMetaverse.TerrainPatch.Header.Range`. - pub range: i32, - /// C# member: `F:LibreMetaverse.TerrainPatch.Header.WordBits`. - pub word_bits: u32, -} -impl TerrainPatchHeader { - /// C# member: `M:LibreMetaverse.TerrainPatch.Header.setPatchIDs(System.Int32,System.Int32)`. - pub fn set_patch_i_ds(&mut self, xx: i32, yy: i32) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.TerrainPatch.Header.setPatchIDs(System.Int32,System.Int32)", - ) - } - /// C# member: `P:LibreMetaverse.TerrainPatch.Header.LargeRegion`. - pub fn large_region(&self) -> bool { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.TerrainPatch.Header.LargeRegion") - } - /// Setter for C# member: `P:LibreMetaverse.TerrainPatch.Header.LargeRegion`. - pub fn set_large_region(&mut self, value: bool) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.TerrainPatch.Header.LargeRegion") - } - /// C# member: `P:LibreMetaverse.TerrainPatch.Header.X`. - pub fn x(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.TerrainPatch.Header.X") - } - /// Setter for C# member: `P:LibreMetaverse.TerrainPatch.Header.X`. - pub fn set_x(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.TerrainPatch.Header.X") - } - /// C# member: `P:LibreMetaverse.TerrainPatch.Header.Y`. - pub fn y(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.TerrainPatch.Header.Y") - } - /// Setter for C# member: `P:LibreMetaverse.TerrainPatch.Header.Y`. - pub fn set_y(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.TerrainPatch.Header.Y") - } -} +/// C# member: `F:LibreMetaverse.TerrainPatch.Header.DCOffset`. +/// C# member: `F:LibreMetaverse.TerrainPatch.Header.PatchIDs`. +/// C# member: `F:LibreMetaverse.TerrainPatch.Header.QuantWBits`. +/// C# member: `F:LibreMetaverse.TerrainPatch.Header.Range`. +/// C# member: `F:LibreMetaverse.TerrainPatch.Header.WordBits`. +/// C# member: `M:LibreMetaverse.TerrainPatch.Header.setPatchIDs(System.Int32,System.Int32)`. +/// C# member: `P:LibreMetaverse.TerrainPatch.Header.LargeRegion`. +/// C# member: `P:LibreMetaverse.TerrainPatch.Header.X`. +/// C# member: `P:LibreMetaverse.TerrainPatch.Header.Y`. +pub use crate::terrain_codec::TerrainPatchHeader; /// C# type: `T:LibreMetaverse.TerrainPatch.LayerType`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -57605,93 +56233,15 @@ pub mod messages { } /// C# type: `T:LibreMetaverse.Messages.Linden.EnvironmentData`. - pub struct EnvironmentData; - impl EnvironmentData { - /// C# member: `M:LibreMetaverse.Messages.Linden.EnvironmentData.#ctor`. - pub fn new() -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.EnvironmentData.#ctor", - ) - } - /// C# member: `M:LibreMetaverse.Messages.Linden.EnvironmentData.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. - pub fn deserialize( - &mut self, - map: libremetaverse_structured_data::OSDMap, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.EnvironmentData.Deserialize(LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.Messages.Linden.EnvironmentData.Serialize`. - pub fn serialize( - &self, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.EnvironmentData.Serialize", - ) - } - /// C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.DayCycle`. - pub fn day_cycle(&self) -> Option { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.EnvironmentData.DayCycle" - ) - } - /// Setter for C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.DayCycle`. - pub fn set_day_cycle(&mut self, value: Option) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.EnvironmentData.DayCycle" - ) - } - /// C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.DayLength`. - pub fn day_length(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.EnvironmentData.DayLength" - ) - } - /// Setter for C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.DayLength`. - pub fn set_day_length(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.EnvironmentData.DayLength" - ) - } - /// C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.DayOffset`. - pub fn day_offset(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.EnvironmentData.DayOffset" - ) - } - /// Setter for C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.DayOffset`. - pub fn set_day_offset(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.EnvironmentData.DayOffset" - ) - } - /// C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.Flags`. - pub fn flags(&self) -> libremetaverse::messages::linden::EnvironmentFlags { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.EnvironmentData.Flags" - ) - } - /// Setter for C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.Flags`. - pub fn set_flags(&mut self, value: libremetaverse::messages::linden::EnvironmentFlags) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.EnvironmentData.Flags" - ) - } - /// C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.IsDefault`. - pub fn is_default(&self) -> bool { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.EnvironmentData.IsDefault" - ) - } - /// Setter for C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.IsDefault`. - pub fn set_is_default(&mut self, value: bool) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.EnvironmentData.IsDefault" - ) - } - } - impl libremetaverse::interfaces::IMessage for EnvironmentData {} + /// C# member: `M:LibreMetaverse.Messages.Linden.EnvironmentData.#ctor`. + /// C# member: `M:LibreMetaverse.Messages.Linden.EnvironmentData.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. + /// C# member: `M:LibreMetaverse.Messages.Linden.EnvironmentData.Serialize`. + /// C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.DayCycle`. + /// C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.DayLength`. + /// C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.DayOffset`. + /// C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.Flags`. + /// C# member: `P:LibreMetaverse.Messages.Linden.EnvironmentData.IsDefault`. + pub use crate::environment_manager::EnvironmentData; /// C# type: `T:LibreMetaverse.Messages.Linden.EnvironmentFlags`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -57839,96 +56389,15 @@ pub mod messages { pub use crate::agent_messages::ExperiencePreferencesMessage; /// C# type: `T:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage`. - pub struct ExtEnvironmentMessage; - impl ExtEnvironmentMessage { - /// C# member: `M:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.#ctor`. - pub fn new() -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.#ctor", - ) - } - /// C# member: `M:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. - pub fn deserialize( - &mut self, - map: libremetaverse_structured_data::OSDMap, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Deserialize(LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Serialize`. - pub fn serialize( - &self, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Serialize", - ) - } - /// C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Environment`. - pub fn environment(&self) -> Option { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Environment" - ) - } - /// Setter for C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Environment`. - pub fn set_environment( - &mut self, - value: Option, - ) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Environment" - ) - } - /// C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Message`. - pub fn message(&self) -> String { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Message" - ) - } - /// Setter for C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Message`. - pub fn set_message(&mut self, value: String) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Message" - ) - } - /// C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.ParcelId`. - pub fn parcel_id(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.ParcelId" - ) - } - /// Setter for C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.ParcelId`. - pub fn set_parcel_id(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.ParcelId" - ) - } - /// C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Success`. - pub fn success(&self) -> bool { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Success" - ) - } - /// Setter for C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Success`. - pub fn set_success(&mut self, value: bool) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Success" - ) - } - /// C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Version`. - pub fn version(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Version" - ) - } - /// Setter for C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Version`. - pub fn set_version(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Version" - ) - } - } - impl libremetaverse::interfaces::IMessage for ExtEnvironmentMessage {} + /// C# member: `M:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.#ctor`. + /// C# member: `M:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. + /// C# member: `M:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Serialize`. + /// C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Environment`. + /// C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Message`. + /// C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.ParcelId`. + /// C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Success`. + /// C# member: `P:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage.Version`. + pub use crate::environment_manager::ExtEnvironmentMessage; /// C# type: `T:LibreMetaverse.Messages.Linden.ForceCloseChatterBoxSessionMessage`. pub struct ForceCloseChatterBoxSessionMessage { @@ -58162,28 +56631,10 @@ pub mod messages { } /// C# type: `T:LibreMetaverse.Messages.Linden.LandResourcesInfo`. - pub struct LandResourcesInfo { - /// C# member: `F:LibreMetaverse.Messages.Linden.LandResourcesInfo.Parcels`. - pub parcels: Vec, - } - impl LandResourcesInfo { - /// C# member: `M:LibreMetaverse.Messages.Linden.LandResourcesInfo.#ctor`. - pub fn new() -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.LandResourcesInfo.#ctor", - ) - } - /// C# member: `M:LibreMetaverse.Messages.Linden.LandResourcesInfo.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. - pub fn deserialize( - &mut self, - map: libremetaverse_structured_data::OSDMap, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.LandResourcesInfo.Deserialize(LibreMetaverse.StructuredData.OSDMap)", - ) - } - } - impl libremetaverse::interfaces::IMessage for LandResourcesInfo {} + /// C# member: `F:LibreMetaverse.Messages.Linden.LandResourcesInfo.Parcels`. + /// C# member: `M:LibreMetaverse.Messages.Linden.LandResourcesInfo.#ctor`. + /// C# member: `M:LibreMetaverse.Messages.Linden.LandResourcesInfo.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. + pub use crate::parcel_manager::LandResourcesInfo; /// C# type: `T:LibreMetaverse.Messages.Linden.LandResourcesMessage`. pub struct LandResourcesMessage { @@ -58338,45 +56789,11 @@ pub mod messages { } /// C# type: `T:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage`. - pub struct LegacyEnvironmentMessage; - impl LegacyEnvironmentMessage { - /// C# member: `M:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.#ctor`. - pub fn new() -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.#ctor", - ) - } - /// C# member: `M:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. - pub fn deserialize( - &mut self, - map: libremetaverse_structured_data::OSDMap, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.Deserialize(LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.Serialize`. - pub fn serialize( - &self, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.Serialize", - ) - } - /// C# member: `P:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.Settings`. - pub fn settings(&self) -> Option { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.Settings" - ) - } - /// Setter for C# member: `P:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.Settings`. - pub fn set_settings(&mut self, value: Option) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.Settings" - ) - } - } - impl libremetaverse::interfaces::IMessage for LegacyEnvironmentMessage {} + /// C# member: `M:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.#ctor`. + /// C# member: `M:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. + /// C# member: `M:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.Serialize`. + /// C# member: `P:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage.Settings`. + pub use crate::environment_manager::LegacyEnvironmentMessage; /// C# type: `T:LibreMetaverse.Messages.Linden.MapLayerMessage`. pub struct MapLayerMessage { @@ -58973,46 +57390,16 @@ pub mod messages { } /// C# type: `T:LibreMetaverse.Messages.Linden.ObjectResourcesDetail`. - pub struct ObjectResourcesDetail { - /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.GroupOwned`. - pub group_owned: bool, - /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.ID`. - pub id: libremetaverse_types::UUID, - /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.Location`. - pub location: libremetaverse_types::Vector3d, - /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.Name`. - pub name: String, - /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.OwnerID`. - pub owner_id: libremetaverse_types::UUID, - /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.Resources`. - pub resources: std::collections::HashMap, - } - impl ObjectResourcesDetail { - /// C# member: `M:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.#ctor`. - pub fn new() -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.#ctor", - ) - } - /// C# member: `M:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. - pub fn deserialize( - &mut self, - obj: libremetaverse_structured_data::OSDMap, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.Deserialize(LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.FromOSD(LibreMetaverse.StructuredData.OSD)`. - pub fn from_osd( - osd: libremetaverse_structured_data::OSD, - ) -> Result - { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.FromOSD(LibreMetaverse.StructuredData.OSD)", - ) - } - } + /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.GroupOwned`. + /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.ID`. + /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.Location`. + /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.Name`. + /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.OwnerID`. + /// C# member: `F:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.Resources`. + /// C# member: `M:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.#ctor`. + /// C# member: `M:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. + /// C# member: `M:LibreMetaverse.Messages.Linden.ObjectResourcesDetail.FromOSD(LibreMetaverse.StructuredData.OSD)`. + pub use crate::parcel_manager::ObjectResourcesDetail; /// C# type: `T:LibreMetaverse.Messages.Linden.ParcelObjectOwnersReplyMessage`. pub struct ParcelObjectOwnersReplyMessage { @@ -59320,46 +57707,16 @@ pub mod messages { } /// C# type: `T:LibreMetaverse.Messages.Linden.ParcelResourcesDetail`. - pub struct ParcelResourcesDetail { - /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.GroupOwned`. - pub group_owned: bool, - /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.ID`. - pub id: libremetaverse_types::UUID, - /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.LocalID`. - pub local_id: i32, - /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.Name`. - pub name: String, - /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.Objects`. - pub objects: Vec, - /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.OwnerID`. - pub owner_id: libremetaverse_types::UUID, - } - impl ParcelResourcesDetail { - /// C# member: `M:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.#ctor`. - pub fn new() -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.#ctor", - ) - } - /// C# member: `M:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. - pub fn deserialize( - &mut self, - map: libremetaverse_structured_data::OSDMap, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.Deserialize(LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.FromOSD(LibreMetaverse.StructuredData.OSD)`. - pub fn from_osd( - osd: libremetaverse_structured_data::OSD, - ) -> Result - { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.FromOSD(LibreMetaverse.StructuredData.OSD)", - ) - } - } + /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.GroupOwned`. + /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.ID`. + /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.LocalID`. + /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.Name`. + /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.Objects`. + /// C# member: `F:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.OwnerID`. + /// C# member: `M:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.#ctor`. + /// C# member: `M:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.Deserialize(LibreMetaverse.StructuredData.OSDMap)`. + /// C# member: `M:LibreMetaverse.Messages.Linden.ParcelResourcesDetail.FromOSD(LibreMetaverse.StructuredData.OSD)`. + pub use crate::parcel_manager::ParcelResourcesDetail; /// C# type: `T:LibreMetaverse.Messages.Linden.ParcelVoiceInfoRequestMessage`. pub struct ParcelVoiceInfoRequestMessage { diff --git a/crates/libremetaverse/src/lib.rs b/crates/libremetaverse/src/lib.rs index 0a34990..461fdcc 100644 --- a/crates/libremetaverse/src/lib.rs +++ b/crates/libremetaverse/src/lib.rs @@ -26,6 +26,7 @@ mod caps_http; mod client_core; mod current_outfit; mod download_manager; +mod environment_manager; mod event_queue; mod gesture; mod initial_outfit; @@ -48,11 +49,15 @@ mod object_model; #[rustfmt::skip] pub mod packet_catalog; mod packet_wire; +mod parcel_manager; mod sim_stats; mod skeleton; +mod sound_manager; #[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator. mod skeleton_catalog; mod targa; +mod terrain_codec; +mod terrain_manager; mod transfers; mod udp_transport; #[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator. diff --git a/crates/libremetaverse/src/network_manager.rs b/crates/libremetaverse/src/network_manager.rs index a55ec3c..7575df8 100644 --- a/crates/libremetaverse/src/network_manager.rs +++ b/crates/libremetaverse/src/network_manager.rs @@ -22,8 +22,8 @@ use crate::udp_transport::{UDPBase, UDPPacketBuffer, UdpPacketHandler, UdpTransp use crate::{ AccountLevelBenefits, Avatar, Caps, Error, GenericStreamingMethod, GridClient, Helpers, LoginCredential, LoginParams, LoginProgressEventArgs, LoginResponseData, LoginStatus, - NetworkManagerLoginResponseCallback, Primitive, RegionFlags, RegionProtocols, SimAccess, - SimulatorDataPool, SimulatorFeatures, SimulatorSimStats, TerrainPatch, + NetworkManagerLoginResponseCallback, Parcel, Primitive, RegionFlags, RegionProtocols, + SimAccess, SimulatorDataPool, SimulatorFeatures, SimulatorSimStats, TerrainPatch, }; use libremetaverse_structured_data::{OSD, OSDFormat, OSDMap, OSDParser}; use libremetaverse_types::compat::{ @@ -978,8 +978,11 @@ pub struct SimulatorData { pub name: String, pub objects_avatars: RwLock>, pub objects_primitives: RwLock>, - pub parcel_overlay: Vec, - pub parcel_overlays_received: i32, + pub parcel_overlay: RwLock>, + pub parcel_overlays_received: AtomicI32, + parcel_overlay_segments: AtomicU8, + pub parcels: RwLock>, + pub parcel_map: RwLock>, pub product_name: String, pub product_sku: String, pub protocols: RegionProtocols, @@ -990,7 +993,7 @@ pub struct SimulatorData { pub size_x: u32, pub size_y: u32, pub stats: SimulatorSimStats, - pub terrain: Vec, + pub terrain: RwLock>, pub terrain_base0: UUID, pub terrain_base1: UUID, pub terrain_base2: UUID, @@ -1008,7 +1011,7 @@ pub struct SimulatorData { pub terrain_start_height10: f32, pub terrain_start_height11: f32, pub water_height: f32, - pub wind_speeds: Option>, + pub wind_speeds: RwLock>>, endpoint: std::net::SocketAddr, connected: AtomicBool, movement_complete: AtomicBool, @@ -1141,8 +1144,11 @@ impl Simulator { name: String::new(), objects_avatars: RwLock::new(HashMap::new()), objects_primitives: RwLock::new(HashMap::new()), - parcel_overlay: vec![0; 4096], - parcel_overlays_received: 0, + parcel_overlay: RwLock::new(vec![0; 4096]), + parcel_overlays_received: AtomicI32::new(0), + parcel_overlay_segments: AtomicU8::new(0), + parcels: RwLock::new(HashMap::new()), + parcel_map: RwLock::new(vec![0; 4096]), product_name: String::new(), product_sku: String::new(), protocols: RegionProtocols(0), @@ -1153,7 +1159,7 @@ impl Simulator { size_x: size_x.unwrap_or(Self::DEFAULT_REGION_SIZE_X), size_y: size_y.unwrap_or(Self::DEFAULT_REGION_SIZE_Y), stats: SimulatorSimStats::default(), - terrain: Vec::new(), + terrain: RwLock::new(Vec::new()), terrain_base0: UUID::zero(), terrain_base1: UUID::zero(), terrain_base2: UUID::zero(), @@ -1171,7 +1177,7 @@ impl Simulator { terrain_start_height10: 0.0, terrain_start_height11: 0.0, water_height: 0.0, - wind_speeds: None, + wind_speeds: RwLock::new(None), endpoint: address, connected: AtomicBool::new(false), movement_complete: AtomicBool::new(false), @@ -1220,6 +1226,187 @@ impl Simulator { self.data.id } + pub(crate) fn native_store_terrain_patch( + &self, + patch: TerrainPatch, + large_region: bool, + ) -> Result<(), Error> { + let (width, height) = if large_region { + ( + usize::try_from(self.size_x.max(16) / 16).map_err(|_| Error::Argument)?, + usize::try_from(self.size_y.max(16) / 16).map_err(|_| Error::Argument)?, + ) + } else { + (16, 16) + }; + let x = usize::try_from(patch.x).map_err(|_| Error::Argument)?; + let y = usize::try_from(patch.y).map_err(|_| Error::Argument)?; + if x >= width || y >= height || width > 256 || height > 256 { + return Err(Error::Argument); + } + let index = y + .checked_mul(width) + .and_then(|value| value.checked_add(x)) + .ok_or(Error::Argument)?; + let patch_count = width.checked_mul(height).ok_or(Error::Argument)?; + let mut terrain = write(&self.terrain); + terrain.resize_with(patch_count, || TerrainPatch { + data: Vec::new(), + x: 0, + y: 0, + }); + terrain[index] = patch; + Ok(()) + } + + pub(crate) fn native_store_wind(&self, values: Vec) { + *write(&self.wind_speeds) = Some(values); + } + + pub(crate) fn native_apply_parcel_overlay( + &self, + sequence_id: i32, + data: Vec, + ) -> Result { + if !(0..4).contains(&sequence_id) { + return Err(Error::Argument); + } + let mut overlay = write(&self.parcel_overlay); + let segment_size = overlay.len().checked_div(4).ok_or(Error::Argument)?; + if data.len() != segment_size { + return Err(Error::Argument); + } + let sequence = usize::try_from(sequence_id).map_err(|_| Error::Argument)?; + let offset = sequence.checked_mul(segment_size).ok_or(Error::Argument)?; + let end = offset.checked_add(segment_size).ok_or(Error::Argument)?; + overlay[offset..end].copy_from_slice(&data); + + // The overlay may be retransmitted or arrive out of order. Count unique + // sequence IDs so duplicates cannot publish an incomplete map. + let bit = 1_u8 << sequence; + let old_segments = self.parcel_overlay_segments.load(Ordering::Acquire); + let segments = old_segments | bit; + self.parcel_overlay_segments + .store(segments, Ordering::Release); + let received = i32::try_from(segments.count_ones()).map_err(|_| Error::Argument)?; + self.parcel_overlays_received + .store(received, Ordering::Release); + if segments == 0b1111 { + self.parcel_overlay_segments.store(0, Ordering::Release); + self.parcel_overlays_received.store(0, Ordering::Release); + Ok(true) + } else { + Ok(false) + } + } + + pub(crate) fn native_store_parcel(&self, parcel: Parcel) { + if parcel.bitmap.len() == 512 { + let mut map = write(&self.parcel_map); + for cell in 0..4096 { + if parcel.bitmap[cell / 8] & (1 << (cell % 8)) != 0 { + map[cell] = parcel.local_id; + } + } + } + write(&self.parcels).insert(parcel.local_id, parcel); + } + + pub(crate) fn native_parcel(&self, local_id: i32) -> Option { + read(&self.parcels).get(&local_id).cloned() + } + + pub(crate) fn native_parcel_map_at(&self, x: usize, y: usize) -> Option { + if x >= 64 || y >= 64 { + return None; + } + read(&self.parcel_map).get(y * 64 + x).copied() + } + + pub(crate) fn native_parcels_snapshot(&self) -> HashMap { + read(&self.parcels).clone() + } + + pub(crate) fn native_parcel_map_snapshot(&self) -> Vec { + read(&self.parcel_map).clone() + } + + pub(crate) fn native_is_parcel_map_full(&self) -> bool { + read(&self.parcel_map).iter().all(|value| *value > 0) + } + + pub(crate) fn native_terrain_height_at_point( + &self, + x: i32, + y: i32, + height: &mut f32, + ) -> Result { + let (Ok(x), Ok(y)) = (u32::try_from(x), u32::try_from(y)) else { + return Ok(false); + }; + if x >= self.size_x || y >= self.size_y { + return Ok(false); + } + let per_edge = usize::try_from(self.size_x.max(16) / 16).map_err(|_| Error::Argument)?; + let patch_x = usize::try_from(x / 16).map_err(|_| Error::Argument)?; + let patch_y = usize::try_from(y / 16).map_err(|_| Error::Argument)?; + let point = usize::try_from((y % 16) * 16 + (x % 16)).map_err(|_| Error::Argument)?; + let terrain = read(&self.terrain); + let Some(patch) = terrain.get(patch_y * per_edge + patch_x) else { + return Ok(false); + }; + let Some(value) = patch.data.get(point) else { + return Ok(false); + }; + *height = *value; + Ok(true) + } + + pub(crate) fn native_clear_parcels(&self) { + write(&self.parcels).clear(); + write(&self.parcel_map).fill(0); + } + + pub(crate) fn native_update_parcel_dwell(&self, local_id: i32, dwell: f32) { + if let Some(parcel) = write(&self.parcels).get_mut(&local_id) { + parcel.dwell = dwell; + } + } + + pub(crate) fn native_update_parcel_access( + &self, + local_id: i32, + flags: u32, + entries: Vec, + ) { + if let Some(parcel) = write(&self.parcels).get_mut(&local_id) { + if flags & crate::AccessList::BAN.0 != 0 { + parcel.access_black_list = entries; + } else { + parcel.access_white_list = entries; + } + } + } + + pub(crate) fn native_send_parcel_clean_time( + &self, + local_id: i32, + clean_time: i32, + ) -> Result<(), Error> { + let mut packet = crate::packets::ParcelSetOtherCleanTimePacket::new_with_constructor()?; + packet.agent_data.agent_id = *read(&self.data.agent_id); + packet.agent_data.session_id = *read(&self.data.session_id); + packet.parcel_data.local_id = local_id; + packet.parcel_data.other_clean_time = clean_time; + let data = packet.to_bytes_with_method()?; + self.native_send_packet_data( + data.clone(), + i32::try_from(data.len()).map_err(|_| Error::Argument)?, + PacketType::ParcelSetOtherCleanTime, + false, + ) + } + pub(crate) fn native_from_weak(data: &Weak) -> Option { data.upgrade().map(|data| Self { caps: None, data }) } diff --git a/crates/libremetaverse/src/parcel_manager.rs b/crates/libremetaverse/src/parcel_manager.rs new file mode 100644 index 0000000..684a5b5 --- /dev/null +++ b/crates/libremetaverse/src/parcel_manager.rs @@ -0,0 +1,1830 @@ +//! Parcel protocol manager, synchronized simulator parcel state, and land capabilities. + +#![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::assigning_clones)] // Fresh compatibility records are populated field by field. +#![allow(clippy::cast_possible_truncation)] // Protocol coordinates and durations use fixed narrow fields. +#![allow(clippy::cast_precision_loss)] // The LLUDP land protocol represents coordinates and time as f32. +#![allow(clippy::cast_sign_loss)] // Values are range-checked before conversion to protocol indices. +#![allow(clippy::collapsible_if)] // Nested packet/message validation keeps failure boundaries explicit. +#![allow(clippy::inherent_to_string)] // The mapped C# surface requires Parcel.ToString as an inherent method. +#![allow(clippy::struct_excessive_bools)] // Parcel flags mirror independent public C# fields. +#![allow(clippy::unnecessary_wraps)] // Mapped constructors have fixed fallible signatures. + +use crate::agent_manager::EventRegistry; +use crate::packet_catalog::{GeneratedPacket, PacketType}; +use crate::{Error, GridClient, Simulator}; +use libremetaverse_structured_data::{OSD, OSDMap, OSDParser}; +use libremetaverse_types::compat::{CancellationToken, EventHandler, Subscription}; +use libremetaverse_types::{UUID, Vector3}; +use std::collections::HashMap; +use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; +use std::sync::{Arc, Mutex, RwLock}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +const PARCEL_MAP_SIZE: usize = 64; +const PARCEL_GRID_STEP: f32 = 4.0; +const MAX_PARCEL_BYTES: usize = 16 * 1024 * 1024; +fn mutex(value: &Mutex) -> std::sync::MutexGuard<'_, T> { + value + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) +} +fn wire_string(value: &[u8]) -> String { + String::from_utf8_lossy(value.split(|byte| *byte == 0).next().unwrap_or_default()).into_owned() +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ParcelManagerParcelAccessEntry { + pub agent_id: UUID, + pub flags: crate::AccessList, + pub time: SystemTime, +} +#[derive(Clone, Debug, PartialEq)] +pub struct ParcelManagerParcelPrimOwners { + pub count: i32, + pub is_group_owned: bool, + pub newest_prim: SystemTime, + pub online_status: bool, + pub owner_id: UUID, +} +#[derive(Clone, Debug, PartialEq)] +pub struct ParcelMedia { + pub media_auto_scale: bool, + pub media_desc: String, + pub media_height: i32, + pub media_id: UUID, + pub media_loop: bool, + pub media_type: String, + pub media_url: String, + pub media_width: i32, +} +impl Default for ParcelMedia { + fn default() -> Self { + Self { + media_auto_scale: false, + media_desc: String::new(), + media_height: 0, + media_id: UUID::zero(), + media_loop: false, + media_type: String::new(), + media_url: String::new(), + media_width: 0, + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Parcel { + pub aabb_max: Vector3, + pub aabb_min: Vector3, + pub access_black_list: Vec, + pub access_white_list: Vec, + pub any_av_sounds: bool, + pub area: i32, + pub auction_id: u32, + pub auth_buyer_id: UUID, + pub bitmap: Vec, + pub category: crate::ParcelCategory, + pub claim_date: SystemTime, + pub claim_price: i32, + pub desc: String, + pub dwell: f32, + pub flags: crate::ParcelFlags, + pub group_av_sounds: bool, + pub group_id: UUID, + pub group_prims: i32, + pub is_group_owned: bool, + pub landing: crate::LandingType, + pub local_id: i32, + pub max_prims: i32, + pub media: ParcelMedia, + pub music_url: String, + pub name: String, + pub obscure_media: bool, + pub obscure_music: bool, + pub other_clean_time: i32, + pub other_count: i32, + pub other_prims: i32, + pub owner_id: UUID, + pub owner_prims: i32, + pub parcel_prim_bonus: f32, + pub pass_hours: f32, + pub pass_price: i32, + pub public_count: i32, + pub region_deny_age_unverified: bool, + pub region_deny_anonymous: bool, + pub region_push_override: bool, + pub rent_price: i32, + pub sale_price: i32, + pub see_a_vs: bool, + pub self_count: i32, + pub sim_wide_max_prims: i32, + pub sim_wide_total_prims: i32, + pub snapshot_id: UUID, + pub status: crate::ParcelStatus, + pub total_prims: i32, + pub user_location: Vector3, + pub user_look_at: Vector3, +} +impl Parcel { + pub fn new(local_id: i32) -> Result { + Ok(Self { + aabb_max: Vector3::default(), + aabb_min: Vector3::default(), + access_black_list: Vec::new(), + access_white_list: Vec::new(), + any_av_sounds: true, + area: 0, + auction_id: 0, + auth_buyer_id: UUID::zero(), + bitmap: Vec::new(), + category: crate::ParcelCategory::None, + claim_date: UNIX_EPOCH, + claim_price: 0, + desc: String::new(), + dwell: 0.0, + flags: crate::ParcelFlags::NONE, + group_av_sounds: true, + group_id: UUID::zero(), + group_prims: 0, + is_group_owned: false, + landing: crate::LandingType::None, + local_id, + max_prims: 0, + media: ParcelMedia::default(), + music_url: String::new(), + name: String::new(), + obscure_media: false, + obscure_music: false, + other_clean_time: 0, + other_count: 0, + other_prims: 0, + owner_id: UUID::zero(), + owner_prims: 0, + parcel_prim_bonus: 0.0, + pass_hours: 0.0, + pass_price: 0, + public_count: 0, + region_deny_age_unverified: false, + region_deny_anonymous: false, + region_push_override: false, + rent_price: 0, + sale_price: 0, + see_a_vs: true, + self_count: 0, + sim_wide_max_prims: 0, + sim_wide_total_prims: 0, + snapshot_id: UUID::zero(), + status: crate::ParcelStatus::None, + total_prims: 0, + user_location: Vector3::default(), + user_look_at: Vector3::default(), + }) + } + pub fn to_string(&self) -> String { + format!("{} ({})", self.name, self.local_id) + } + pub fn update( + &self, + client: GridClient, + simulator: Simulator, + want_reply: bool, + ) -> Result<(), Error> { + ParcelManager::new(client)?.send_parcel_update(self, simulator, want_reply) + } + pub fn update_other_clean_time(&self, simulator: Simulator) -> Result<(), Error> { + simulator.native_send_parcel_clean_time(self.local_id, self.other_clean_time) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ParcelInfo { + pub actual_area: i32, + pub auction_id: i32, + pub billable_area: i32, + pub description: String, + pub dwell: f32, + pub global_x: f32, + pub global_y: f32, + pub global_z: f32, + pub id: UUID, + pub mature: bool, + pub name: String, + pub owner_id: UUID, + pub sale_price: i32, + pub sim_name: String, + pub snapshot_id: UUID, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectResourcesDetail { + pub group_owned: bool, + pub id: UUID, + pub location: libremetaverse_types::Vector3d, + pub name: String, + pub owner_id: UUID, + pub resources: HashMap, +} +impl ObjectResourcesDetail { + pub fn new() -> Result { + Ok(Self { + group_owned: false, + id: UUID::zero(), + location: libremetaverse_types::Vector3d::default(), + name: String::new(), + owner_id: UUID::zero(), + resources: HashMap::new(), + }) + } + pub fn deserialize(&mut self, obj: OSDMap) -> Result<(), Error> { + self.id = obj.get("id").map_or(Ok(UUID::zero()), |v| v.as_uuid())?; + self.name = obj + .get("name") + .map_or(Ok(String::new()), |v| v.as_string())?; + self.location = obj + .get("location") + .map_or(Ok(libremetaverse_types::Vector3d::default()), |v| { + v.as_vector3d() + })?; + self.group_owned = obj + .get("is_group_owned") + .is_some_and(|v| v.as_boolean().unwrap_or(false)); + self.owner_id = obj + .get("owner_id") + .map_or(Ok(UUID::zero()), |v| v.as_uuid())?; + self.resources = match obj.get("resources") { + Some(OSD::Map(values)) => values + .into_iter() + .take(4096) + .filter_map(|(key, value)| value.as_integer().ok().map(|amount| (key, amount))) + .collect(), + _ => HashMap::new(), + }; + Ok(()) + } + pub fn from_osd(osd: OSD) -> Result { + let OSD::Map(values) = osd else { + return Err(Error::Argument); + }; + let mut result = Self::new()?; + result.deserialize(OSDMap::new_with_dictionary(values)?)?; + Ok(result) + } +} +#[derive(Clone, Debug, PartialEq)] +pub struct ParcelResourcesDetail { + pub group_owned: bool, + pub id: UUID, + pub local_id: i32, + pub name: String, + pub objects: Vec, + pub owner_id: UUID, +} +impl ParcelResourcesDetail { + pub fn new() -> Result { + Ok(Self { + group_owned: false, + id: UUID::zero(), + local_id: 0, + name: String::new(), + objects: Vec::new(), + owner_id: UUID::zero(), + }) + } + pub fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> { + self.id = map.get("id").map_or(Ok(UUID::zero()), |v| v.as_uuid())?; + self.local_id = map.get("local_id").map_or(Ok(0), |v| v.as_integer())?; + self.name = map + .get("name") + .map_or(Ok(String::new()), |v| v.as_string())?; + self.group_owned = map + .get("is_group_owned") + .is_some_and(|v| v.as_boolean().unwrap_or(false)); + self.owner_id = map + .get("owner_id") + .map_or(Ok(UUID::zero()), |v| v.as_uuid())?; + self.objects = match map.get("objects") { + Some(OSD::Array(values)) if values.len() <= 65_535 => values + .into_iter() + .map(ObjectResourcesDetail::from_osd) + .collect::>()?, + Some(OSD::Array(_)) => return Err(Error::Argument), + _ => Vec::new(), + }; + Ok(()) + } + pub fn from_osd(osd: OSD) -> Result { + let OSD::Map(values) = osd else { + return Err(Error::Argument); + }; + let mut result = Self::new()?; + result.deserialize(OSDMap::new_with_dictionary(values)?)?; + Ok(result) + } +} +#[derive(Clone, Debug, PartialEq)] +pub struct LandResourcesInfo { + pub parcels: Vec, + summary_available: HashMap, + summary_used: HashMap, +} +impl LandResourcesInfo { + pub fn new() -> Result { + Ok(Self { + parcels: Vec::new(), + summary_available: HashMap::new(), + summary_used: HashMap::new(), + }) + } + pub fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> { + if let Some(OSD::Map(summary)) = map.get("summary") { + self.summary_available = resource_summary(summary.get("available"))?; + self.summary_used = resource_summary(summary.get("used"))?; + } else if let Some(OSD::Array(values)) = map.get("parcels") { + if values.len() > 65_535 { + return Err(Error::Argument); + } + self.parcels = values + .into_iter() + .map(ParcelResourcesDetail::from_osd) + .collect::>()?; + } + Ok(()) + } +} +fn resource_summary(value: Option<&OSD>) -> Result, Error> { + let Some(OSD::Array(values)) = value else { + return Ok(HashMap::new()); + }; + if values.len() > 4096 { + return Err(Error::Argument); + } + Ok(values + .iter() + .filter_map(|entry| { + let OSD::Map(map) = entry else { + return None; + }; + let name = map.get("type")?.as_string().ok()?; + let amount = map.get("amount")?.as_integer().ok()?; + Some((name, amount)) + }) + .collect()) +} +impl crate::interfaces::IMessage for LandResourcesInfo { + fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> { + self.deserialize(map) + } + fn serialize(&self) -> Result { + OSDMap::new_with_constructor() + } +} + +macro_rules! simple_event { + ($name:ident { $($field:ident : $type:ty),* $(,)? }) => { + #[derive(Clone)] pub struct $name { $(pub(crate) $field: $type),* } + }; +} +simple_event!(ForceSelectObjectsReplyEventArgs { simulator: Simulator, object_i_ds: Vec, reset_list: bool }); +impl ForceSelectObjectsReplyEventArgs { + pub fn new( + simulator: Simulator, + object_i_ds: Vec, + reset_list: bool, + ) -> Result { + Ok(Self { + simulator, + object_i_ds, + reset_list, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn object_i_ds(&self) -> Vec { + self.object_i_ds.clone() + } + pub fn reset_list(&self) -> bool { + self.reset_list + } +} +simple_event!(ParcelAccessListReplyEventArgs { simulator: Simulator, sequence_id: i32, local_id: i32, flags: u32, access_list: Vec }); +impl ParcelAccessListReplyEventArgs { + pub fn new( + simulator: Simulator, + sequence_id: i32, + local_id: i32, + flags: u32, + access_entries: Vec, + ) -> Result { + Ok(Self { + simulator, + sequence_id, + local_id, + flags, + access_list: access_entries, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn sequence_id(&self) -> i32 { + self.sequence_id + } + pub fn local_id(&self) -> i32 { + self.local_id + } + pub fn flags(&self) -> u32 { + self.flags + } + pub fn access_list(&self) -> Vec { + self.access_list.clone() + } +} +simple_event!(ParcelDwellReplyEventArgs { + parcel_id: UUID, + local_id: i32, + dwell: f32 +}); +impl ParcelDwellReplyEventArgs { + pub fn new(parcel_id: UUID, local_id: i32, dwell: f32) -> Result { + Ok(Self { + parcel_id, + local_id, + dwell, + }) + } + pub fn parcel_id(&self) -> UUID { + self.parcel_id + } + pub fn local_id(&self) -> i32 { + self.local_id + } + pub fn dwell(&self) -> f32 { + self.dwell + } +} +simple_event!(ParcelInfoReplyEventArgs { parcel: ParcelInfo }); +impl ParcelInfoReplyEventArgs { + pub fn new(parcel: ParcelInfo) -> Result { + Ok(Self { parcel }) + } + pub fn parcel(&self) -> ParcelInfo { + self.parcel.clone() + } +} +simple_event!(ParcelMediaCommandEventArgs { + simulator: Simulator, + sequence: u32, + parcel_flags: crate::ParcelFlags, + media_command: crate::ParcelMediaCommand, + time: f32 +}); +impl ParcelMediaCommandEventArgs { + pub fn new( + simulator: Simulator, + sequence: u32, + flags: crate::ParcelFlags, + command: crate::ParcelMediaCommand, + time: f32, + ) -> Result { + Ok(Self { + simulator, + sequence, + parcel_flags: flags, + media_command: command, + time, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn sequence(&self) -> u32 { + self.sequence + } + pub fn parcel_flags(&self) -> crate::ParcelFlags { + self.parcel_flags + } + pub fn media_command(&self) -> crate::ParcelMediaCommand { + self.media_command + } + pub fn time(&self) -> f32 { + self.time + } +} +simple_event!(ParcelMediaUpdateReplyEventArgs { + simulator: Simulator, + media: ParcelMedia +}); +impl ParcelMediaUpdateReplyEventArgs { + pub fn new(simulator: Simulator, media: ParcelMedia) -> Result { + Ok(Self { simulator, media }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn media(&self) -> ParcelMedia { + self.media.clone() + } +} +simple_event!(ParcelObjectOwnersReplyEventArgs { simulator: Simulator, prim_owners: Vec }); +impl ParcelObjectOwnersReplyEventArgs { + pub fn new( + simulator: Simulator, + prim_owners: Vec, + ) -> Result { + Ok(Self { + simulator, + prim_owners, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn prim_owners(&self) -> Vec { + self.prim_owners.clone() + } +} +simple_event!(ParcelOverlayEventArgs { + simulator: Simulator +}); +impl ParcelOverlayEventArgs { + pub fn new(simulator: Simulator) -> Result { + Ok(Self { simulator }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } +} +simple_event!(ParcelPropertiesEventArgs { + simulator: Simulator, + parcel: Parcel, + result: crate::ParcelResult, + selected_prims: i32, + sequence_id: i32, + snap_selection: bool +}); +impl ParcelPropertiesEventArgs { + pub fn new( + simulator: Simulator, + parcel: Parcel, + result: crate::ParcelResult, + selected_prims: i32, + sequence_id: i32, + snap_selection: bool, + ) -> Result { + Ok(Self { + simulator, + parcel, + result, + selected_prims, + sequence_id, + snap_selection, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn parcel(&self) -> Parcel { + self.parcel.clone() + } + pub fn result(&self) -> crate::ParcelResult { + self.result + } + pub fn selected_prims(&self) -> i32 { + self.selected_prims + } + pub fn sequence_id(&self) -> i32 { + self.sequence_id + } + pub fn snap_selection(&self) -> bool { + self.snap_selection + } +} +simple_event!(SimParcelsDownloadedEventArgs { simulator: Simulator, sim_parcels: HashMap, parcel_map: Vec }); +impl SimParcelsDownloadedEventArgs { + pub fn new( + simulator: Simulator, + sim_parcels: RwLock>, + parcel_map: Vec, + ) -> Result { + Ok(Self { + simulator, + sim_parcels: sim_parcels + .into_inner() + .unwrap_or_else(std::sync::PoisonError::into_inner), + parcel_map, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn parcel_map(&self) -> Vec { + self.parcel_map.clone() + } + pub fn parcels(&self) -> RwLock> { + RwLock::new(self.sim_parcels.clone()) + } +} + +#[derive(Default)] +struct ParcelEvents { + force: EventRegistry, + access: EventRegistry, + dwell: EventRegistry, + info: EventRegistry, + media_command: EventRegistry, + media_update: EventRegistry, + owners: EventRegistry, + overlay: EventRegistry, + properties: EventRegistry, + all: EventRegistry, +} +pub(crate) struct ParcelManagerInner { + client: Arc, + events: ParcelEvents, + raw_subscription: Mutex>, + caps_subscriptions: Mutex>, + sequence: AtomicI32, + disposed: AtomicBool, +} +#[derive(Clone)] +pub struct ParcelManager { + pub(crate) inner: Arc, +} + +impl ParcelManager { + pub fn new(client: GridClient) -> Result { + Self::native_new(Arc::new(client)) + } + pub(crate) fn native_new(client: Arc) -> Result { + if let Some(inner) = client.cached_parcel_manager_inner() { + return Ok(Self { inner }); + } + let inner = Arc::new(ParcelManagerInner { + client: Arc::clone(&client), + events: ParcelEvents::default(), + raw_subscription: Mutex::new(None), + caps_subscriptions: Mutex::new(Vec::new()), + sequence: AtomicI32::new(1), + 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_packet(event); + } + }, + ))); + let weak = Arc::downgrade(&inner); + mutex(&inner.caps_subscriptions).push(client.network().subscribe_caps( + "ParcelProperties".into(), + crate::CapsEventQueueCallback::from_handler(move |_, message, simulator| { + if let Some(inner) = weak.upgrade() { + if let Ok(map) = message.serialize() { + if let Ok(mut value) = + crate::messages::linden::ParcelPropertiesMessage::new() + { + if value.deserialize(map).is_ok() { + inner.handle_properties(value, simulator); + } + } + } + } + }), + )); + let weak = Arc::downgrade(&inner); + mutex(&inner.caps_subscriptions).push(client.network().subscribe_caps( + "ParcelObjectOwnersReply".into(), + crate::CapsEventQueueCallback::from_handler(move |_, message, simulator| { + if let Some(inner) = weak.upgrade() { + if let Ok(map) = message.serialize() { + if let Ok(mut value) = + crate::messages::linden::ParcelObjectOwnersReplyMessage::new() + { + if value.deserialize(map).is_ok() { + inner.handle_owners(value, simulator); + } + } + } + } + }), + )); + Ok(Self { + inner: client.install_parcel_manager_inner(inner), + }) + } + fn agent_ids(&self) -> (UUID, UUID) { + let mut client = (*self.inner.client).clone(); + let agent = client.self_(); + (agent.agent_id(), agent.session_id()) + } + fn send( + &self, + sim: &Simulator, + packet: &T, + kind: PacketType, + ) -> Result<(), Error> { + if self.inner.disposed.load(Ordering::Acquire) { + return Err(Error::InvalidOperation); + } + let bytes = packet.encode_packet()?; + let result = sim.native_send_packet_data( + bytes.clone(), + i32::try_from(bytes.len()).map_err(|_| Error::Argument)?, + kind, + false, + ); + match result { + // The reference manager accepts packet submission to an inert test + // simulator. Connected transports still surface every send error. + Err(Error::InvalidOperation) if !sim.connected() => Ok(()), + result => result, + } + } + pub fn dispose(&self) -> Result<(), Error> { + self.inner.disposed.store(true, Ordering::Release); + mutex(&self.inner.raw_subscription).take(); + mutex(&self.inner.caps_subscriptions).clear(); + Ok(()) + } + pub fn current_parcel(&self) -> Option { + let sim = self.inner.client.network().current_sim()?; + let mut client = (*self.inner.client).clone(); + let id = self + .get_parcel_local_id(sim.clone(), client.self_().sim_position()) + .ok()?; + sim.native_parcel(id) + } + pub fn get_parcel_local_id( + &self, + simulator: Simulator, + position: Vector3, + ) -> Result { + if !position.x.is_finite() || !position.y.is_finite() { + return Err(Error::Argument); + } + let x = (position.x / PARCEL_GRID_STEP).floor() as i32; + let y = (position.y / PARCEL_GRID_STEP).floor() as i32; + if !(0..64).contains(&x) || !(0..64).contains(&y) { + return Ok(0); + } + Ok(simulator + .native_parcel_map_at(x as usize, y as usize) + .unwrap_or(0)) + } + pub fn request_parcel_info(&self, parcel_id: UUID) -> Result<(), Error> { + let sim = self + .inner + .client + .network() + .current_sim() + .ok_or(Error::InvalidOperation)?; + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelInfoRequestPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.data.parcel_id = parcel_id; + self.send(&sim, &p, PacketType::ParcelInfoRequest) + } + pub fn request_parcel_properties_with_simulator_int32_int32( + &self, + sim: Simulator, + local_id: i32, + sequence_id: i32, + ) -> Result<(), Error> { + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelPropertiesRequestByIDPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.parcel_data.local_id = local_id; + p.parcel_data.sequence_id = sequence_id; + self.send(&sim, &p, PacketType::ParcelPropertiesRequestByID) + } + pub fn request_parcel_properties_with_simulator_single_single_single_single_int32_boolean( + &self, + sim: Simulator, + north: f32, + east: f32, + south: f32, + west: f32, + sequence_id: i32, + snap_selection: bool, + ) -> Result<(), Error> { + if ![north, east, south, west].iter().all(|v| v.is_finite()) || north < south || east < west + { + return Err(Error::Argument); + } + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelPropertiesRequestPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.parcel_data.north = north; + p.parcel_data.east = east; + p.parcel_data.south = south; + p.parcel_data.west = west; + p.parcel_data.sequence_id = sequence_id; + p.parcel_data.snap_selection = snap_selection; + self.send(&sim, &p, PacketType::ParcelPropertiesRequest) + } + pub fn request_parcel_access_list( + &self, + sim: Simulator, + local_id: i32, + flags: crate::AccessList, + sequence_id: i32, + ) -> Result<(), Error> { + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelAccessListRequestPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.data.local_id = local_id; + p.data.flags = flags.0; + p.data.sequence_id = sequence_id; + self.send(&sim, &p, PacketType::ParcelAccessListRequest) + } + pub fn request_dwell(&self, sim: Simulator, local_id: i32) -> Result<(), Error> { + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelDwellRequestPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.data.local_id = local_id; + p.data.parcel_id = UUID::zero(); + self.send(&sim, &p, PacketType::ParcelDwellRequest) + } + pub fn request_object_owners(&self, sim: Simulator, local_id: i32) -> Result<(), Error> { + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelObjectOwnersRequestPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.parcel_data.local_id = local_id; + self.send(&sim, &p, PacketType::ParcelObjectOwnersRequest) + } + fn send_parcel_update( + &self, + parcel: &Parcel, + sim: Simulator, + want_reply: bool, + ) -> Result<(), Error> { + if parcel.desc.len() > 1024 * 1024 + || parcel.name.len() > 1024 * 1024 + || parcel.media.media_url.len() > 1024 * 1024 + || parcel.music_url.len() > 1024 * 1024 + { + return Err(Error::Argument); + } + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelPropertiesUpdatePacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.parcel_data.local_id = parcel.local_id; + p.parcel_data.auth_buyer_id = parcel.auth_buyer_id; + p.parcel_data.category = parcel.category as u8; + p.parcel_data.desc = parcel.desc.as_bytes().to_vec(); + p.parcel_data.group_id = parcel.group_id; + p.parcel_data.landing_type = parcel.landing as u8; + p.parcel_data.media_auto_scale = u8::from(parcel.media.media_auto_scale); + p.parcel_data.media_id = parcel.media.media_id; + p.parcel_data.media_url = parcel.media.media_url.as_bytes().to_vec(); + p.parcel_data.music_url = parcel.music_url.as_bytes().to_vec(); + p.parcel_data.name = parcel.name.as_bytes().to_vec(); + p.parcel_data.flags = u32::from(want_reply); + p.parcel_data.parcel_flags = parcel.flags.0; + p.parcel_data.pass_hours = parcel.pass_hours; + p.parcel_data.pass_price = parcel.pass_price; + p.parcel_data.sale_price = parcel.sale_price; + p.parcel_data.snapshot_id = parcel.snapshot_id; + p.parcel_data.user_location = parcel.user_location; + p.parcel_data.user_look_at = parcel.user_look_at; + self.send(&sim, &p, PacketType::ParcelPropertiesUpdate)?; + parcel.update_other_clean_time(sim) + } + pub fn parcel_subdivide( + &self, + sim: Simulator, + west: f32, + south: f32, + east: f32, + north: f32, + ) -> Result<(), Error> { + self.send_bounds::( + sim, + west, + south, + east, + north, + PacketType::ParcelDivide, + ) + } + pub fn parcel_join( + &self, + sim: Simulator, + west: f32, + south: f32, + east: f32, + north: f32, + ) -> Result<(), Error> { + self.send_bounds::( + sim, + west, + south, + east, + north, + PacketType::ParcelJoin, + ) + } + fn send_bounds( + &self, + sim: Simulator, + west: f32, + south: f32, + east: f32, + north: f32, + kind: PacketType, + ) -> Result<(), Error> { + if ![west, south, east, north].iter().all(|v| v.is_finite()) + || west >= east + || south >= north + { + return Err(Error::Argument); + } + let (a, s) = self.agent_ids(); + let mut p = T::new_generated(); + p.set_agent(a, s); + p.set_bounds(west, south, east, north); + self.send(&sim, &p, kind) + } + pub async fn request_all_sim_parcels_with_simulator_cancellation_token( + &self, + sim: Simulator, + token: Option, + ) -> Result<(), Error> { + self.request_all_sim_parcels_with_simulator_boolean_time_span_cancellation_token( + sim, + false, + Duration::from_millis(750), + token, + ) + .await + } + pub async fn request_all_sim_parcels_with_simulator_boolean_time_span_cancellation_token( + &self, + sim: Simulator, + refresh: bool, + delay: Duration, + token: Option, + ) -> Result<(), Error> { + let token = token.unwrap_or_default(); + if refresh { + sim.native_clear_parcels(); + } + for y in 0..PARCEL_MAP_SIZE { + for x in 0..PARCEL_MAP_SIZE { + token.throw_if_cancellation_requested()?; + if sim.native_parcel_map_at(x, y).unwrap_or(0) > 0 { + continue; + } + let sequence = self.inner.sequence.fetch_add(1, Ordering::Relaxed); + self.request_parcel_properties_with_simulator_single_single_single_single_int32_boolean(sim.clone(),(y+1)as f32*4.0,(x+1)as f32*4.0,y as f32*4.0,x as f32*4.0,sequence,false)?; + if !delay.is_zero() { + tokio::select! { + () = tokio::time::sleep(delay.min(Duration::from_secs(10))) => {} + () = token.cancelled() => return Err(Error::Cancelled), + } + } + } + } + Ok(()) + } + pub fn buy( + &self, + sim: Simulator, + local_id: i32, + for_group: bool, + group_id: UUID, + remove_contribution: bool, + parcel_area: i32, + parcel_price: i32, + ) -> Result<(), Error> { + if parcel_area < 0 || parcel_price < 0 { + return Err(Error::Argument); + } + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelBuyPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.data.final_ = true; + p.data.group_id = group_id; + p.data.local_id = local_id; + p.data.is_group_owned = for_group; + p.data.remove_contribution = remove_contribution; + p.parcel_data.area = parcel_area; + p.parcel_data.price = parcel_price; + self.send(&sim, &p, PacketType::ParcelBuy) + } + pub fn reclaim(&self, sim: Simulator, local_id: i32) -> Result<(), Error> { + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelReclaimPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.data.local_id = local_id; + self.send(&sim, &p, PacketType::ParcelReclaim) + } + pub fn deed_to_group( + &self, + sim: Simulator, + local_id: i32, + group_id: UUID, + ) -> Result<(), Error> { + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelDeedToGroupPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.data.local_id = local_id; + p.data.group_id = group_id; + self.send(&sim, &p, PacketType::ParcelDeedToGroup) + } + pub fn release_parcel(&self, sim: Simulator, local_id: i32) -> Result<(), Error> { + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelReleasePacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.data.local_id = local_id; + self.send(&sim, &p, PacketType::ParcelRelease) + } + pub fn start_auction( + &self, + sim: Simulator, + local_id: i32, + snapshot_id: UUID, + ) -> Result<(), Error> { + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ViewerStartAuctionPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.parcel_data.local_id = local_id; + p.parcel_data.snapshot_id = snapshot_id; + self.send(&sim, &p, PacketType::ViewerStartAuction) + } + pub fn eject_user(&self, target_id: UUID, ban: bool) -> Result<(), Error> { + let sim = self + .inner + .client + .network() + .current_sim() + .ok_or(Error::InvalidOperation)?; + let (a, s) = self.agent_ids(); + let mut p = crate::packets::EjectUserPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.data.target_id = target_id; + p.data.flags = u32::from(ban); + self.send(&sim, &p, PacketType::EjectUser) + } + pub fn freeze_user(&self, target_id: UUID, freeze: bool) -> Result<(), Error> { + let sim = self + .inner + .client + .network() + .current_sim() + .ok_or(Error::InvalidOperation)?; + let (a, s) = self.agent_ids(); + let mut p = crate::packets::FreezeUserPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.data.target_id = target_id; + p.data.flags = u32::from(!freeze); + self.send(&sim, &p, PacketType::FreezeUser) + } + pub fn request_select_objects( + &self, + local_id: i32, + select_type: crate::ObjectReturnType, + owner_id: UUID, + ) -> Result<(), Error> { + let sim = self + .inner + .client + .network() + .current_sim() + .ok_or(Error::InvalidOperation)?; + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelSelectObjectsPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.parcel_data.local_id = local_id; + p.parcel_data.return_type = select_type as u32; + let mut owner = + crate::packets::ParcelSelectObjectsPacketReturnIDsBlock::new_with_constructor()?; + owner.return_id = owner_id; + p.return_i_ds = vec![owner]; + self.send(&sim, &p, PacketType::ParcelSelectObjects) + } + pub fn return_objects( + &self, + sim: Simulator, + local_id: i32, + type_: crate::ObjectReturnType, + owner_i_ds: Vec, + ) -> Result<(), Error> { + if owner_i_ds.len() > 65_535 { + return Err(Error::Argument); + } + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ParcelReturnObjectsPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.parcel_data.local_id = local_id; + p.parcel_data.return_type = type_ as u32; + let mut task = + crate::packets::ParcelReturnObjectsPacketTaskIDsBlock::new_with_constructor()?; + task.task_id = UUID::zero(); + p.task_i_ds = vec![task]; + p.owner_i_ds = owner_i_ds + .into_iter() + .map(|owner_id| { + let mut b = + crate::packets::ParcelReturnObjectsPacketOwnerIDsBlock::new_with_constructor()?; + b.owner_id = owner_id; + Ok(b) + }) + .collect::, Error>>()?; + self.send(&sim, &p, PacketType::ParcelReturnObjects) + } + + pub fn terraform_with_simulator_int32_terraform_action_terraform_brush_size( + &self, + sim: Simulator, + local_id: i32, + action: crate::TerraformAction, + brush_size: crate::TerraformBrushSize, + ) -> Result { + let Some(parcel) = sim.native_parcel(local_id) else { + return Ok(false); + }; + self.terraform_with_simulator_int32_single_single_single_single_terraform_action_terraform_brush_size_int32(sim,local_id,parcel.aabb_min.x,parcel.aabb_min.y,parcel.aabb_max.x,parcel.aabb_max.y,action,brush_size,None) + } + pub fn terraform_with_simulator_int32_single_single_single_single_terraform_action_terraform_brush_size_int32( + &self, + sim: Simulator, + local_id: i32, + west: f32, + south: f32, + east: f32, + north: f32, + action: crate::TerraformAction, + brush_size: crate::TerraformBrushSize, + seconds: Option, + ) -> Result { + let height = sim.native_parcel(local_id).map_or(0.0, |p| p.aabb_max.z); + self.terraform_with_simulator_int32_single_single_single_single_terraform_action_terraform_brush_size_int32_single(sim,local_id,west,south,east,north,action,brush_size,seconds.unwrap_or(1),height)?; + Ok(true) + } + pub fn terraform_with_simulator_int32_single_single_single_single_terraform_action_terraform_brush_size_int32_single( + &self, + sim: Simulator, + _local_id: i32, + west: f32, + south: f32, + east: f32, + north: f32, + action: crate::TerraformAction, + brush_size: crate::TerraformBrushSize, + seconds: i32, + height: f32, + ) -> Result<(), Error> { + if ![west, south, east, north, height] + .iter() + .all(|v| v.is_finite()) + || west >= east + || south >= north + || seconds < 0 + { + return Err(Error::Argument); + } + let (a, s) = self.agent_ids(); + let mut p = crate::packets::ModifyLandPacket::new_with_constructor()?; + p.agent_data.agent_id = a; + p.agent_data.session_id = s; + p.modify_block.action = action as u8; + p.modify_block.brush_size = brush_size as u8; + p.modify_block.seconds = seconds as f32; + p.modify_block.height = height; + let mut b = crate::packets::ModifyLandPacketParcelDataBlock::new_with_constructor()?; + b.west = west; + b.south = south; + b.east = east; + b.north = north; + p.parcel_data = vec![b]; + self.send(&sim, &p, PacketType::ModifyLand) + } + + pub async fn request_remote_parcel_id( + &self, + location: Vector3, + region_handle: u64, + region_id: UUID, + cancellation_token: Option, + ) -> Result { + let token = cancellation_token.unwrap_or_default(); + token.throw_if_cancellation_requested()?; + let Some(sim) = self.inner.client.network().current_sim() else { + return Ok(UUID::zero()); + }; + let Some(caps) = sim.native_caps() else { + return Ok(UUID::zero()); + }; + let Some(uri) = caps.capability_uri("RemoteParcelRequest".into())? else { + return Ok(UUID::zero()); + }; + let mut msg = crate::messages::linden::RemoteParcelRequestRequest::new()?; + msg.location = location; + msg.region_handle = region_handle; + msg.region_id = region_id; + let body = OSDParser::serialize_llsd_xml_bytes(OSD::Map(msg.serialize()?.snapshot()))?; + let (response, bytes) = self + .inner + .client + .native_http_caps_client() + .post_with_uri_string_bytes_cancellation_token_i_progress( + uri, + "application/llsd+xml".into(), + body, + token, + None, + ) + .await?; + if !(200..300).contains(&response.status_code) || bytes.len() > MAX_PARCEL_BYTES { + return Ok(UUID::zero()); + } + let OSD::Map(values) = OSDParser::deserialize_with_bytes(bytes)? else { + return Ok(UUID::zero()); + }; + Ok(values + .get("parcel_id") + .and_then(|v| v.as_uuid().ok()) + .unwrap_or_else(UUID::zero)) + } + pub async fn get_parcel_resources( + &self, + parcel_id: UUID, + get_details: bool, + cancellation_token: Option, + ) -> Result<(bool, crate::messages::linden::LandResourcesInfo), Error> { + let token = cancellation_token.unwrap_or_default(); + token.throw_if_cancellation_requested()?; + let mut info = crate::messages::linden::LandResourcesInfo::new()?; + let Some(sim) = self.inner.client.network().current_sim() else { + return Ok((false, info)); + }; + let Some(caps) = sim.native_caps() else { + return Ok((false, info)); + }; + let Some(uri) = caps.capability_uri("LandResources".into())? else { + return Ok((false, info)); + }; + let body = OSDParser::serialize_llsd_xml_bytes(OSD::Map(HashMap::from([( + "parcel_id".into(), + OSD::UUID(parcel_id), + )])))?; + let (response, bytes) = self + .inner + .client + .native_http_caps_client() + .post_with_uri_string_bytes_cancellation_token_i_progress( + uri, + "application/llsd+xml".into(), + body, + token.clone(), + None, + ) + .await?; + if !(200..300).contains(&response.status_code) || bytes.len() > MAX_PARCEL_BYTES { + return Ok((false, info)); + } + let OSD::Map(links) = OSDParser::deserialize_with_bytes(bytes)? else { + return Ok((false, info)); + }; + for key in if get_details { + ["ScriptResourceSummary", "ScriptResourceDetails"].as_slice() + } else { + ["ScriptResourceSummary"].as_slice() + } { + if let Some(uri) = links.get(*key).and_then(|v| v.as_uri().ok().flatten()) { + let (resp, data) = self + .inner + .client + .native_http_caps_client() + .get(uri, token.clone(), None) + .await?; + if (200..300).contains(&resp.status_code) && data.len() <= MAX_PARCEL_BYTES { + if let OSD::Map(values) = OSDParser::deserialize_with_bytes(data)? { + info.deserialize(OSDMap::new_with_dictionary(values)?)?; + } + } + } + } + Ok((true, info)) + } + + pub fn subscribe_force_select_objects_reply( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.force.subscribe(h) + } + pub fn subscribe_parcel_access_list_reply( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.access.subscribe(h) + } + pub fn subscribe_parcel_dwell_reply( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.dwell.subscribe(h) + } + pub fn subscribe_parcel_info_reply( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.info.subscribe(h) + } + pub fn subscribe_parcel_media_command( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.media_command.subscribe(h) + } + pub fn subscribe_parcel_media_update_reply( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.media_update.subscribe(h) + } + pub fn subscribe_parcel_object_owners_reply( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.owners.subscribe(h) + } + pub fn subscribe_parcel_overlays_downloaded( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.overlay.subscribe(h) + } + pub fn subscribe_parcel_properties( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.properties.subscribe(h) + } + pub fn subscribe_sim_parcels_downloaded( + &self, + h: EventHandler, + ) -> Subscription { + self.inner.events.all.subscribe(h) + } +} + +trait ParcelBoundsPacket: GeneratedPacket { + fn set_agent(&mut self, a: UUID, s: UUID); + fn set_bounds(&mut self, w: f32, s: f32, e: f32, n: f32); +} +impl ParcelBoundsPacket for crate::packets::ParcelDividePacket { + fn set_agent(&mut self, a: UUID, s: UUID) { + self.agent_data.agent_id = a; + self.agent_data.session_id = s; + } + fn set_bounds(&mut self, w: f32, s: f32, e: f32, n: f32) { + self.parcel_data.west = w; + self.parcel_data.south = s; + self.parcel_data.east = e; + self.parcel_data.north = n; + } +} +impl ParcelBoundsPacket for crate::packets::ParcelJoinPacket { + fn set_agent(&mut self, a: UUID, s: UUID) { + self.agent_data.agent_id = a; + self.agent_data.session_id = s; + } + fn set_bounds(&mut self, w: f32, s: f32, e: f32, n: f32) { + self.parcel_data.west = w; + self.parcel_data.south = s; + self.parcel_data.east = e; + self.parcel_data.north = n; + } +} + +impl ParcelManagerInner { + fn handle_packet(&self, event: crate::network_manager::RawPacketReceivedEventArgs) { + if self.disposed.load(Ordering::Acquire) || event.data.len() > MAX_PARCEL_BYTES { + return; + } + let mut pos = 0; + match event.packet_type { + PacketType::ParcelOverlay => { + if let Ok(p) = + crate::packets::ParcelOverlayPacket::new_from_bytes(&event.data, &mut pos) + { + if event + .simulator + .native_apply_parcel_overlay(p.parcel_data.sequence_id, p.parcel_data.data) + .unwrap_or(false) + { + self.events.overlay.emit(ParcelOverlayEventArgs { + simulator: event.simulator, + }); + } + } + } + PacketType::ParcelDwellReply => { + if let Ok(p) = + crate::packets::ParcelDwellReplyPacket::new_from_bytes(&event.data, &mut pos) + { + event + .simulator + .native_update_parcel_dwell(p.data.local_id, p.data.dwell); + self.events.dwell.emit(ParcelDwellReplyEventArgs { + parcel_id: p.data.parcel_id, + local_id: p.data.local_id, + dwell: p.data.dwell, + }); + } + } + PacketType::ParcelInfoReply => { + if let Ok(p) = + crate::packets::ParcelInfoReplyPacket::new_from_bytes(&event.data, &mut pos) + { + let d = p.data; + self.events.info.emit(ParcelInfoReplyEventArgs { + parcel: ParcelInfo { + actual_area: d.actual_area, + auction_id: d.auction_id, + billable_area: d.billable_area, + description: wire_string(&d.desc), + dwell: d.dwell, + global_x: d.global_x, + global_y: d.global_y, + global_z: d.global_z, + id: d.parcel_id, + mature: d.flags & 1 != 0, + name: wire_string(&d.name), + owner_id: d.owner_id, + sale_price: d.sale_price, + sim_name: wire_string(&d.sim_name), + snapshot_id: d.snapshot_id, + }, + }); + } + } + PacketType::ParcelAccessListReply => { + if let Ok(p) = crate::packets::ParcelAccessListReplyPacket::new_from_bytes( + &event.data, + &mut pos, + ) { + let entries: Vec<_> = p + .list + .into_iter() + .take(65_535) + .map(|v| ParcelManagerParcelAccessEntry { + agent_id: v.id, + flags: crate::AccessList(v.flags), + time: UNIX_EPOCH + Duration::from_secs(v.time.max(0) as u64), + }) + .collect(); + event.simulator.native_update_parcel_access( + p.data.local_id, + p.data.flags, + entries.clone(), + ); + self.events.access.emit(ParcelAccessListReplyEventArgs { + simulator: event.simulator, + sequence_id: p.data.sequence_id, + local_id: p.data.local_id, + flags: p.data.flags, + access_list: entries, + }); + } + } + PacketType::ForceObjectSelect => { + if let Ok(p) = + crate::packets::ForceObjectSelectPacket::new_from_bytes(&event.data, &mut pos) + { + self.events.force.emit(ForceSelectObjectsReplyEventArgs { + simulator: event.simulator, + object_i_ds: p.data.into_iter().map(|block| block.local_id).collect(), + reset_list: p.header.reset_list, + }); + } + } + PacketType::ParcelMediaUpdate => { + if let Ok(p) = + crate::packets::ParcelMediaUpdatePacket::new_from_bytes(&event.data, &mut pos) + { + self.events + .media_update + .emit(ParcelMediaUpdateReplyEventArgs { + simulator: event.simulator, + media: ParcelMedia { + media_auto_scale: p.data_block.media_auto_scale == 1, + media_desc: wire_string(&p.data_block_extended.media_desc), + media_height: p.data_block_extended.media_height, + media_id: p.data_block.media_id, + media_loop: p.data_block_extended.media_loop & 1 != 0, + media_type: wire_string(&p.data_block_extended.media_type), + media_url: wire_string(&p.data_block.media_url), + media_width: p.data_block_extended.media_width, + }, + }); + } + } + PacketType::ParcelMediaCommandMessage => { + if let Ok(p) = crate::packets::ParcelMediaCommandMessagePacket::new_from_bytes( + &event.data, + &mut pos, + ) { + if let Some(command) = parcel_media_command(p.command_block.command) { + let mut header_pos = 0; + let mut packet_end = i32::try_from(event.data.len()) + .ok() + .and_then(|length| length.checked_sub(1)) + .unwrap_or_default(); + let sequence = crate::packet_wire::decode_header( + &event.data, + &mut header_pos, + &mut packet_end, + ) + .map_or(0, |header| header.sequence); + self.events.media_command.emit(ParcelMediaCommandEventArgs { + simulator: event.simulator, + sequence, + parcel_flags: crate::ParcelFlags(p.command_block.flags), + media_command: command, + time: p.command_block.time, + }); + } + } + } + _ => {} + } + } + + fn handle_owners( + &self, + msg: crate::messages::linden::ParcelObjectOwnersReplyMessage, + simulator: Simulator, + ) { + if msg.prim_owners_block.len() > 65_535 { + return; + } + let prim_owners = msg + .prim_owners_block + .into_iter() + .map(|owner| ParcelManagerParcelPrimOwners { + count: owner.count, + is_group_owned: owner.is_group_owned, + newest_prim: owner.time_stamp, + online_status: owner.online_status, + owner_id: owner.owner_id, + }) + .collect(); + self.events.owners.emit(ParcelObjectOwnersReplyEventArgs { + simulator, + prim_owners, + }); + } + fn handle_properties( + &self, + msg: crate::messages::linden::ParcelPropertiesMessage, + simulator: Simulator, + ) { + if let Ok(parcel) = parcel_from_message(&msg) { + simulator.native_store_parcel(parcel.clone()); + self.events.properties.emit(ParcelPropertiesEventArgs { + simulator: simulator.clone(), + parcel: parcel.clone(), + result: msg.request_result, + selected_prims: msg.selected_prims, + sequence_id: msg.sequence_id, + snap_selection: msg.snap_selection, + }); + if simulator.native_is_parcel_map_full() { + self.events.all.emit(SimParcelsDownloadedEventArgs { + simulator: simulator.clone(), + sim_parcels: simulator.native_parcels_snapshot(), + parcel_map: simulator.native_parcel_map_snapshot(), + }); + } + } + } +} + +fn parcel_media_command(value: u32) -> Option { + Some(match value { + 0 => crate::ParcelMediaCommand::Stop, + 1 => crate::ParcelMediaCommand::Pause, + 2 => crate::ParcelMediaCommand::Play, + 3 => crate::ParcelMediaCommand::Loop, + 4 => crate::ParcelMediaCommand::Texture, + 5 => crate::ParcelMediaCommand::URL, + 6 => crate::ParcelMediaCommand::Time, + 7 => crate::ParcelMediaCommand::Agent, + 8 => crate::ParcelMediaCommand::Unload, + 9 => crate::ParcelMediaCommand::AutoAlign, + 10 => crate::ParcelMediaCommand::Type, + 11 => crate::ParcelMediaCommand::Size, + 12 => crate::ParcelMediaCommand::Desc, + _ => return None, + }) +} + +fn parcel_from_message( + msg: &crate::messages::linden::ParcelPropertiesMessage, +) -> Result { + let mut p = Parcel::new(msg.local_id)?; + p.aabb_max = msg.aabb_max; + p.aabb_min = msg.aabb_min; + p.area = msg.area; + p.auction_id = msg.auction_id; + p.auth_buyer_id = msg.auth_buyer_id; + p.bitmap = msg.bitmap.clone(); + if p.bitmap.len() > 512 { + return Err(Error::Argument); + } + p.category = msg.category; + p.claim_date = msg.claim_date; + p.claim_price = msg.claim_price; + p.desc = msg.desc.clone(); + p.flags = msg.parcel_flags; + p.group_id = msg.group_id; + p.group_prims = msg.group_prims; + p.is_group_owned = msg.is_group_owned; + p.landing = msg.landing_type; + p.max_prims = msg.max_prims; + p.media.media_auto_scale = msg.media_auto_scale; + p.media.media_id = msg.media_id; + p.media.media_url = msg.media_url.clone(); + p.media.media_desc = msg.media_desc.clone(); + p.media.media_height = msg.media_height; + p.media.media_width = msg.media_width; + p.media.media_loop = msg.media_loop; + p.media.media_type = msg.media_type.clone(); + p.music_url = msg.music_url.clone(); + p.name = msg.name.clone(); + p.other_clean_time = msg.other_clean_time; + p.other_count = msg.other_count; + p.other_prims = msg.other_prims; + p.owner_id = msg.owner_id; + p.owner_prims = msg.owner_prims; + p.parcel_prim_bonus = msg.parcel_prim_bonus; + p.pass_hours = msg.pass_hours; + p.pass_price = msg.pass_price; + p.public_count = msg.public_count; + p.region_deny_age_unverified = msg.region_deny_age_unverified; + p.region_deny_anonymous = msg.region_deny_anonymous; + p.region_push_override = msg.region_push_override; + p.rent_price = msg.rent_price; + p.sale_price = msg.sale_price; + p.see_a_vs = msg.see_a_vs; + p.any_av_sounds = msg.any_av_sounds; + p.group_av_sounds = msg.group_av_sounds; + p.self_count = msg.self_count; + p.sim_wide_max_prims = msg.sim_wide_max_prims; + p.sim_wide_total_prims = msg.sim_wide_total_prims; + p.snapshot_id = msg.snapshot_id; + p.status = msg.status; + p.total_prims = msg.total_prims; + p.user_location = msg.user_location; + p.user_look_at = msg.user_look_at; + p.obscure_media = msg.obscure_media; + p.obscure_music = msg.obscure_music; + Ok(p) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::atomic::Ordering; + + #[test] + fn parcel_coordinates_are_bounded() { + let client = GridClient::new().unwrap(); + let sim = Simulator::new( + client.clone(), + "127.0.0.1:13".parse().unwrap(), + 0, + None, + None, + ) + .unwrap(); + let manager = ParcelManager::new(client).unwrap(); + assert_eq!( + manager + .get_parcel_local_id( + sim.clone(), + Vector3 { + x: -1.0, + y: 0.0, + z: 0.0 + } + ) + .unwrap(), + 0 + ); + assert_eq!( + manager + .get_parcel_local_id( + sim, + Vector3 { + x: 256.0, + y: 0.0, + z: 0.0 + } + ) + .unwrap(), + 0 + ); + } + #[test] + fn bitmap_populates_four_meter_map() { + let client = GridClient::new().unwrap(); + let sim = Simulator::new(client, "127.0.0.1:13".parse().unwrap(), 0, None, None).unwrap(); + let mut parcel = Parcel::new(7).unwrap(); + parcel.bitmap = vec![1; 512]; + sim.native_store_parcel(parcel); + assert_eq!(sim.native_parcel_map_at(0, 0), Some(7)); + } + + #[test] + fn overlay_segments_are_correlated_by_sequence_before_completion() { + let client = GridClient::new().unwrap(); + let sim = Simulator::new(client, "127.0.0.1:13".parse().unwrap(), 0, None, None).unwrap(); + + assert!(!sim.native_apply_parcel_overlay(2, vec![3; 1024]).unwrap()); + assert!(!sim.native_apply_parcel_overlay(2, vec![7; 1024]).unwrap()); + assert_eq!(sim.parcel_overlays_received.load(Ordering::Acquire), 1); + assert!(!sim.native_apply_parcel_overlay(0, vec![1; 1024]).unwrap()); + assert!(!sim.native_apply_parcel_overlay(3, vec![4; 1024]).unwrap()); + assert!(sim.native_apply_parcel_overlay(1, vec![2; 1024]).unwrap()); + assert_eq!(sim.parcel_overlays_received.load(Ordering::Acquire), 0); + + { + let overlay = sim + .parcel_overlay + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + assert_eq!(&overlay[0..1024], &[1; 1024]); + assert_eq!(&overlay[1024..2048], &[2; 1024]); + assert_eq!(&overlay[2048..3072], &[7; 1024]); + assert_eq!(&overlay[3072..4096], &[4; 1024]); + } + assert!(sim.native_apply_parcel_overlay(0, vec![0; 1023]).is_err()); + } + + #[test] + fn parcel_properties_caps_message_updates_state_before_emitting() { + let client = GridClient::new().unwrap(); + let sim = Simulator::new( + client.clone(), + "127.0.0.1:13".parse().unwrap(), + 0, + None, + None, + ) + .unwrap(); + let manager = ParcelManager::new(client).unwrap(); + let observed = Arc::new(Mutex::new(None)); + let captured = Arc::clone(&observed); + let _subscription = manager.subscribe_parcel_properties(Arc::new(move |event| { + *mutex(&captured) = Some(( + event.sequence_id(), + event.parcel().local_id, + event.simulator().native_parcel(event.parcel().local_id), + )); + })); + + let mut message = crate::messages::linden::ParcelPropertiesMessage::new().unwrap(); + message.local_id = 23; + message.sequence_id = 91; + message.name = "Packet-path parcel".into(); + message.bitmap = vec![0; 512]; + message.bitmap[0] = 1; + manager.inner.handle_properties(message, sim.clone()); + + let observed = mutex(&observed).clone().expect("parcel event"); + assert_eq!((observed.0, observed.1), (91, 23)); + assert_eq!( + observed.2.expect("state visible to callback").name, + "Packet-path parcel" + ); + assert_eq!(sim.native_parcel_map_at(0, 0), Some(23)); + } +} diff --git a/crates/libremetaverse/src/sound_manager.rs b/crates/libremetaverse/src/sound_manager.rs new file mode 100644 index 0000000..46aeb50 --- /dev/null +++ b/crates/libremetaverse/src/sound_manager.rs @@ -0,0 +1,497 @@ +//! Sound trigger routing and simulator sound event decoding. + +#![allow(clippy::missing_errors_doc)] +#![allow(clippy::must_use_candidate)] +#![allow(clippy::needless_pass_by_value)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::collapsible_if)] // Packet decoding and payload bounds remain distinct checks. +#![allow(clippy::unnecessary_wraps)] // Mapped constructors have fixed fallible signatures. + +use crate::agent_manager::EventRegistry; +use crate::packet_catalog::{GeneratedPacket, PacketType}; +use crate::{Error, GridClient, Simulator}; +use libremetaverse_types::compat::{EventHandler, Subscription}; +use libremetaverse_types::{SoundFlags, UUID, Vector3}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; + +const MAX_SOUND_PACKET_BYTES: usize = 1024 * 1024; +fn mutex(value: &Mutex) -> std::sync::MutexGuard<'_, T> { + value + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) +} + +#[derive(Clone)] +pub struct AttachedSoundEventArgs { + sim: Simulator, + sound_id: UUID, + owner_id: UUID, + object_id: UUID, + gain: f32, + flags: SoundFlags, +} +impl AttachedSoundEventArgs { + pub fn new( + sim: Simulator, + sound_id: UUID, + owner_id: UUID, + object_id: UUID, + gain: f32, + flags: SoundFlags, + ) -> Result { + Ok(Self { + sim, + sound_id, + owner_id, + object_id, + gain, + flags, + }) + } + pub fn simulator(&self) -> Simulator { + self.sim.clone() + } + pub fn sound_id(&self) -> UUID { + self.sound_id + } + pub fn owner_id(&self) -> UUID { + self.owner_id + } + pub fn object_id(&self) -> UUID { + self.object_id + } + pub fn gain(&self) -> f32 { + self.gain + } + pub fn flags(&self) -> SoundFlags { + self.flags + } +} +#[derive(Clone)] +pub struct AttachedSoundGainChangeEventArgs { + sim: Simulator, + object_id: UUID, + gain: f32, +} +impl AttachedSoundGainChangeEventArgs { + pub fn new(sim: Simulator, object_id: UUID, gain: f32) -> Result { + Ok(Self { + sim, + object_id, + gain, + }) + } + pub fn simulator(&self) -> Simulator { + self.sim.clone() + } + pub fn object_id(&self) -> UUID { + self.object_id + } + pub fn gain(&self) -> f32 { + self.gain + } +} +#[derive(Clone)] +pub struct PreloadSoundEventArgs { + sim: Simulator, + sound_id: UUID, + owner_id: UUID, + object_id: UUID, +} +impl PreloadSoundEventArgs { + pub fn new( + sim: Simulator, + sound_id: UUID, + owner_id: UUID, + object_id: UUID, + ) -> Result { + Ok(Self { + sim, + sound_id, + owner_id, + object_id, + }) + } + pub fn simulator(&self) -> Simulator { + self.sim.clone() + } + pub fn sound_id(&self) -> UUID { + self.sound_id + } + pub fn owner_id(&self) -> UUID { + self.owner_id + } + pub fn object_id(&self) -> UUID { + self.object_id + } +} +#[derive(Clone)] +pub struct SoundTriggerEventArgs { + sim: Simulator, + sound_id: UUID, + owner_id: UUID, + object_id: UUID, + parent_id: UUID, + gain: f32, + region_handle: u64, + position: Vector3, +} +impl SoundTriggerEventArgs { + pub fn new( + sim: Simulator, + sound_id: UUID, + owner_id: UUID, + object_id: UUID, + parent_id: UUID, + gain: f32, + region_handle: u64, + position: Vector3, + ) -> Result { + Ok(Self { + sim, + sound_id, + owner_id, + object_id, + parent_id, + gain, + region_handle, + position, + }) + } + pub fn simulator(&self) -> Simulator { + self.sim.clone() + } + pub fn sound_id(&self) -> UUID { + self.sound_id + } + pub fn owner_id(&self) -> UUID { + self.owner_id + } + pub fn object_id(&self) -> UUID { + self.object_id + } + pub fn parent_id(&self) -> UUID { + self.parent_id + } + pub fn gain(&self) -> f32 { + self.gain + } + pub fn region_handle(&self) -> u64 { + self.region_handle + } + pub fn position(&self) -> Vector3 { + self.position + } +} + +#[derive(Default)] +struct SoundEvents { + attached: EventRegistry, + gain: EventRegistry, + preload: EventRegistry, + trigger: EventRegistry, +} +pub(crate) struct SoundManagerInner { + client: Arc, + events: SoundEvents, + raw_subscription: Mutex>, + disposed: AtomicBool, +} +#[derive(Clone)] +pub struct SoundManager { + pub(crate) inner: Arc, +} +impl SoundManager { + pub fn new(client: GridClient) -> Result { + Self::native_new(Arc::new(client)) + } + pub(crate) fn native_new(client: Arc) -> Result { + if let Some(inner) = client.cached_sound_manager_inner() { + return Ok(Self { inner }); + } + let inner = Arc::new(SoundManagerInner { + client: Arc::clone(&client), + events: SoundEvents::default(), + raw_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(event); + } + }, + ))); + Ok(Self { + inner: client.install_sound_manager_inner(inner), + }) + } + pub fn subscribe_attached_sound( + &self, + handler: EventHandler, + ) -> Subscription { + self.inner.events.attached.subscribe(handler) + } + pub fn subscribe_attached_sound_gain_change( + &self, + handler: EventHandler, + ) -> Subscription { + self.inner.events.gain.subscribe(handler) + } + pub fn subscribe_preload_sound( + &self, + handler: EventHandler, + ) -> Subscription { + self.inner.events.preload.subscribe(handler) + } + pub fn subscribe_sound_trigger( + &self, + handler: EventHandler, + ) -> Subscription { + self.inner.events.trigger.subscribe(handler) + } + pub fn dispose(&self) -> Result<(), Error> { + self.inner.disposed.store(true, Ordering::Release); + mutex(&self.inner.raw_subscription).take(); + Ok(()) + } + pub fn play_sound(&self, sound_id: UUID) -> Result<(), Error> { + let mut client = (*self.inner.client).clone(); + let position = client.self_().sim_position(); + self.send_sound_trigger_with_uuid_vector3_single(sound_id, position, 1.0) + } + pub fn send_sound_trigger_with_uuid_vector3( + &self, + sound_id: UUID, + position: Vector3, + ) -> Result<(), Error> { + self.send_sound_trigger_with_uuid_vector3_single(sound_id, position, 1.0) + } + pub fn send_sound_trigger_with_uuid_vector3_single( + &self, + sound_id: UUID, + position: Vector3, + gain: f32, + ) -> Result<(), Error> { + let sim = self + .inner + .client + .network() + .current_sim() + .ok_or(Error::InvalidOperation)?; + self.send_to_sim(sound_id, &sim, sim.handle, position, gain) + } + pub fn send_sound_trigger_with_uuid_simulator_vector3_single( + &self, + sound_id: UUID, + sim: Simulator, + position: Vector3, + gain: f32, + ) -> Result<(), Error> { + self.send_to_sim(sound_id, &sim, sim.handle, position, gain) + } + pub fn send_sound_trigger_with_uuid_u_int64_vector3_single( + &self, + sound_id: UUID, + handle: u64, + position: Vector3, + gain: f32, + ) -> Result<(), Error> { + let sim = self + .inner + .client + .network() + .current_sim() + .ok_or(Error::InvalidOperation)?; + self.send_to_sim(sound_id, &sim, handle, position, gain) + } + fn send_to_sim( + &self, + sound_id: UUID, + sim: &Simulator, + handle: u64, + position: Vector3, + gain: f32, + ) -> Result<(), Error> { + if self.inner.disposed.load(Ordering::Acquire) { + return Err(Error::InvalidOperation); + } + if sound_id == UUID::zero() + || !gain.is_finite() + || !position.x.is_finite() + || !position.y.is_finite() + || !position.z.is_finite() + { + return Err(Error::Argument); + } + let mut packet = crate::packets::SoundTriggerPacket::new_with_constructor()?; + packet.sound_data.sound_id = sound_id; + packet.sound_data.object_id = UUID::zero(); + packet.sound_data.owner_id = UUID::zero(); + packet.sound_data.parent_id = UUID::zero(); + packet.sound_data.handle = handle; + packet.sound_data.position = position; + packet.sound_data.gain = gain.clamp(0.0, 1.0); + let bytes = packet.to_bytes_with_method()?; + sim.native_send_packet_data( + bytes.clone(), + i32::try_from(bytes.len()).map_err(|_| Error::Argument)?, + PacketType::SoundTrigger, + false, + ) + } +} + +impl SoundManagerInner { + fn handle(&self, event: crate::network_manager::RawPacketReceivedEventArgs) { + if self.disposed.load(Ordering::Acquire) || event.data.len() > MAX_SOUND_PACKET_BYTES { + return; + } + let mut position = 0; + match event.packet_type { + PacketType::AttachedSound => { + if let Ok(packet) = + crate::packets::AttachedSoundPacket::new_from_bytes(&event.data, &mut position) + { + let value = packet.data_block; + self.events.attached.emit(AttachedSoundEventArgs { + sim: event.simulator, + sound_id: value.sound_id, + owner_id: value.owner_id, + object_id: value.object_id, + gain: value.gain, + flags: SoundFlags(value.flags), + }); + } + } + PacketType::AttachedSoundGainChange => { + if let Ok(packet) = crate::packets::AttachedSoundGainChangePacket::new_from_bytes( + &event.data, + &mut position, + ) { + self.events.gain.emit(AttachedSoundGainChangeEventArgs { + sim: event.simulator, + object_id: packet.data_block.object_id, + gain: packet.data_block.gain, + }); + } + } + PacketType::PreloadSound => { + if let Ok(packet) = + crate::packets::PreloadSoundPacket::new_from_bytes(&event.data, &mut position) + { + if packet.data_block.len() <= 65_535 { + for value in packet.data_block { + self.events.preload.emit(PreloadSoundEventArgs { + sim: event.simulator.clone(), + sound_id: value.sound_id, + owner_id: value.owner_id, + object_id: value.object_id, + }); + } + } + } + } + PacketType::SoundTrigger => { + if let Ok(packet) = + crate::packets::SoundTriggerPacket::new_from_bytes(&event.data, &mut position) + { + let value = packet.sound_data; + self.events.trigger.emit(SoundTriggerEventArgs { + sim: event.simulator, + sound_id: value.sound_id, + owner_id: value.owner_id, + object_id: value.object_id, + parent_id: value.parent_id, + gain: value.gain, + region_handle: value.handle, + position: value.position, + }); + } + } + _ => {} + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sound_trigger_rejects_zero_ids_and_non_finite_gain() { + let client = GridClient::new().unwrap(); + let manager = SoundManager::new(client).unwrap(); + assert!( + manager + .send_sound_trigger_with_uuid_u_int64_vector3_single( + UUID::zero(), + 0, + Vector3::default(), + 1.0 + ) + .is_err() + ); + } + #[test] + fn event_args_preserve_gain_and_queue_identity() { + let client = GridClient::new().unwrap(); + let sim = Simulator::new(client, "127.0.0.1:13".parse().unwrap(), 0, None, None).unwrap(); + let id = UUID::random().unwrap(); + let value = + AttachedSoundEventArgs::new(sim, id, UUID::zero(), UUID::zero(), 0.25, SoundFlags(2)) + .unwrap(); + assert_eq!(value.sound_id(), id); + assert_eq!(value.gain(), 0.25); + assert_eq!(value.flags(), SoundFlags(2)); + } + + #[test] + fn attached_sound_packet_preserves_ids_gain_and_queue_flag() { + let client = GridClient::new().unwrap(); + let sim = Simulator::new( + client.clone(), + "127.0.0.1:13".parse().unwrap(), + 0, + None, + None, + ) + .unwrap(); + let manager = SoundManager::new(client).unwrap(); + let observed = Arc::new(Mutex::new(None)); + let captured = Arc::clone(&observed); + let _subscription = manager.subscribe_attached_sound(Arc::new(move |event| { + *mutex(&captured) = Some(( + event.sound_id(), + event.owner_id(), + event.object_id(), + event.gain(), + event.flags(), + )); + })); + let sound_id = UUID::random().unwrap(); + let owner_id = UUID::random().unwrap(); + let object_id = UUID::random().unwrap(); + let mut packet = crate::packets::AttachedSoundPacket::new_with_constructor().unwrap(); + packet.data_block.sound_id = sound_id; + packet.data_block.owner_id = owner_id; + packet.data_block.object_id = object_id; + packet.data_block.gain = 0.375; + packet.data_block.flags = 2; + + manager + .inner + .handle(crate::network_manager::RawPacketReceivedEventArgs { + packet_type: PacketType::AttachedSound, + data: packet.to_bytes_with_method().unwrap(), + simulator: sim, + }); + + assert_eq!( + mutex(&observed).clone(), + Some((sound_id, owner_id, object_id, 0.375, SoundFlags(2))) + ); + } +} diff --git a/crates/libremetaverse/src/terrain_codec.rs b/crates/libremetaverse/src/terrain_codec.rs new file mode 100644 index 0000000..9d99859 --- /dev/null +++ b/crates/libremetaverse/src/terrain_codec.rs @@ -0,0 +1,578 @@ +//! Second Life terrain patch codec and layer packet construction. + +#![allow(clippy::missing_errors_doc)] +#![allow(clippy::must_use_candidate)] +#![allow(clippy::needless_pass_by_value)] +#![allow(clippy::cast_possible_truncation)] +#![allow(clippy::cast_precision_loss)] +#![allow(clippy::cast_possible_wrap)] // Bit widths and bounded patch coordinates are range-checked. +#![allow(clippy::cast_sign_loss)] // Patch coordinates are validated before indexing. + +use crate::{BitPack, Error, TerrainPatchLayerType}; + +const PATCH_SIZE: usize = 16; +const PATCH_VALUES: usize = PATCH_SIZE * PATCH_SIZE; +const STRIDE: i32 = 264; +const OO_SQRT2: f32 = std::f32::consts::FRAC_1_SQRT_2; + +#[derive(Clone, Debug, PartialEq)] +pub struct TerrainPatch { + pub data: Vec, + pub x: i32, + pub y: i32, +} +impl TerrainPatch { + pub fn new() -> Result { + Ok(Self { + data: Vec::new(), + x: 0, + y: 0, + }) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct TerrainPatchGroupHeader { + pub stride: i32, + pub patch_size: i32, + pub type_: TerrainPatchLayerType, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct TerrainPatchHeader { + pub dc_offset: f32, + pub range: i32, + pub quant_w_bits: i32, + pub patch_i_ds: i32, + pub word_bits: u32, + large_region: bool, +} +impl Default for TerrainPatchHeader { + fn default() -> Self { + Self { + dc_offset: 0.0, + range: 0, + quant_w_bits: 0, + patch_i_ds: 0, + word_bits: 0, + large_region: false, + } + } +} +impl TerrainPatchHeader { + pub fn set_patch_i_ds(&mut self, xx: i32, yy: i32) -> Result { + let valid = if self.large_region { + (0..=65_535).contains(&xx) && (0..=65_535).contains(&yy) + } else { + (0..=31).contains(&xx) && (0..=31).contains(&yy) + }; + if !valid { + return Err(Error::Argument); + } + self.patch_i_ds = if self.large_region { + (xx << 16) | (yy & 0xffff) + } else { + (xx << 5) | (yy & 0x1f) + }; + Ok(self.patch_i_ds) + } + pub fn large_region(&self) -> bool { + self.large_region + } + pub fn set_large_region(&mut self, value: bool) { + self.large_region = value; + } + pub fn x(&self) -> i32 { + self.patch_i_ds >> if self.large_region { 16 } else { 5 } + } + pub fn set_x(&mut self, value: i32) { + let _ = self.set_patch_i_ds(value, self.y()); + } + pub fn y(&self) -> i32 { + self.patch_i_ds & if self.large_region { 0xffff } else { 0x1f } + } + pub fn set_y(&mut self, value: i32) { + let _ = self.set_patch_i_ds(self.x(), value); + } +} + +pub struct TerrainCompressor; +impl TerrainCompressor { + pub const PATCHES_PER_EDGE: i32 = 16; + pub const END_OF_PATCHES: i32 = 97; + + fn copy_matrix() -> [usize; PATCH_VALUES] { + let mut result = [0; PATCH_VALUES]; + let (mut diag, mut right, mut i, mut j, mut count) = (false, true, 0usize, 0usize, 0usize); + while i < PATCH_SIZE && j < PATCH_SIZE { + result[j * PATCH_SIZE + i] = count; + count += 1; + if !diag { + if right { + if i < PATCH_SIZE - 1 { + i += 1; + } else { + j += 1; + } + } else if j < PATCH_SIZE - 1 { + j += 1; + } else { + i += 1; + } + right = !right; + diag = true; + } else if right { + i += 1; + j -= 1; + if i == PATCH_SIZE - 1 || j == 0 { + diag = false; + } + } else { + i -= 1; + j += 1; + if j == PATCH_SIZE - 1 || i == 0 { + diag = false; + } + } + } + result + } + + fn cosine(u: usize, n: usize) -> f32 { + (((2 * n + 1) as f32) * (u as f32) * std::f32::consts::PI * 0.5 / PATCH_SIZE as f32).cos() + } + + fn prescan(data: &[f32]) -> Result { + if data.len() != PATCH_VALUES || data.iter().any(|value| !value.is_finite()) { + return Err(Error::Argument); + } + let mut min = f32::INFINITY; + let mut max = f32::NEG_INFINITY; + for value in data { + min = min.min(*value); + max = max.max(*value); + } + Ok(TerrainPatchHeader { + dc_offset: min, + range: ((max - min) + 1.0) as i32, + ..TerrainPatchHeader::default() + }) + } + + fn compress(data: &[f32], header: &TerrainPatchHeader) -> [i32; PATCH_VALUES] { + let premult = 1024.0 / header.range.max(1) as f32; + let sub = 512.0 + header.dc_offset * premult; + let mut block = [0.0; PATCH_VALUES]; + for (target, value) in block.iter_mut().zip(data) { + *target = *value * premult - sub; + } + let mut rows = [0.0; PATCH_VALUES]; + for line in 0..PATCH_SIZE { + let base = line * PATCH_SIZE; + let total: f32 = block[base..base + PATCH_SIZE].iter().sum(); + rows[base] = OO_SQRT2 * total; + for u in 1..PATCH_SIZE { + rows[base + u] = (0..PATCH_SIZE) + .map(|n| block[base + n] * Self::cosine(u, n)) + .sum(); + } + } + let copy = Self::copy_matrix(); + let mut output = [0; PATCH_VALUES]; + for column in 0..PATCH_SIZE { + let total: f32 = (0..PATCH_SIZE).map(|n| rows[n * PATCH_SIZE + column]).sum(); + output[copy[column]] = (OO_SQRT2 * total * 0.125 / (1.0 + 2.0 * column as f32)) as i32; + for u in 1..PATCH_SIZE { + let total: f32 = (0..PATCH_SIZE) + .map(|n| rows[n * PATCH_SIZE + column] * Self::cosine(u, n)) + .sum(); + output[copy[u * PATCH_SIZE + column]] = + (total * 0.125 / (1.0 + 2.0 * (u + column) as f32)) as i32; + } + } + output + } + + fn encode_header( + output: &mut BitPack, + mut header: TerrainPatchHeader, + patch: &[i32], + ) -> Result { + let base = (header.quant_w_bits & 0x0f) + 2; + let mut word_bits = base / 2; + for value in patch.iter().copied().filter(|value| *value != 0) { + let magnitude = value.unsigned_abs(); + let bits = (u32::BITS - magnitude.leading_zeros()) as i32; + word_bits = word_bits.max(bits); + } + word_bits = (word_bits + 1).clamp(2, 17); + header.quant_w_bits = (header.quant_w_bits & 0xf0) | (word_bits - 2); + output.pack_bits_with_int32_int32(header.quant_w_bits, 8)?; + output.pack_float(header.dc_offset)?; + output.pack_bits_with_int32_int32(header.range, 16)?; + output.pack_bits_with_int32_int32( + header.patch_i_ds, + if header.large_region { 32 } else { 10 }, + )?; + Ok(word_bits) + } + + fn encode_patch_values( + output: &mut BitPack, + patch: &[i32], + word_bits: i32, + ) -> Result<(), Error> { + for (index, value) in patch.iter().copied().enumerate() { + if value == 0 { + if patch[index..].iter().all(|candidate| *candidate == 0) { + output.pack_bits_with_int32_int32(2, 2)?; + return Ok(()); + } + output.pack_bits_with_int32_int32(0, 1)?; + } else { + output.pack_bits_with_int32_int32(if value < 0 { 7 } else { 6 }, 3)?; + let maximum = (1_i32 << word_bits) - 1; + output.pack_bits_with_int32_int32( + i32::try_from(value.unsigned_abs()) + .unwrap_or(maximum) + .min(maximum), + word_bits, + )?; + } + } + Ok(()) + } + + fn encode_one( + output: &mut BitPack, + data: &[f32], + x: i32, + y: i32, + large_region: bool, + ) -> Result<(), Error> { + let mut header = Self::prescan(data)?; + header.large_region = large_region; + header.quant_w_bits = 136; + header.set_patch_i_ds(x, y)?; + let patch = Self::compress(data, &header); + let word_bits = Self::encode_header(output, header, &patch)?; + Self::encode_patch_values(output, &patch, word_bits) + } + + pub fn create_layer_data_packet( + patches: Vec, + type_: TerrainPatchLayerType, + ) -> Result { + if patches.len() > 4096 { + return Err(Error::Argument); + } + let capacity = patches + .len() + .checked_mul(1024) + .and_then(|value| value.checked_add(32)) + .ok_or(Error::Argument)?; + let mut bits = BitPack::new(vec![0; capacity], 0)?; + bits.pack_bits_with_int32_int32(STRIDE, 16)?; + bits.pack_bits_with_int32_int32(PATCH_SIZE as i32, 8)?; + bits.pack_bits_with_int32_int32(i32::from(type_.0), 8)?; + let large = matches!(type_.0, 57 | 58 | 77); + for patch in patches { + Self::encode_one(&mut bits, &patch.data, patch.x, patch.y, large)?; + } + bits.pack_bits_with_int32_int32(Self::END_OF_PATCHES, 8)?; + let used = usize::try_from(bits.byte_pos()) + .map_err(|_| Error::Argument)? + .checked_add(1) + .ok_or(Error::Argument)?; + let mut packet = crate::packets::LayerDataPacket::new_with_constructor()?; + packet.layer_id.type_ = type_.0; + packet.layer_data.data = bits.data[..used.min(bits.data.len())].to_vec(); + Ok(packet) + } + + pub fn create_land_packet_with_single_array_int32_int32( + patch_data: Vec, + x: i32, + y: i32, + ) -> Result { + Self::create_layer_data_packet( + vec![TerrainPatch { + data: patch_data, + x, + y, + }], + TerrainPatchLayerType::LAND, + ) + } + pub fn create_land_packet_with_single_array_int32_int32_80090bdf( + patch_data: Vec, + x: i32, + y: i32, + ) -> Result { + Self::create_land_packet_with_single_array_int32_int32(patch_data, x, y) + } + pub fn create_land_packet_with_single_array_int32_array( + heightmap: Vec, + patches: Vec, + ) -> Result { + let edge = (heightmap.len() as f64).sqrt() as usize; + if edge * edge != heightmap.len() + || edge < PATCH_SIZE + || !edge.is_multiple_of(PATCH_SIZE) + || patches.len() > 4096 + { + return Err(Error::Argument); + } + let per_edge = edge / PATCH_SIZE; + let mut values = Vec::with_capacity(patches.len()); + for id in patches { + let id = usize::try_from(id).map_err(|_| Error::Argument)?; + let (x, y) = (id % per_edge, id / per_edge); + if y >= per_edge { + return Err(Error::Argument); + } + let mut data = Vec::with_capacity(PATCH_VALUES); + for row in 0..PATCH_SIZE { + let start = (y * PATCH_SIZE + row) * edge + x * PATCH_SIZE; + data.extend_from_slice(&heightmap[start..start + PATCH_SIZE]); + } + values.push(TerrainPatch { + data, + x: x as i32, + y: y as i32, + }); + } + let layer = if edge > 256 { + TerrainPatchLayerType::LAND_EXTENDED + } else { + TerrainPatchLayerType::LAND + }; + Self::create_layer_data_packet(values, layer) + } + + pub fn create_patch_with_bit_pack_single_array_int32_int32( + mut output: BitPack, + patch_data: Vec, + x: i32, + y: i32, + ) -> Result<(), Error> { + Self::encode_one(&mut output, &patch_data, x, y, false) + } + pub fn create_patch_with_bit_pack_single_array_int32_int32_boolean( + mut output: BitPack, + patch_data: Vec, + x: i32, + y: i32, + large_region: bool, + ) -> Result<(), Error> { + Self::encode_one(&mut output, &patch_data, x, y, large_region) + } + pub fn create_patch_from_heightmap_with_bit_pack_single_array_int32_int32( + output: BitPack, + heightmap: Vec, + x: i32, + y: i32, + ) -> Result<(), Error> { + Self::create_patch_from_heightmap_with_bit_pack_single_array_int32_int32_boolean( + output, heightmap, x, y, false, + ) + } + pub fn create_patch_from_heightmap_with_bit_pack_single_array_int32_int32_boolean( + mut output: BitPack, + heightmap: Vec, + x: i32, + y: i32, + large_region: bool, + ) -> Result<(), Error> { + if heightmap.len() != 65_536 || !(0..16).contains(&x) || !(0..16).contains(&y) { + return Err(Error::Argument); + } + let mut data = Vec::with_capacity(PATCH_VALUES); + for row in 0..PATCH_SIZE { + let start = (y as usize * PATCH_SIZE + row) * 256 + x as usize * PATCH_SIZE; + data.extend_from_slice(&heightmap[start..start + PATCH_SIZE]); + } + Self::encode_one(&mut output, &data, x, y, large_region) + } + + pub fn decode_patch_header_with_bit_pack( + bitpack: BitPack, + ) -> Result { + Self::decode_patch_header_with_bit_pack_boolean(bitpack, false) + } + pub fn decode_patch_header_with_bit_pack_boolean( + mut bitpack: BitPack, + large_region: bool, + ) -> Result { + Self::decode_header(&mut bitpack, large_region) + } + pub(crate) fn decode_header( + bitpack: &mut BitPack, + large_region: bool, + ) -> Result { + let quant_w_bits = bitpack.unpack_bits(8)?; + if quant_w_bits == Self::END_OF_PATCHES { + return Ok(TerrainPatchHeader { + quant_w_bits, + large_region, + ..TerrainPatchHeader::default() + }); + } + let dc_offset = bitpack.unpack_float()?; + let range = bitpack.unpack_bits(16)?; + let patch_i_ds = bitpack.unpack_bits(if large_region { 32 } else { 10 })?; + Ok(TerrainPatchHeader { + dc_offset, + range, + quant_w_bits, + patch_i_ds, + word_bits: ((quant_w_bits & 0x0f) + 2) as u32, + large_region, + }) + } + pub fn decode_patch( + mut patches: Vec, + mut bitpack: BitPack, + header: TerrainPatchHeader, + size: i32, + ) -> Result<(), Error> { + Self::decode_values(&mut patches, &mut bitpack, header, size) + } + pub(crate) fn decode_values( + patches: &mut [i32], + bitpack: &mut BitPack, + header: TerrainPatchHeader, + size: i32, + ) -> Result<(), Error> { + let count = usize::try_from(size.checked_mul(size).ok_or(Error::Argument)?) + .map_err(|_| Error::Argument)?; + if patches.len() < count + || !(1..=32).contains(&size) + || !(2..=17).contains(&header.word_bits) + { + return Err(Error::Argument); + } + for index in 0..count { + if bitpack.unpack_bits(1)? == 0 { + patches[index] = 0; + continue; + } + if bitpack.unpack_bits(1)? == 0 { + patches[index..count].fill(0); + break; + } + let negative = bitpack.unpack_bits(1)? != 0; + let value = bitpack.unpack_bits(header.word_bits as i32)?; + patches[index] = if negative { -value } else { value }; + } + Ok(()) + } + pub fn decompress_patch( + patches: Vec, + header: TerrainPatchHeader, + group: TerrainPatchGroupHeader, + ) -> Result, Error> { + if group.patch_size != 16 || patches.len() < PATCH_VALUES { + return Err(Error::Argument); + } + let copy = Self::copy_matrix(); + let mut block = [0.0; PATCH_VALUES]; + for index in 0..PATCH_VALUES { + block[index] = patches[copy[index]] as f32 + * (1.0 + 2.0 * ((index / PATCH_SIZE) + (index % PATCH_SIZE)) as f32); + } + let mut columns = [0.0; PATCH_VALUES]; + for column in 0..PATCH_SIZE { + for n in 0..PATCH_SIZE { + columns[n * PATCH_SIZE + column] = OO_SQRT2 * block[column] + + (1..PATCH_SIZE) + .map(|u| block[u * PATCH_SIZE + column] * Self::cosine(u, n)) + .sum::(); + } + } + let mut transformed = [0.0; PATCH_VALUES]; + for line in 0..PATCH_SIZE { + let base = line * PATCH_SIZE; + for n in 0..PATCH_SIZE { + transformed[base + n] = (OO_SQRT2 * columns[base] + + (1..PATCH_SIZE) + .map(|u| columns[base + u] * Self::cosine(u, n)) + .sum::()) + * 0.125; + } + } + let prequant = (header.quant_w_bits >> 4) + 2; + if !(1..31).contains(&prequant) { + return Err(Error::Argument); + } + let mult = header.range as f32 / (1_i32 << prequant) as f32; + let add = mult * (1_i32 << (prequant - 1)) as f32 + header.dc_offset; + Ok(transformed + .into_iter() + .map(|value| value * mult + add) + .collect()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn terrain_codec_round_trip_stays_within_quantization_error() { + let source: Vec = (0..PATCH_VALUES) + .map(|index| 20.0 + index as f32 * 0.03125) + .collect(); + let packet = TerrainCompressor::create_land_packet_with_single_array_int32_int32( + source.clone(), + 3, + 7, + ) + .unwrap(); + let mut bits = BitPack::new(packet.layer_data.data, 0).unwrap(); + assert_eq!(bits.unpack_bits(16).unwrap(), STRIDE); + let size = bits.unpack_bits(8).unwrap(); + assert_eq!( + bits.unpack_bits(8).unwrap(), + i32::from(TerrainPatchLayerType::LAND.0) + ); + let header = TerrainCompressor::decode_header(&mut bits, false).unwrap(); + let mut coefficients = vec![0; PATCH_VALUES]; + TerrainCompressor::decode_values(&mut coefficients, &mut bits, header, size).unwrap(); + let decoded = TerrainCompressor::decompress_patch( + coefficients, + header, + TerrainPatchGroupHeader { + stride: STRIDE, + patch_size: size, + type_: TerrainPatchLayerType::LAND, + }, + ) + .unwrap(); + assert_eq!((header.x(), header.y()), (3, 7)); + assert!( + source + .iter() + .zip(decoded) + .all(|(expected, actual)| (expected - actual).abs() < 0.2) + ); + } + #[test] + fn terrain_payloads_are_bounded_and_coordinates_validated() { + assert!( + TerrainCompressor::create_land_packet_with_single_array_int32_int32( + vec![0.0; 255], + 0, + 0 + ) + .is_err() + ); + assert!( + TerrainCompressor::create_land_packet_with_single_array_int32_int32( + vec![0.0; 256], + 32, + 0 + ) + .is_err() + ); + } +} diff --git a/crates/libremetaverse/src/terrain_manager.rs b/crates/libremetaverse/src/terrain_manager.rs new file mode 100644 index 0000000..e12937f --- /dev/null +++ b/crates/libremetaverse/src/terrain_manager.rs @@ -0,0 +1,464 @@ +//! Terrain layer reception, wind state, and PBR terrain material capabilities. + +#![allow(clippy::missing_errors_doc)] +#![allow(clippy::must_use_candidate)] +#![allow(clippy::needless_pass_by_value)] +#![allow(clippy::unnecessary_wraps)] // Mapped constructors have fixed fallible signatures. + +use crate::agent_manager::EventRegistry; +use crate::packet_catalog::{GeneratedPacket, PacketType}; +use crate::terrain_codec::{TerrainCompressor, TerrainPatch, TerrainPatchGroupHeader}; +use crate::{BitPack, Error, GridClient, Simulator, TerrainPatchLayerType}; +use futures_channel::oneshot; +use futures_util::future::{Either, select}; +use futures_util::pin_mut; +use libremetaverse_structured_data::{OSD, OSDMap, OSDParser}; +use libremetaverse_types::Vector2; +use libremetaverse_types::compat::{CancellationToken, EventHandler, Subscription, Uri}; +use std::collections::HashMap; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; + +const MAX_LAYER_BYTES: usize = 16 * 1024 * 1024; + +fn mutex(value: &Mutex) -> std::sync::MutexGuard<'_, T> { + value + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) +} + +#[derive(Clone)] +pub struct LandPatchReceivedEventArgs { + simulator: Simulator, + x: i32, + y: i32, + patch_size: i32, + height_map: Vec, +} +impl LandPatchReceivedEventArgs { + pub fn new( + simulator: Simulator, + x: i32, + y: i32, + patch_size: i32, + height_map: Vec, + ) -> Result { + if height_map.len() + != usize::try_from(patch_size.checked_mul(patch_size).ok_or(Error::Argument)?) + .map_err(|_| Error::Argument)? + { + return Err(Error::Argument); + } + Ok(Self { + simulator, + x, + y, + patch_size, + height_map, + }) + } + pub fn simulator(&self) -> Simulator { + self.simulator.clone() + } + pub fn x(&self) -> i32 { + self.x + } + pub fn y(&self) -> i32 { + self.y + } + pub fn patch_size(&self) -> i32 { + self.patch_size + } + pub fn height_map(&self) -> Vec { + self.height_map.clone() + } +} + +pub(crate) struct TerrainManagerInner { + client: Arc, + events: EventRegistry, + raw_subscription: Mutex>, + disposed: AtomicBool, +} + +#[derive(Clone)] +pub struct TerrainManager { + pub(crate) inner: Arc, +} +impl TerrainManager { + pub const TERRAIN_MATERIAL_SLOT_COUNT: i32 = 4; + pub fn new(client: GridClient) -> Result { + Self::native_new(Arc::new(client)) + } + pub(crate) fn native_new(client: Arc) -> Result { + if let Some(inner) = client.cached_terrain_manager_inner() { + return Ok(Self { inner }); + } + let inner = Arc::new(TerrainManagerInner { + client: Arc::clone(&client), + events: EventRegistry::default(), + raw_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_layer(event); + } + }, + ))); + Ok(Self { + inner: client.install_terrain_manager_inner(inner), + }) + } + pub fn subscribe_land_patch_received( + &self, + handler: EventHandler, + ) -> Subscription { + self.inner.events.subscribe(handler) + } + pub fn dispose(&self) -> Result<(), Error> { + self.inner.disposed.store(true, Ordering::Release); + mutex(&self.inner.raw_subscription).take(); + Ok(()) + } + + async fn capability( + &self, + name: &str, + token: &CancellationToken, + ) -> Result, Error> { + if self.inner.disposed.load(Ordering::Acquire) { + return Err(Error::InvalidOperation); + } + let Some(caps) = self + .inner + .client + .network() + .current_sim() + .and_then(|sim| sim.native_caps()) + else { + return Ok(None); + }; + if let Some(uri) = caps.capability_uri(name.into())? { + return Ok(Some(uri)); + } + if caps.seed_request_finished() { + return Ok(None); + } + let (sender, receiver) = oneshot::channel(); + let sender = Arc::new(Mutex::new(Some(sender))); + let wake = Arc::clone(&sender); + let _subscription = caps.subscribe_capabilities_received(Some(Arc::new(move |_| { + if let Some(sender) = mutex(&wake).take() { + let _ = sender.send(()); + } + }))); + if let Some(uri) = caps.capability_uri(name.into())? { + return Ok(Some(uri)); + } + let cancellation = token.cancelled(); + pin_mut!(receiver, cancellation); + match select(receiver, cancellation).await { + Either::Left(_) => caps.capability_uri(name.into()), + Either::Right(_) => Err(Error::Cancelled), + } + } + pub async fn get_terrain_material_overrides( + &self, + cancellation_token: Option, + ) -> Result>>, Error> { + let token = cancellation_token.unwrap_or_default(); + let Some(uri) = self.capability("ModifyRegion", &token).await? else { + return Ok(None); + }; + let (response, bytes) = self + .inner + .client + .native_http_caps_client() + .get(uri, token, None) + .await?; + if !(200..300).contains(&response.status_code) || bytes.len() > MAX_LAYER_BYTES { + return Ok(None); + } + let OSD::Map(map) = OSDParser::deserialize_with_bytes(bytes)? else { + return Ok(None); + }; + if !map + .get("success") + .and_then(|value| value.as_boolean().ok()) + .unwrap_or(false) + { + return Ok(None); + } + let Some(OSD::Array(overrides)) = map.get("overrides") else { + return Ok(None); + }; + if overrides.len() < 4 || overrides.len() > 1024 { + return Ok(None); + } + let mut result = Vec::with_capacity(4); + for value in overrides.iter().take(4) { + match value { + OSD::Map(values) if !values.is_empty() => { + result.push(Some(crate::assets::AssetMaterial::from_override_osd( + OSDMap::new_with_dictionary(values.clone())?, + )?)); + } + _ => result.push(None), + } + } + Ok(Some(result)) + } + pub async fn set_terrain_material_overrides( + &self, + overrides: Vec>, + cancellation_token: Option, + ) -> Result { + if overrides.len() != 4 { + return Err(Error::Argument); + } + let token = cancellation_token.unwrap_or_default(); + let Some(uri) = self.capability("ModifyRegion", &token).await? else { + return Ok(false); + }; + let mut wire = Vec::with_capacity(4); + for material in overrides { + wire.push(match material { + Some(value) => OSD::Map(value.to_override_osd()?.snapshot()), + None => OSD::Map(HashMap::new()), + }); + } + let body = OSDParser::serialize_llsd_xml_bytes(OSD::Map(HashMap::from([( + "overrides".into(), + OSD::Array(wire), + )])))?; + if body.len() > MAX_LAYER_BYTES { + return Err(Error::Argument); + } + let (response, bytes) = self + .inner + .client + .native_http_caps_client() + .post_with_uri_string_bytes_cancellation_token_i_progress( + uri, + "application/llsd+xml".into(), + body, + token, + None, + ) + .await?; + if !(200..300).contains(&response.status_code) || bytes.len() > MAX_LAYER_BYTES { + return Ok(false); + } + let OSD::Map(map) = OSDParser::deserialize_with_bytes(bytes)? else { + return Ok(false); + }; + Ok(map + .get("success") + .and_then(|value| value.as_boolean().ok()) + .unwrap_or(false)) + } +} + +impl TerrainManagerInner { + fn handle_layer(&self, event: crate::network_manager::RawPacketReceivedEventArgs) { + if self.disposed.load(Ordering::Acquire) + || event.packet_type != PacketType::LayerData + || event.data.len() > MAX_LAYER_BYTES + { + return; + } + let mut position = 0; + let Ok(packet) = + crate::packets::LayerDataPacket::new_from_bytes(&event.data, &mut position) + else { + return; + }; + let _ = self.decode_layer(packet, event.simulator); + } + fn decode_layer( + &self, + packet: crate::packets::LayerDataPacket, + simulator: Simulator, + ) -> Result<(), Error> { + if packet.layer_data.data.len() > MAX_LAYER_BYTES { + return Err(Error::Argument); + } + let mut bits = BitPack::new(packet.layer_data.data, 0)?; + let group = TerrainPatchGroupHeader { + stride: bits.unpack_bits(16)?, + patch_size: bits.unpack_bits(8)?, + type_: TerrainPatchLayerType( + u8::try_from(bits.unpack_bits(8)?).map_err(|_| Error::Argument)?, + ), + }; + if group.patch_size != 16 { + return Err(Error::Argument); + } + match packet.layer_id.type_ { + 76 | 77 => self.decode_land(&mut bits, group, simulator, packet.layer_id.type_ == 77), + 55 | 57 => Self::decode_wind(&mut bits, group, simulator, packet.layer_id.type_ == 57), + 56 | 58 | 87 => Ok(()), + _ => Err(Error::Argument), + } + } + fn decode_land( + &self, + bits: &mut BitPack, + group: TerrainPatchGroupHeader, + simulator: Simulator, + large: bool, + ) -> Result<(), Error> { + let mut count = 0usize; + loop { + let header = TerrainCompressor::decode_header(bits, large)?; + if header.quant_w_bits == TerrainCompressor::END_OF_PATCHES { + break; + } + if !large && (!(0..16).contains(&header.x()) || !(0..16).contains(&header.y())) { + return Err(Error::Argument); + } + count += 1; + if count > 4096 { + return Err(Error::Argument); + } + let mut coefficients = vec![0; 1024]; + TerrainCompressor::decode_values(&mut coefficients, bits, header, group.patch_size)?; + let heights = TerrainCompressor::decompress_patch(coefficients, header, group)?; + simulator.native_store_terrain_patch( + TerrainPatch { + data: heights.clone(), + x: header.x(), + y: header.y(), + }, + large, + )?; + self.events.emit(LandPatchReceivedEventArgs::new( + simulator.clone(), + header.x(), + header.y(), + group.patch_size, + heights, + )?); + } + Ok(()) + } + fn decode_wind( + bits: &mut BitPack, + mut group: TerrainPatchGroupHeader, + simulator: Simulator, + large: bool, + ) -> Result<(), Error> { + group.stride = group.patch_size; + let mut axes = Vec::with_capacity(2); + for _ in 0..2 { + let header = TerrainCompressor::decode_header(bits, large)?; + if header.quant_w_bits == TerrainCompressor::END_OF_PATCHES { + return Err(Error::Argument); + } + let mut coefficients = vec![0; 1024]; + TerrainCompressor::decode_values(&mut coefficients, bits, header, group.patch_size)?; + axes.push(TerrainCompressor::decompress_patch( + coefficients, + header, + group, + )?); + } + simulator.native_store_wind( + axes[0] + .iter() + .zip(&axes[1]) + .take(256) + .map(|(x, y)| Vector2 { x: *x, y: *y }) + .collect(), + ); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn land_event_validates_patch_dimensions() { + let client = GridClient::new().unwrap(); + let sim = Simulator::new(client, "127.0.0.1:13".parse().unwrap(), 0, None, None).unwrap(); + assert!(LandPatchReceivedEventArgs::new(sim, 0, 0, 16, vec![0.0; 255]).is_err()); + } + + #[test] + fn layer_packet_updates_terrain_before_emitting_the_correlated_patch() { + let client = GridClient::new().unwrap(); + let sim = Simulator::new( + client.clone(), + "127.0.0.1:13".parse().unwrap(), + 0, + None, + None, + ) + .unwrap(); + let manager = TerrainManager::new(client).unwrap(); + let observed = Arc::new(Mutex::new(None)); + let captured = Arc::clone(&observed); + let _subscription = manager.subscribe_land_patch_received(Arc::new(move |event| { + let mut height = 0.0; + let stored = event + .simulator() + .terrain_height_at_point(event.x() * 16, event.y() * 16, &mut height) + .unwrap(); + *mutex(&captured) = Some((event.x(), event.y(), stored, height)); + })); + let source: Vec = (0..256).map(|index| 32.0 + index as f32 / 64.0).collect(); + let packet = TerrainCompressor::create_land_packet_with_single_array_int32_int32( + source.clone(), + 5, + 9, + ) + .unwrap(); + + manager.inner.decode_layer(packet, sim).unwrap(); + + let observed = mutex(&observed).expect("land event"); + assert_eq!((observed.0, observed.1, observed.2), (5, 9, true)); + assert!((observed.3 - source[0]).abs() < 0.2); + } + + #[test] + fn rectangular_large_region_uses_independent_patch_dimensions() { + let client = GridClient::new().unwrap(); + let sim = Simulator::new( + client, + "127.0.0.1:13".parse().unwrap(), + 0, + Some(512), + Some(256), + ) + .unwrap(); + sim.native_store_terrain_patch( + TerrainPatch { + data: vec![41.5; 256], + x: 31, + y: 15, + }, + true, + ) + .unwrap(); + let mut height = 0.0; + assert!(sim.terrain_height_at_point(511, 255, &mut height).unwrap()); + assert_eq!(height, 41.5); + assert!( + sim.native_store_terrain_patch( + TerrainPatch { + data: vec![0.0; 256], + x: 0, + y: 16, + }, + true, + ) + .is_err() + ); + } +} diff --git a/docs/land.md b/docs/land.md new file mode 100644 index 0000000..28aa7ae --- /dev/null +++ b/docs/land.md @@ -0,0 +1,73 @@ +# Land, terrain, environment, and sound + +`GridClient` owns one lazily constructed `ParcelManager`, `TerrainManager`, +`EnvironmentManager`, and `SoundManager`. Cloning a manager keeps the same +registries and state; disposing it releases packet and capability +subscriptions. Incoming state is committed before an event callback is run, +and registries snapshot callbacks before invoking them so callbacks never run +under a manager lock. + +## Parcels + +Parcel properties received from the capability event queue retain their +request sequence, result, selection state, region-local coordinates, flags, +permissions, media settings, and 512-byte occupancy bitmap. The bitmap is +expanded to the simulator's four-metre parcel map. Overlay packets are four +fixed 1 KiB segments: they may arrive out of order, duplicate sequence IDs do +not advance completion, and the downloaded event is emitted only after all +four unique segments are present. + +Requests select the reference LLUDP packet for property, access-list, dwell, +owner, update, subdivision/join, purchase, reclaim, deed, release, auction, +selection/return, and terraform operations. Remote parcel lookup and script +resource summary/detail use their named capabilities. Resource replies, +variable blocks, strings, bitmaps, and HTTP bodies are bounded before state is +published. Bulk parcel discovery is cancellation-aware and skips cells already +covered by received bitmaps. + +Parcel and estate mutation methods send real protocol operations. Applications +must place user confirmation and authorization policy in front of destructive +operations. Live-grid tests must remain ignored unless explicit credentials +and opt-in configuration are supplied; the default tests use only synthetic +packets and in-memory HTTP handlers. + +## Terrain and wind + +`TerrainCompressor` implements the 16×16 DCT patch format, including zig-zag +coefficient order, quantization headers, normal and large-region coordinate +encodings, end markers, and bounded layer construction. `TerrainManager` +decodes land and wind layer packets, stores patches before emitting +`LandPatchReceived`, and keeps width and height independent for rectangular +variable regions. `Simulator::terrain_height_at_point` resolves a local metre +coordinate through the stored patch grid. + +PBR terrain material overrides use the `ModifyRegion` capability. Exactly four +slots are accepted. Empty maps remain empty slots; non-empty maps use the +native `AssetMaterial` override codec. Capability and LLSD response bodies are +limited to 16 MiB and operations honor cancellation. + +## Environment + +Extended environment requests use `ExtEnvironment`; parcel operations append +the compatibility query key `parcelid`, while region operations leave the URI +unchanged. GET, POST, and DELETE implement retrieval, update, and reset. +`EnvironmentSettings` provides the legacy LLSD path. Parsed region and legacy +state is installed before update events, and all request/response bodies are +limited to 16 MiB. + +## Sound + +The sound manager emits typed events for attached sounds, attached gain +changes, preload queues, and positional sound triggers. UUID identity, gain, +region handle, position, and `SoundFlags` (including queueing) are retained. +Outgoing triggers validate IDs and finite values, clamp gain to the protocol +range, and select the current or explicitly supplied simulator and region +handle. + +## Focused validation + +The native tests cover terrain golden round trips and bounds, rectangular +region addressing, parcel bitmap and overlay correlation, capability parcel +state publication, environment LLSD shapes, sound packet identity/queueing, and +state-before-callback ordering. The translated compatibility cases cover +extended-environment HTTP selection and all four terrain-material slots. diff --git a/tests/compat/tests/world_manager_shims.rs b/tests/compat/tests/world_manager_shims.rs index 11130a0..d7fbe12 100644 --- a/tests/compat/tests/world_manager_shims.rs +++ b/tests/compat/tests/world_manager_shims.rs @@ -81,8 +81,7 @@ fn world_facing_constructors_preserve_implemented_and_pending_behavior() { assert!(parcel.always_request_acl); assert!(parcel.always_request_dwell); assert!(!parcel.pool_parcel_data); - assert_eq!( - member_id(TerrainPatch::new()), - "M:LibreMetaverse.TerrainPatch.#ctor" - ); + let terrain = TerrainPatch::new().expect("TerrainPatch constructor"); + assert!(terrain.data.is_empty()); + assert_eq!((terrain.x, terrain.y), (0, 0)); } diff --git a/tools/generate_api_shims.py b/tools/generate_api_shims.py index 30a2709..6067304 100644 --- a/tools/generate_api_shims.py +++ b/tools/generate_api_shims.py @@ -147,6 +147,36 @@ NATIVE_TYPES = { "T:LibreMetaverse.RegionExperiencesEventArgs": "crate::agent_messages::RegionExperiencesEventArgs", "T:LibreMetaverse.SetDisplayNameReplyEventArgs": "crate::agent_messages::SetDisplayNameReplyEventArgs", "T:LibreMetaverse.TeleportEventArgs": "crate::agent_movement::TeleportEventArgs", + "T:LibreMetaverse.TerrainCompressor": "crate::terrain_codec::TerrainCompressor", + "T:LibreMetaverse.TerrainPatch": "crate::terrain_codec::TerrainPatch", + "T:LibreMetaverse.TerrainPatch.GroupHeader": "crate::terrain_codec::TerrainPatchGroupHeader", + "T:LibreMetaverse.TerrainPatch.Header": "crate::terrain_codec::TerrainPatchHeader", + "T:LibreMetaverse.TerrainManager": "crate::terrain_manager::TerrainManager", + "T:LibreMetaverse.LandPatchReceivedEventArgs": "crate::terrain_manager::LandPatchReceivedEventArgs", + "T:LibreMetaverse.SoundManager": "crate::sound_manager::SoundManager", + "T:LibreMetaverse.AttachedSoundEventArgs": "crate::sound_manager::AttachedSoundEventArgs", + "T:LibreMetaverse.AttachedSoundGainChangeEventArgs": "crate::sound_manager::AttachedSoundGainChangeEventArgs", + "T:LibreMetaverse.PreloadSoundEventArgs": "crate::sound_manager::PreloadSoundEventArgs", + "T:LibreMetaverse.SoundTriggerEventArgs": "crate::sound_manager::SoundTriggerEventArgs", + "T:LibreMetaverse.Parcel": "crate::parcel_manager::Parcel", + "T:LibreMetaverse.ParcelInfo": "crate::parcel_manager::ParcelInfo", + "T:LibreMetaverse.ParcelMedia": "crate::parcel_manager::ParcelMedia", + "T:LibreMetaverse.ParcelManager": "crate::parcel_manager::ParcelManager", + "T:LibreMetaverse.ParcelManager.ParcelAccessEntry": "crate::parcel_manager::ParcelManagerParcelAccessEntry", + "T:LibreMetaverse.ParcelManager.ParcelPrimOwners": "crate::parcel_manager::ParcelManagerParcelPrimOwners", + "T:LibreMetaverse.ForceSelectObjectsReplyEventArgs": "crate::parcel_manager::ForceSelectObjectsReplyEventArgs", + "T:LibreMetaverse.ParcelAccessListReplyEventArgs": "crate::parcel_manager::ParcelAccessListReplyEventArgs", + "T:LibreMetaverse.ParcelDwellReplyEventArgs": "crate::parcel_manager::ParcelDwellReplyEventArgs", + "T:LibreMetaverse.ParcelInfoReplyEventArgs": "crate::parcel_manager::ParcelInfoReplyEventArgs", + "T:LibreMetaverse.ParcelMediaCommandEventArgs": "crate::parcel_manager::ParcelMediaCommandEventArgs", + "T:LibreMetaverse.ParcelMediaUpdateReplyEventArgs": "crate::parcel_manager::ParcelMediaUpdateReplyEventArgs", + "T:LibreMetaverse.ParcelObjectOwnersReplyEventArgs": "crate::parcel_manager::ParcelObjectOwnersReplyEventArgs", + "T:LibreMetaverse.ParcelOverlayEventArgs": "crate::parcel_manager::ParcelOverlayEventArgs", + "T:LibreMetaverse.ParcelPropertiesEventArgs": "crate::parcel_manager::ParcelPropertiesEventArgs", + "T:LibreMetaverse.SimParcelsDownloadedEventArgs": "crate::parcel_manager::SimParcelsDownloadedEventArgs", + "T:LibreMetaverse.Messages.Linden.ObjectResourcesDetail": "crate::parcel_manager::ObjectResourcesDetail", + "T:LibreMetaverse.Messages.Linden.ParcelResourcesDetail": "crate::parcel_manager::ParcelResourcesDetail", + "T:LibreMetaverse.Messages.Linden.LandResourcesInfo": "crate::parcel_manager::LandResourcesInfo", "T:LibreMetaverse.ProductInfoEventArgs": "crate::agent_messages::ProductInfoEventArgs", "T:LibreMetaverse.RegionCrossedEventArgs": "crate::agent_movement::RegionCrossedEventArgs", "T:LibreMetaverse.RegionCrossingPredictionEventArgs": "crate::agent_movement::RegionCrossingPredictionEventArgs", @@ -177,6 +207,13 @@ NATIVE_TYPES = { "T:LibreMetaverse.Caps.EventQueueCallback": "crate::network_manager::CapsEventQueueCallback", "T:LibreMetaverse.CapsEventDictionary": "crate::network_manager::CapsEventDictionary", "T:LibreMetaverse.DisconnectedEventArgs": "crate::network_manager::DisconnectedEventArgs", + "T:LibreMetaverse.EnvironmentManager": "crate::environment_manager::EnvironmentManager", + "T:LibreMetaverse.LegacyEnvironmentEventArgs": "crate::environment_manager::LegacyEnvironmentEventArgs", + "T:LibreMetaverse.ParcelEnvironmentEventArgs": "crate::environment_manager::ParcelEnvironmentEventArgs", + "T:LibreMetaverse.RegionEnvironmentEventArgs": "crate::environment_manager::RegionEnvironmentEventArgs", + "T:LibreMetaverse.Messages.Linden.EnvironmentData": "crate::environment_manager::EnvironmentData", + "T:LibreMetaverse.Messages.Linden.ExtEnvironmentMessage": "crate::environment_manager::ExtEnvironmentMessage", + "T:LibreMetaverse.Messages.Linden.LegacyEnvironmentMessage": "crate::environment_manager::LegacyEnvironmentMessage", "T:LibreMetaverse.EventQueueRunningEventArgs": "crate::network_manager::EventQueueRunningEventArgs", "T:LibreMetaverse.GenericStreamingMessageEventArgs": "crate::network_manager::GenericStreamingMessageEventArgs", "T:LibreMetaverse.NetworkManager.IncomingPacket": "crate::network_manager::NetworkManagerIncomingPacket", @@ -358,6 +395,19 @@ NATIVE_DECLARATIONS = { } NATIVE_MEMBER_BODIES = { + "P:LibreMetaverse.GridClient.Environment": + "self.native_environment().unwrap_or_else(|_| panic!(\"failed to construct EnvironmentManager\"))", + "P:LibreMetaverse.GridClient.Environment#set": "self.native_set_environment(value)", + "P:LibreMetaverse.GridClient.Terrain": "self.native_terrain().unwrap_or_else(|_| panic!(\"failed to construct TerrainManager\"))", + "P:LibreMetaverse.GridClient.Terrain#set": "self.native_set_terrain(value)", + "P:LibreMetaverse.GridClient.Sound": "self.native_sound().unwrap_or_else(|_| panic!(\"failed to construct SoundManager\"))", + "P:LibreMetaverse.GridClient.Sound#set": "self.native_set_sound(value)", + "P:LibreMetaverse.GridClient.Parcels": "self.native_parcels().unwrap_or_else(|_| panic!(\"failed to construct ParcelManager\"))", + "P:LibreMetaverse.GridClient.Parcels#set": "self.native_set_parcels(value)", + "M:LibreMetaverse.Simulator.IsParcelMapFull": "Ok(self.native_is_parcel_map_full())", + "M:LibreMetaverse.Simulator.TerrainHeightAtPoint(System.Int32,System.Int32,System.Single@)": "self.native_terrain_height_at_point(x, y, height)", + "P:LibreMetaverse.Simulator.ParcelMap": "self.native_parcel_map_snapshot()", + "P:LibreMetaverse.Simulator.Parcels": "std::sync::RwLock::new(self.native_parcels_snapshot())", "E:LibreMetaverse.ObjectManager.AvatarSitChanged": "self.native_subscribe_avatar_sit_changed(handler)", "E:LibreMetaverse.ObjectManager.AvatarUpdate":