From 3951bdb809c259179583d4ee34488c30f4c64dc3 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Sun, 9 Aug 2026 18:18:28 +0000 Subject: [PATCH] Implement native login and session initialization (#56) --- Cargo.lock | 300 ++- README.md | 9 + api/SHIM-COVERAGE.md | 2 +- crates/libremetaverse/Cargo.toml | 2 + crates/libremetaverse/src/client_core.rs | 27 + crates/libremetaverse/src/generated.rs | 1649 ++++------------ crates/libremetaverse/src/lib.rs | 1 + crates/libremetaverse/src/login.rs | 1687 +++++++++++++++++ crates/libremetaverse/src/network_manager.rs | 758 +++++++- .../libremetaverse/tests/network_manager.rs | 444 ++++- docs/network-manager.md | 28 +- tests/api-compile/Cargo.lock | 300 ++- tests/compat/tests/core_runtime_shims.rs | 13 +- tools/generate_api_shims.py | 126 ++ tools/generate_rust_mapping.py | 1 + 15 files changed, 3992 insertions(+), 1355 deletions(-) create mode 100644 crates/libremetaverse/src/login.rs diff --git a/Cargo.lock b/Cargo.lock index 532fc8f..0ca4986 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,12 +17,27 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + [[package]] name = "aws-lc-rs" version = "1.18.0" @@ -102,6 +117,15 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + [[package]] name = "bumpalo" version = "3.20.3" @@ -141,6 +165,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "cfg_aliases" version = "0.2.2" @@ -277,6 +307,16 @@ dependencies = [ "crypto-common 0.2.2", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.7" @@ -724,7 +764,7 @@ dependencies = [ "jni-sys", "log", "simd_cesu8", - "thiserror", + "thiserror 2.0.20", "walkdir", "windows-link", ] @@ -808,6 +848,8 @@ dependencies = [ "libremetaverse-imaging", "libremetaverse-structured-data", "libremetaverse-types", + "mac_address2", + "os_info", "reqwest", "roxmltree", "tokio", @@ -997,6 +1039,18 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "mac_address2" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc5e04aa39a4dc96076bca67bf60b63201ff06f9568e62f7904293d39de8cb0b" +dependencies = [ + "nix 0.28.0", + "thiserror 1.0.69", + "widestring", + "windows-sys 0.52.0", +] + [[package]] name = "md-5" version = "0.10.6" @@ -1013,6 +1067,15 @@ version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1040,6 +1103,31 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases 0.1.1", + "libc", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases 0.2.2", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -1050,6 +1138,165 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -1062,6 +1309,21 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "os_info" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" +dependencies = [ + "android_system_properties", + "log", + "nix 0.31.3", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "windows-sys 0.61.2", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1115,14 +1377,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", - "cfg_aliases", + "cfg_aliases 0.2.2", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash", "rustls", "socket2", - "thiserror", + "thiserror 2.0.20", "tokio", "tracing", "web-time", @@ -1145,7 +1407,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.20", "tinyvec", "tracing", "web-time", @@ -1157,7 +1419,7 @@ version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ - "cfg_aliases", + "cfg_aliases 0.2.2", "libc", "once_cell", "socket2", @@ -1694,13 +1956,33 @@ dependencies = [ "xattr", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.20", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] @@ -2078,6 +2360,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + [[package]] name = "winapi-util" version = "0.1.11" diff --git a/README.md b/README.md index 0594cb5..f6314c5 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,15 @@ retry handling while preserving permanent 401/403/404/410 failures. Capability URLs and tokens are absent from errors and diagnostics. Ownership, limits, injection, and offline fake-server coverage are documented in [`docs/caps-http.md`](docs/caps-http.md). +Login is implemented against that transport with the C# password-hashing and +token rules, viewer/channel/platform fields, start-location normalization, +bounded redirects, cancellation, and typed status transitions. Native response +parsing populates session, simulator, inventory, buddy, account-benefit, and +service fields before the initial UDP circuit and seed capability are installed. +Credentials and session/capability tokens are redacted from diagnostics, and +offline fake login/simulator tests cover success, rejection, redirect, timeout, +and cancellation cleanup. The login contract is documented with the network +lifecycle in [`docs/network-manager.md`](docs/network-manager.md). Seed-cap discovery and `EventQueueGet` are native Rust too: the full reference capability list is posted as LLSD/XML, accepted URIs are rate-categorized, and bounded long polls preserve ack IDs, reconnect retries, shutdown `done`, and diff --git a/api/SHIM-COVERAGE.md b/api/SHIM-COVERAGE.md index e433723..e4858e3 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: 68 types / 13,858 members; remaining surface is callable failure-only shims | +| `LibreMetaverse` | 2,711 | 27,281 | native implementation: 77 types / 14,065 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/Cargo.toml b/crates/libremetaverse/Cargo.toml index e5c9723..457aa34 100644 --- a/crates/libremetaverse/Cargo.toml +++ b/crates/libremetaverse/Cargo.toml @@ -19,6 +19,8 @@ futures-util = "0.3.31" libremetaverse-imaging = { path = "../libremetaverse-imaging" } libremetaverse-structured-data = { path = "../libremetaverse-structured-data" } libremetaverse-types = { path = "../libremetaverse-types" } +mac_address2 = "2.0.2" +os_info = { version = "3.15.0", default-features = false } reqwest = { version = "0.13.4", default-features = false, features = ["rustls", "stream"] } roxmltree = "0.21.1" tokio = { version = "1.47.1", features = ["macros", "net", "rt", "sync", "time"] } diff --git a/crates/libremetaverse/src/client_core.rs b/crates/libremetaverse/src/client_core.rs index 6d539fd..d2157ba 100644 --- a/crates/libremetaverse/src/client_core.rs +++ b/crates/libremetaverse/src/client_core.rs @@ -116,6 +116,7 @@ struct ClientRuntime { agent_throttle_sender: Mutex>>, http_caps_client: Mutex, caps_rate_limiter: Mutex, + network_manager: Mutex>, shutdown_complete: Condvar, shutdown_wait: Mutex<()>, } @@ -142,6 +143,7 @@ impl ClientRuntime { agent_throttle_sender: Mutex::new(None), http_caps_client: Mutex::new(http_caps_client), caps_rate_limiter: Mutex::new(caps_rate_limiter), + network_manager: Mutex::new(std::sync::Weak::new()), shutdown_complete: Condvar::new(), shutdown_wait: Mutex::new(()), } @@ -350,6 +352,31 @@ impl GridClient { .clone() } + pub(crate) fn native_network(&self) -> Result { + let mut cached = self + .runtime + .network_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(inner) = cached.upgrade() { + return Ok(crate::network_manager::NetworkManager::native_from_inner( + inner, + )); + } + let manager = crate::network_manager::NetworkManager::native_new(self.clone())?; + *cached = manager.native_inner_weak(); + Ok(manager) + } + + #[allow(clippy::needless_pass_by_value)] // The mapped C# property setter owns its value. + pub(crate) fn native_set_network(&self, value: crate::NetworkManager) { + *self + .runtime + .network_manager + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = value.native_inner_weak(); + } + pub(crate) fn native_set_caps_rate_limiter( &mut self, value: crate::caps_http::CapsRateLimiter, diff --git a/crates/libremetaverse/src/generated.rs b/crates/libremetaverse/src/generated.rs index b4160ab..b1ed24f 100644 --- a/crates/libremetaverse/src/generated.rs +++ b/crates/libremetaverse/src/generated.rs @@ -85,277 +85,53 @@ impl AccessList { } /// C# type: `T:LibreMetaverse.AccountLevelBenefits`. -pub struct AccountLevelBenefits; -impl AccountLevelBenefits { - /// C# member: `M:LibreMetaverse.AccountLevelBenefits.#ctor(LibreMetaverse.StructuredData.OSDMap)`. - pub fn new_with_osd_map( - reply: libremetaverse_structured_data::OSDMap, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.AccountLevelBenefits.#ctor(LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.AccountLevelBenefits.#ctor(System.Collections.Hashtable)`. - pub fn new_with_hashtable( - reply: libremetaverse_types::compat::Hashtable, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.AccountLevelBenefits.#ctor(System.Collections.Hashtable)", - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.AnimatedObjectLimit`. - pub fn animated_object_limit(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.AnimatedObjectLimit" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.AnimationUploadCost`. - pub fn animation_upload_cost(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.AnimationUploadCost" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.AttachmentLimit`. - pub fn attachment_limit(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.AttachmentLimit" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.BetaGridLand`. - pub fn beta_grid_land(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.BetaGridLand" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.CreateGroupCost`. - pub fn create_group_cost(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.CreateGroupCost" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.CreateRepeatingEvents`. - pub fn create_repeating_events(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.CreateRepeatingEvents" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.EstateAccessToken`. - pub fn estate_access_token(&self) -> String { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.EstateAccessToken" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.GridwideExperienceLimit`. - pub fn gridwide_experience_limit(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.GridwideExperienceLimit" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.GroupMembershipLimit`. - pub fn group_membership_limit(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.GroupMembershipLimit" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.LandAuctionsAllowed`. - pub fn land_auctions_allowed(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.LandAuctionsAllowed" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.LargeTextureUploadCost`. - pub fn large_texture_upload_cost(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.LargeTextureUploadCost" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.LastnameChangeAllowed`. - pub fn lastname_change_allowed(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.LastnameChangeAllowed" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.LastnameChangeCost`. - pub fn lastname_change_cost(&self) -> f64 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.LastnameChangeCost" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.LastnameChangeRate`. - pub fn lastname_change_rate(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.LastnameChangeRate" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.LindenBuyFee`. - pub fn linden_buy_fee(&self) -> f64 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.LindenBuyFee" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.LiveChat`. - pub fn live_chat(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AccountLevelBenefits.LiveChat") - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.LocalExperiences`. - pub fn local_experiences(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.LocalExperiences" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.MainlandTier`. - pub fn mainland_tier(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.MainlandTier" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.MarketplaceConciergeSupport`. - pub fn marketplace_concierge_support(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.MarketplaceConciergeSupport" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.MarketplaceListingLimit`. - pub fn marketplace_listing_limit(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.MarketplaceListingLimit" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.MarketplacePleLimit`. - pub fn marketplace_ple_limit(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.MarketplacePleLimit" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.MeshUploadCost`. - pub fn mesh_upload_cost(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.MeshUploadCost" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.ObjectAccountLevel`. - pub fn object_account_level(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.ObjectAccountLevel" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.OneTimeEventAllowed`. - pub fn one_time_event_allowed(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.OneTimeEventAllowed" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.OneTimeEventCost`. - pub fn one_time_event_cost(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.OneTimeEventCost" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.PartnerFee`. - pub fn partner_fee(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AccountLevelBenefits.PartnerFee") - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.PhoneSupport`. - pub fn phone_support(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.PhoneSupport" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.PicksLimit`. - pub fn picks_limit(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AccountLevelBenefits.PicksLimit") - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.PlacePages`. - pub fn place_pages(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AccountLevelBenefits.PlacePages") - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.PremiumAccess`. - pub fn premium_access(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.PremiumAccess" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.PremiumAlts`. - pub fn premium_alts(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.PremiumAlts" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.PremiumGifts`. - pub fn premium_gifts(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.PremiumGifts" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.PriorityEntry`. - pub fn priority_entry(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.PriorityEntry" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.RepeatingEventsCost`. - pub fn repeating_events_cost(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.RepeatingEventsCost" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.ScriptLimit`. - pub fn script_limit(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.ScriptLimit" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.SignupBonus`. - pub fn signup_bonus(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.SignupBonus" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.SoundUploadCost`. - pub fn sound_upload_cost(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.SoundUploadCost" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.Stipend`. - pub fn stipend(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AccountLevelBenefits.Stipend") - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.StoredImLimit`. - pub fn stored_im_limit(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.StoredImLimit" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.TextureUploadCost`. - pub fn texture_upload_cost(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.TextureUploadCost" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.TransactionHistoryLimit`. - pub fn transaction_history_limit(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.TransactionHistoryLimit" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.UnpartnerFee`. - pub fn unpartner_fee(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.UnpartnerFee" - ) - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.UseAnimesh`. - pub fn use_animesh(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AccountLevelBenefits.UseAnimesh") - } - /// C# member: `P:LibreMetaverse.AccountLevelBenefits.VoiceMorphing`. - pub fn voice_morphing(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.AccountLevelBenefits.VoiceMorphing" - ) - } -} +/// C# member: `M:LibreMetaverse.AccountLevelBenefits.#ctor(LibreMetaverse.StructuredData.OSDMap)`. +/// C# member: `M:LibreMetaverse.AccountLevelBenefits.#ctor(System.Collections.Hashtable)`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.AnimatedObjectLimit`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.AnimationUploadCost`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.AttachmentLimit`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.BetaGridLand`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.CreateGroupCost`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.CreateRepeatingEvents`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.EstateAccessToken`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.GridwideExperienceLimit`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.GroupMembershipLimit`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.LandAuctionsAllowed`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.LargeTextureUploadCost`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.LastnameChangeAllowed`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.LastnameChangeCost`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.LastnameChangeRate`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.LindenBuyFee`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.LiveChat`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.LocalExperiences`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.MainlandTier`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.MarketplaceConciergeSupport`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.MarketplaceListingLimit`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.MarketplacePleLimit`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.MeshUploadCost`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.ObjectAccountLevel`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.OneTimeEventAllowed`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.OneTimeEventCost`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.PartnerFee`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.PhoneSupport`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.PicksLimit`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.PlacePages`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.PremiumAccess`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.PremiumAlts`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.PremiumGifts`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.PriorityEntry`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.RepeatingEventsCost`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.ScriptLimit`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.SignupBonus`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.SoundUploadCost`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.Stipend`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.StoredImLimit`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.TextureUploadCost`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.TransactionHistoryLimit`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.UnpartnerFee`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.UseAnimesh`. +/// C# member: `P:LibreMetaverse.AccountLevelBenefits.VoiceMorphing`. +pub use crate::login::AccountLevelBenefits; /// C# type: `T:LibreMetaverse.AgentAccessEventArgs`. pub struct AgentAccessEventArgs; @@ -6588,6 +6364,7 @@ pub enum BorderCrossingDirection { } /// C# type: `T:LibreMetaverse.BuddyListEntry`. +#[derive(Clone, Debug, Eq, PartialEq)] pub struct BuddyListEntry { /// C# member: `F:LibreMetaverse.BuddyListEntry.BuddyId`. pub buddy_id: String, @@ -10679,11 +10456,14 @@ impl GridClient { } /// C# member: `P:LibreMetaverse.GridClient.Network`. pub fn network(&self) -> libremetaverse::NetworkManager { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Network") + /* native client-core implementation */ + self.native_network() + .unwrap_or_else(|_| panic!("failed to construct NetworkManager")) } /// Setter for C# member: `P:LibreMetaverse.GridClient.Network`. pub fn set_network(&mut self, value: libremetaverse::NetworkManager) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Network") + /* native client-core implementation */ + self.native_set_network(value) } /// C# member: `P:LibreMetaverse.GridClient.Objects`. pub fn objects(&self) -> libremetaverse::ObjectManager { @@ -12566,6 +12346,7 @@ impl Helpers { } /// C# type: `T:LibreMetaverse.HomeInfo`. +#[derive(Clone, Copy, Debug, PartialEq)] pub struct HomeInfo { /// C# member: `F:LibreMetaverse.HomeInfo.LookAt`. pub look_at: libremetaverse_types::Vector3, @@ -13847,7 +13628,13 @@ impl InventoryAttachment { } /// C# type: `T:LibreMetaverse.InventoryBase`. -pub struct InventoryBase; +#[derive(Clone, Debug)] +pub struct InventoryBase { + native_uuid: libremetaverse_types::UUID, + native_owner_id: libremetaverse_types::UUID, + native_parent_uuid: libremetaverse_types::UUID, + native_name: String, +} impl InventoryBase { /// C# member: `M:LibreMetaverse.InventoryBase.Equals(LibreMetaverse.InventoryBase)`. pub fn equals_with_inventory_base(&self, o: libremetaverse::InventoryBase) -> bool { @@ -13871,35 +13658,43 @@ impl InventoryBase { } /// C# member: `P:LibreMetaverse.InventoryBase.Name`. pub fn name(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryBase.Name") + /* native network implementation */ + self.native_name.clone() } /// Setter for C# member: `P:LibreMetaverse.InventoryBase.Name`. pub fn set_name(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryBase.Name") + /* native network implementation */ + self.native_name = value } /// C# member: `P:LibreMetaverse.InventoryBase.OwnerID`. pub fn owner_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryBase.OwnerID") + /* native network implementation */ + self.native_owner_id } /// Setter for C# member: `P:LibreMetaverse.InventoryBase.OwnerID`. pub fn set_owner_id(&mut self, value: libremetaverse_types::UUID) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryBase.OwnerID") + /* native network implementation */ + self.native_owner_id = value } /// C# member: `P:LibreMetaverse.InventoryBase.ParentUUID`. pub fn parent_uuid(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryBase.ParentUUID") + /* native network implementation */ + self.native_parent_uuid } /// Setter for C# member: `P:LibreMetaverse.InventoryBase.ParentUUID`. pub fn set_parent_uuid(&mut self, value: libremetaverse_types::UUID) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryBase.ParentUUID") + /* native network implementation */ + self.native_parent_uuid = value } /// C# member: `P:LibreMetaverse.InventoryBase.UUID`. pub fn uuid(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryBase.UUID") + /* native network implementation */ + self.native_uuid } /// Setter for C# member: `P:LibreMetaverse.InventoryBase.UUID`. pub fn set_uuid(&mut self, value: libremetaverse_types::UUID) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryBase.UUID") + /* native network implementation */ + self.native_uuid = value } } @@ -13956,18 +13751,31 @@ impl InventoryException { } /// C# type: `T:LibreMetaverse.InventoryFolder`. +#[derive(Clone, Debug)] pub struct InventoryFolder { /// Rust composition for C# base type `LibreMetaverse.InventoryBase`. pub base: libremetaverse::InventoryBase, + native_descendent_count: i32, + native_preferred_type: libremetaverse_types::FolderType, + native_version: i32, } impl InventoryFolder { /// C# member: `F:LibreMetaverse.InventoryFolder.VERSION_UNKNOWN`. pub const VERSION_UNKNOWN: i32 = -1; /// C# member: `M:LibreMetaverse.InventoryFolder.#ctor(LibreMetaverse.UUID)`. pub fn new(uuid: libremetaverse_types::UUID) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.InventoryFolder.#ctor(LibreMetaverse.UUID)", - ) + /* native network implementation */ + Ok(Self { + base: crate::InventoryBase { + native_uuid: uuid, + native_owner_id: libremetaverse_types::UUID::zero(), + native_parent_uuid: libremetaverse_types::UUID::zero(), + native_name: String::new(), + }, + native_descendent_count: 0, + native_preferred_type: libremetaverse_types::FolderType::None, + native_version: Self::VERSION_UNKNOWN, + }) } /// C# member: `M:LibreMetaverse.InventoryFolder.Equals(LibreMetaverse.InventoryBase)`. pub fn equals_with_inventory_base(&self, o: libremetaverse::InventoryBase) -> bool { @@ -14015,27 +13823,33 @@ impl InventoryFolder { } /// C# member: `P:LibreMetaverse.InventoryFolder.DescendentCount`. pub fn descendent_count(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryFolder.DescendentCount") + /* native network implementation */ + self.native_descendent_count } /// Setter for C# member: `P:LibreMetaverse.InventoryFolder.DescendentCount`. pub fn set_descendent_count(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryFolder.DescendentCount") + /* native network implementation */ + self.native_descendent_count = value } /// C# member: `P:LibreMetaverse.InventoryFolder.PreferredType`. pub fn preferred_type(&self) -> libremetaverse_types::FolderType { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryFolder.PreferredType") + /* native network implementation */ + self.native_preferred_type } /// Setter for C# member: `P:LibreMetaverse.InventoryFolder.PreferredType`. pub fn set_preferred_type(&mut self, value: libremetaverse_types::FolderType) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryFolder.PreferredType") + /* native network implementation */ + self.native_preferred_type = value } /// C# member: `P:LibreMetaverse.InventoryFolder.Version`. pub fn version(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryFolder.Version") + /* native network implementation */ + self.native_version } /// Setter for C# member: `P:LibreMetaverse.InventoryFolder.Version`. pub fn set_version(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.InventoryFolder.Version") + /* native network implementation */ + self.native_version = value } } @@ -16832,871 +16646,136 @@ impl LoggingSettings { } /// C# type: `T:LibreMetaverse.LoginCredential`. -pub struct LoginCredential; -impl LoginCredential { - /// C# member: `M:LibreMetaverse.LoginCredential.#ctor(System.String,System.String,System.String)`. - pub fn new_with_string_string_string( - first: String, - last: String, - passwd: String, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginCredential.#ctor(System.String,System.String,System.String)", - ) - } - /// C# member: `M:LibreMetaverse.LoginCredential.#ctor(System.String,System.String,System.String,System.String,System.String)`. - pub fn new_with_string_string_string_string_string( - first: String, - last: String, - passwd: String, - token: String, - mfa_hash: String, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginCredential.#ctor(System.String,System.String,System.String,System.String,System.String)", - ) - } - /// C# member: `P:LibreMetaverse.LoginCredential.FirstName`. - pub fn first_name(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginCredential.FirstName") - } - /// C# member: `P:LibreMetaverse.LoginCredential.LastName`. - pub fn last_name(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginCredential.LastName") - } - /// C# member: `P:LibreMetaverse.LoginCredential.MfaHash`. - pub fn mfa_hash(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginCredential.MfaHash") - } - /// C# member: `P:LibreMetaverse.LoginCredential.Password`. - pub fn password(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginCredential.Password") - } - /// C# member: `P:LibreMetaverse.LoginCredential.Token`. - pub fn token(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginCredential.Token") - } -} +/// C# member: `M:LibreMetaverse.LoginCredential.#ctor(System.String,System.String,System.String)`. +/// C# member: `M:LibreMetaverse.LoginCredential.#ctor(System.String,System.String,System.String,System.String,System.String)`. +/// C# member: `P:LibreMetaverse.LoginCredential.FirstName`. +/// C# member: `P:LibreMetaverse.LoginCredential.LastName`. +/// C# member: `P:LibreMetaverse.LoginCredential.MfaHash`. +/// C# member: `P:LibreMetaverse.LoginCredential.Password`. +/// C# member: `P:LibreMetaverse.LoginCredential.Token`. +pub use crate::login::LoginCredential; /// C# type: `T:LibreMetaverse.LoginException`. -pub struct LoginException; -impl LoginException { - /// C# member: `M:LibreMetaverse.LoginException.#ctor(System.String)`. - pub fn new_with_string(message: String) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginException.#ctor(System.String)", - ) - } - /// C# member: `M:LibreMetaverse.LoginException.#ctor(System.String,System.Exception)`. - pub fn new_with_string_exception( - message: String, - inner_exception: libremetaverse_types::compat::ExternalError, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginException.#ctor(System.String,System.Exception)", - ) - } -} +/// C# member: `M:LibreMetaverse.LoginException.#ctor(System.String)`. +/// C# member: `M:LibreMetaverse.LoginException.#ctor(System.String,System.Exception)`. +pub use crate::login::LoginException; /// C# type: `T:LibreMetaverse.LoginParams`. -pub struct LoginParams { - /// C# member: `F:LibreMetaverse.LoginParams.AgreeToTos`. - pub agree_to_tos: bool, - /// C# member: `F:LibreMetaverse.LoginParams.Author`. - pub author: String, - /// C# member: `F:LibreMetaverse.LoginParams.Channel`. - pub channel: String, - /// C# member: `F:LibreMetaverse.LoginParams.FirstName`. - pub first_name: String, - /// C# member: `F:LibreMetaverse.LoginParams.ID0`. - pub id0: String, - /// C# member: `F:LibreMetaverse.LoginParams.LastExecEvent`. - pub last_exec_event: libremetaverse::LastExecStatus, - /// C# member: `F:LibreMetaverse.LoginParams.LastName`. - pub last_name: String, - /// C# member: `F:LibreMetaverse.LoginParams.LoginLocation`. - pub login_location: String, - /// C# member: `F:LibreMetaverse.LoginParams.MAC`. - pub mac: String, - /// C# member: `F:LibreMetaverse.LoginParams.MethodName`. - pub method_name: String, - /// C# member: `F:LibreMetaverse.LoginParams.MfaEnabled`. - pub mfa_enabled: bool, - /// C# member: `F:LibreMetaverse.LoginParams.MfaHash`. - pub mfa_hash: String, - /// C# member: `F:LibreMetaverse.LoginParams.Options`. - pub options: Vec, - /// C# member: `F:LibreMetaverse.LoginParams.Password`. - pub password: String, - /// C# member: `F:LibreMetaverse.LoginParams.Platform`. - pub platform: String, - /// C# member: `F:LibreMetaverse.LoginParams.PlatformVersion`. - pub platform_version: String, - /// C# member: `F:LibreMetaverse.LoginParams.ReadCritical`. - pub read_critical: bool, - /// C# member: `F:LibreMetaverse.LoginParams.Start`. - pub start: String, - /// C# member: `F:LibreMetaverse.LoginParams.Timeout`. - pub timeout: i32, - /// C# member: `F:LibreMetaverse.LoginParams.Token`. - pub token: String, - /// C# member: `F:LibreMetaverse.LoginParams.URI`. - pub uri: String, - /// C# member: `F:LibreMetaverse.LoginParams.UserAgent`. - pub user_agent: String, - /// C# member: `F:LibreMetaverse.LoginParams.Version`. - pub version: String, - /// C# member: `F:LibreMetaverse.LoginParams.ViewerDigest`. - pub viewer_digest: String, -} -impl LoginParams { - /// C# member: `M:LibreMetaverse.LoginParams.#ctor`. - pub fn new_with_constructor() -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.LoginParams.#ctor") - } - /// C# member: `M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,LibreMetaverse.LoginCredential,System.String,System.String)`. - pub fn new_with_grid_client_login_credential_string_string( - client: libremetaverse::GridClient, - credential: libremetaverse::LoginCredential, - channel: String, - version: String, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,LibreMetaverse.LoginCredential,System.String,System.String)", - ) - } - /// C# member: `M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,LibreMetaverse.LoginCredential,System.String,System.String,System.String)`. - pub fn new_with_grid_client_login_credential_string_string_string( - client: libremetaverse::GridClient, - credential: libremetaverse::LoginCredential, - channel: String, - version: String, - login_uri: String, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,LibreMetaverse.LoginCredential,System.String,System.String,System.String)", - ) - } - /// C# member: `M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,System.String,System.String,System.String,System.String,System.String)`. - pub fn new_with_grid_client_string_string_string_string_string( - client: libremetaverse::GridClient, - first_name: String, - last_name: String, - password: String, - channel: String, - version: String, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,System.String,System.String,System.String,System.String,System.String)", - ) - } - /// C# member: `M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,System.String,System.String,System.String,System.String,System.String,System.String)`. - pub fn new_with_grid_client_string_string_string_string_string_string( - client: libremetaverse::GridClient, - first_name: String, - last_name: String, - password: String, - channel: String, - version: String, - login_uri: String, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,System.String,System.String,System.String,System.String,System.String,System.String)", - ) - } -} +/// C# member: `F:LibreMetaverse.LoginParams.AgreeToTos`. +/// C# member: `F:LibreMetaverse.LoginParams.Author`. +/// C# member: `F:LibreMetaverse.LoginParams.Channel`. +/// C# member: `F:LibreMetaverse.LoginParams.FirstName`. +/// C# member: `F:LibreMetaverse.LoginParams.ID0`. +/// C# member: `F:LibreMetaverse.LoginParams.LastExecEvent`. +/// C# member: `F:LibreMetaverse.LoginParams.LastName`. +/// C# member: `F:LibreMetaverse.LoginParams.LoginLocation`. +/// C# member: `F:LibreMetaverse.LoginParams.MAC`. +/// C# member: `F:LibreMetaverse.LoginParams.MethodName`. +/// C# member: `F:LibreMetaverse.LoginParams.MfaEnabled`. +/// C# member: `F:LibreMetaverse.LoginParams.MfaHash`. +/// C# member: `F:LibreMetaverse.LoginParams.Options`. +/// C# member: `F:LibreMetaverse.LoginParams.Password`. +/// C# member: `F:LibreMetaverse.LoginParams.Platform`. +/// C# member: `F:LibreMetaverse.LoginParams.PlatformVersion`. +/// C# member: `F:LibreMetaverse.LoginParams.ReadCritical`. +/// C# member: `F:LibreMetaverse.LoginParams.Start`. +/// C# member: `F:LibreMetaverse.LoginParams.Timeout`. +/// C# member: `F:LibreMetaverse.LoginParams.Token`. +/// C# member: `F:LibreMetaverse.LoginParams.URI`. +/// C# member: `F:LibreMetaverse.LoginParams.UserAgent`. +/// C# member: `F:LibreMetaverse.LoginParams.Version`. +/// C# member: `F:LibreMetaverse.LoginParams.ViewerDigest`. +/// C# member: `M:LibreMetaverse.LoginParams.#ctor`. +/// C# member: `M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,LibreMetaverse.LoginCredential,System.String,System.String)`. +/// C# member: `M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,LibreMetaverse.LoginCredential,System.String,System.String,System.String)`. +/// C# member: `M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,System.String,System.String,System.String,System.String,System.String)`. +/// C# member: `M:LibreMetaverse.LoginParams.#ctor(LibreMetaverse.GridClient,System.String,System.String,System.String,System.String,System.String,System.String)`. +pub use crate::login::LoginParams; /// C# type: `T:LibreMetaverse.LoginProgressEventArgs`. -pub struct LoginProgressEventArgs; -impl LoginProgressEventArgs { - /// C# member: `M:LibreMetaverse.LoginProgressEventArgs.#ctor(LibreMetaverse.LoginStatus,System.String,System.String)`. - pub fn new( - login: libremetaverse::LoginStatus, - message: String, - fail_reason: String, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginProgressEventArgs.#ctor(LibreMetaverse.LoginStatus,System.String,System.String)", - ) - } - /// C# member: `P:LibreMetaverse.LoginProgressEventArgs.FailReason`. - pub fn fail_reason(&self) -> String { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginProgressEventArgs.FailReason" - ) - } - /// C# member: `P:LibreMetaverse.LoginProgressEventArgs.Message`. - pub fn message(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginProgressEventArgs.Message") - } - /// C# member: `P:LibreMetaverse.LoginProgressEventArgs.Status`. - pub fn status(&self) -> libremetaverse::LoginStatus { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginProgressEventArgs.Status") - } -} +/// C# member: `M:LibreMetaverse.LoginProgressEventArgs.#ctor(LibreMetaverse.LoginStatus,System.String,System.String)`. +/// C# member: `P:LibreMetaverse.LoginProgressEventArgs.FailReason`. +/// C# member: `P:LibreMetaverse.LoginProgressEventArgs.Message`. +/// C# member: `P:LibreMetaverse.LoginProgressEventArgs.Status`. +pub use crate::login::LoginProgressEventArgs; /// C# type: `T:LibreMetaverse.LoginResponseData`. -pub struct LoginResponseData; -impl LoginResponseData { - /// C# member: `M:LibreMetaverse.LoginResponseData.#ctor`. - pub fn new() -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.LoginResponseData.#ctor") - } - /// C# member: `M:LibreMetaverse.LoginResponseData.Parse(LibreMetaverse.StructuredData.OSDMap)`. - pub fn parse_with_osd_map( - &self, - reply: libremetaverse_structured_data::OSDMap, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.Parse(LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.Parse(System.Collections.Hashtable)`. - pub fn parse_with_hashtable( - &self, - reply: libremetaverse_types::compat::Hashtable, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.Parse(System.Collections.Hashtable)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseInventoryFolders(System.String,LibreMetaverse.UUID,LibreMetaverse.StructuredData.OSDMap)`. - pub fn parse_inventory_folders( - key: String, - owner: libremetaverse_types::UUID, - reply: libremetaverse_structured_data::OSDMap, - ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseInventoryFolders(System.String,LibreMetaverse.UUID,LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseInventorySkeleton(System.String,LibreMetaverse.StructuredData.OSDMap)`. - pub fn parse_inventory_skeleton_with_string_osd_map( - &self, - key: String, - reply: libremetaverse_structured_data::OSDMap, - ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseInventorySkeleton(System.String,LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseInventorySkeleton(System.String,System.Collections.Hashtable)`. - pub fn parse_inventory_skeleton_with_string_hashtable( - &self, - key: String, - reply: libremetaverse_types::compat::Hashtable, - ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseInventorySkeleton(System.String,System.Collections.Hashtable)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseMappedUUID(System.String,System.String,LibreMetaverse.StructuredData.OSDMap)`. - pub fn parse_mapped_uuid_with_string_string_osd_map( - key: String, - key2: String, - reply: libremetaverse_structured_data::OSDMap, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseMappedUUID(System.String,System.String,LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseMappedUUID(System.String,System.String,System.Collections.Hashtable)`. - pub fn parse_mapped_uuid_with_string_string_hashtable( - key: String, - key2: String, - reply: libremetaverse_types::compat::Hashtable, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseMappedUUID(System.String,System.String,System.Collections.Hashtable)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseString(System.String,LibreMetaverse.StructuredData.OSDMap)`. - pub fn parse_string_with_string_osd_map( - key: String, - reply: libremetaverse_structured_data::OSDMap, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseString(System.String,LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseString(System.String,System.Collections.Hashtable)`. - pub fn parse_string_with_string_hashtable( - key: String, - reply: libremetaverse_types::compat::Hashtable, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseString(System.String,System.Collections.Hashtable)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseUInt(System.String,LibreMetaverse.StructuredData.OSDMap)`. - pub fn parse_u_int_with_string_osd_map( - key: String, - reply: libremetaverse_structured_data::OSDMap, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseUInt(System.String,LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseUInt(System.String,System.Collections.Hashtable)`. - pub fn parse_u_int_with_string_hashtable( - key: String, - reply: libremetaverse_types::compat::Hashtable, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseUInt(System.String,System.Collections.Hashtable)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseUUID(System.String,LibreMetaverse.StructuredData.OSDMap)`. - pub fn parse_uuid_with_string_osd_map( - key: String, - reply: libremetaverse_structured_data::OSDMap, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseUUID(System.String,LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseUUID(System.String,System.Collections.Hashtable)`. - pub fn parse_uuid_with_string_hashtable( - key: String, - reply: libremetaverse_types::compat::Hashtable, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseUUID(System.String,System.Collections.Hashtable)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseVector3(System.String,LibreMetaverse.StructuredData.OSDMap)`. - pub fn parse_vector3_with_string_osd_map( - key: String, - reply: libremetaverse_structured_data::OSDMap, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseVector3(System.String,LibreMetaverse.StructuredData.OSDMap)", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseVector3(System.String,System.Collections.Hashtable)`. - pub fn parse_vector3_with_string_hashtable( - key: String, - reply: libremetaverse_types::compat::Hashtable, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseVector3(System.String,System.Collections.Hashtable)", - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.AccountLevelBenefits`. - pub fn account_level_benefits(&self) -> Option { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.AccountLevelBenefits" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.AccountLevelBenefits`. - pub fn set_account_level_benefits( - &mut self, - value: Option, - ) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.AccountLevelBenefits" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.AccountType`. - pub fn account_type(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.AccountType") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.AccountType`. - pub fn set_account_type(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.AccountType") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.AgentAccess`. - pub fn agent_access(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.AgentAccess") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.AgentAccess`. - pub fn set_agent_access(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.AgentAccess") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.AgentAccessMax`. - pub fn agent_access_max(&self) -> String { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.AgentAccessMax" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.AgentAccessMax`. - pub fn set_agent_access_max(&mut self, value: String) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.AgentAccessMax" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.AgentAppearanceServiceURL`. - pub fn agent_appearance_service_url(&self) -> String { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.AgentAppearanceServiceURL" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.AgentAppearanceServiceURL`. - pub fn set_agent_appearance_service_url(&mut self, value: String) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.AgentAppearanceServiceURL" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.AgentID`. - pub fn agent_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.AgentID") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.AgentID`. - pub fn set_agent_id(&mut self, value: libremetaverse_types::UUID) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.AgentID") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.AgentRegionAccess`. - pub fn agent_region_access(&self) -> String { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.AgentRegionAccess" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.AgentRegionAccess`. - pub fn set_agent_region_access(&mut self, value: String) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.AgentRegionAccess" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.BuddyList`. - pub fn buddy_list(&self) -> Vec { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.BuddyList") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.BuddyList`. - pub fn set_buddy_list(&mut self, value: Vec) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.BuddyList") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.COFVersion`. - pub fn cof_version(&self) -> u32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.COFVersion") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.COFVersion`. - pub fn set_cof_version(&mut self, value: u32) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.COFVersion") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.CircuitCode`. - pub fn circuit_code(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.CircuitCode") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.CircuitCode`. - pub fn set_circuit_code(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.CircuitCode") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.ClassifiedCategories`. - pub fn classified_categories(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.ClassifiedCategories" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.ClassifiedCategories`. - pub fn set_classified_categories(&mut self, value: Vec) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.ClassifiedCategories" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.EventCategories`. - pub fn event_categories(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.EventCategories" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.EventCategories`. - pub fn set_event_categories(&mut self, value: Vec) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.EventCategories" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.FirstLogin`. - pub fn first_login(&self) -> bool { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.FirstLogin") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.FirstLogin`. - pub fn set_first_login(&mut self, value: bool) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.FirstLogin") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.FirstName`. - pub fn first_name(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.FirstName") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.FirstName`. - pub fn set_first_name(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.FirstName") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.Gestures`. - pub fn gestures( - &self, - ) -> std::collections::HashMap { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.Gestures") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.Gestures`. - pub fn set_gestures( - &mut self, - value: std::collections::HashMap, - ) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.Gestures") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.GlobalTextures`. - pub fn global_textures(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.GlobalTextures" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.GlobalTextures`. - pub fn set_global_textures(&mut self, value: Vec) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.GlobalTextures" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.Home`. - pub fn home(&self) -> libremetaverse::HomeInfo { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.Home") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.InitialOutfit`. - pub fn initial_outfit(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.InitialOutfit") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.InitialOutfit`. - pub fn set_initial_outfit(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.InitialOutfit") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.InventoryRoot`. - pub fn inventory_root(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.InventoryRoot") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.InventoryRoot`. - pub fn set_inventory_root(&mut self, value: libremetaverse_types::UUID) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.InventoryRoot") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.InventorySkeleton`. - pub fn inventory_skeleton(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.InventorySkeleton" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.InventorySkeleton`. - pub fn set_inventory_skeleton(&mut self, value: Vec) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.InventorySkeleton" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.LastName`. - pub fn last_name(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.LastName") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.LastName`. - pub fn set_last_name(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.LastName") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.LibraryOwner`. - pub fn library_owner(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.LibraryOwner") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.LibraryOwner`. - pub fn set_library_owner(&mut self, value: libremetaverse_types::UUID) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.LibraryOwner") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.LibraryRoot`. - pub fn library_root(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.LibraryRoot") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.LibraryRoot`. - pub fn set_library_root(&mut self, value: libremetaverse_types::UUID) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.LibraryRoot") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.LibrarySkeleton`. - pub fn library_skeleton(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.LibrarySkeleton" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.LibrarySkeleton`. - pub fn set_library_skeleton(&mut self, value: Vec) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.LibrarySkeleton" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.Login`. - pub fn login(&self) -> libremetaverse::LoginState { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.Login") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.Login`. - pub fn set_login(&mut self, value: libremetaverse::LoginState) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.Login") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.LookAt`. - pub fn look_at(&self) -> libremetaverse_types::Vector3 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.LookAt") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.LookAt`. - pub fn set_look_at(&mut self, value: libremetaverse_types::Vector3) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.LookAt") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.MapServerUrl`. - pub fn map_server_url(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.MapServerUrl") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.MapServerUrl`. - pub fn set_map_server_url(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.MapServerUrl") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.MaxAgentGroups`. - pub fn max_agent_groups(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.MaxAgentGroups" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.MaxAgentGroups`. - pub fn set_max_agent_groups(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.MaxAgentGroups" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.Message`. - pub fn message(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.Message") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.Message`. - pub fn set_message(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.Message") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.MfaHash`. - pub fn mfa_hash(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.MfaHash") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.MfaHash`. - pub fn set_mfa_hash(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.MfaHash") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.NextDuration`. - pub fn next_duration(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.NextDuration") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.NextDuration`. - pub fn set_next_duration(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.NextDuration") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.NextMethod`. - pub fn next_method(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.NextMethod") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.NextMethod`. - pub fn set_next_method(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.NextMethod") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.NextOptions`. - pub fn next_options(&self) -> Vec { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.NextOptions") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.NextOptions`. - pub fn set_next_options(&mut self, value: Vec) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.NextOptions") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.NextUrl`. - pub fn next_url(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.NextUrl") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.NextUrl`. - pub fn set_next_url(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.NextUrl") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.OpenIDUrl`. - pub fn open_id_url(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.OpenIDUrl") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.OpenIDUrl`. - pub fn set_open_id_url(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.OpenIDUrl") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.PremiumPackages`. - pub fn premium_packages( - &self, - ) -> std::collections::HashMap> { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.PremiumPackages" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.PremiumPackages`. - pub fn set_premium_packages( - &mut self, - value: std::collections::HashMap>, - ) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.PremiumPackages" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.Reason`. - pub fn reason(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.Reason") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.Reason`. - pub fn set_reason(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.Reason") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.RegionSizeX`. - pub fn region_size_x(&self) -> u32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.RegionSizeX") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.RegionSizeX`. - pub fn set_region_size_x(&mut self, value: u32) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.RegionSizeX") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.RegionSizeY`. - pub fn region_size_y(&self) -> u32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.RegionSizeY") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.RegionSizeY`. - pub fn set_region_size_y(&mut self, value: u32) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.RegionSizeY") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.RegionX`. - pub fn region_x(&self) -> u32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.RegionX") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.RegionX`. - pub fn set_region_x(&mut self, value: u32) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.RegionX") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.RegionY`. - pub fn region_y(&self) -> u32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.RegionY") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.RegionY`. - pub fn set_region_y(&mut self, value: u32) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.RegionY") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.SecondsSinceEpoch`. - pub fn seconds_since_epoch(&self) -> i32 { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.SecondsSinceEpoch" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.SecondsSinceEpoch`. - pub fn set_seconds_since_epoch(&mut self, value: i32) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.SecondsSinceEpoch" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.SecureSessionID`. - pub fn secure_session_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.SecureSessionID" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.SecureSessionID`. - pub fn set_secure_session_id(&mut self, value: libremetaverse_types::UUID) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.SecureSessionID" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.SeedCapability`. - pub fn seed_capability(&self) -> String { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.SeedCapability" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.SeedCapability`. - pub fn set_seed_capability(&mut self, value: String) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.SeedCapability" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.SessionID`. - pub fn session_id(&self) -> libremetaverse_types::UUID { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.SessionID") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.SessionID`. - pub fn set_session_id(&mut self, value: libremetaverse_types::UUID) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.SessionID") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.SimIP`. - pub fn sim_ip(&self) -> Option { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.SimIP") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.SimIP`. - pub fn set_sim_ip(&mut self, value: Option) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.SimIP") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.SimPort`. - pub fn sim_port(&self) -> u16 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.SimPort") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.SimPort`. - pub fn set_sim_port(&mut self, value: u16) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.SimPort") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.SnapshotConfigUrl`. - pub fn snapshot_config_url(&self) -> String { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.SnapshotConfigUrl" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.SnapshotConfigUrl`. - pub fn set_snapshot_config_url(&mut self, value: String) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.SnapshotConfigUrl" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.StartLocation`. - pub fn start_location(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.StartLocation") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.StartLocation`. - pub fn set_start_location(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.StartLocation") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.Success`. - pub fn success(&self) -> bool { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.Success") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.UDPBlacklist`. - pub fn udp_blacklist(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.UDPBlacklist") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.UDPBlacklist`. - pub fn set_udp_blacklist(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.UDPBlacklist") - } - /// C# member: `P:LibreMetaverse.LoginResponseData.UiConfig`. - pub fn ui_config(&self) -> Vec { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.UiConfig") - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.UiConfig`. - pub fn set_ui_config(&mut self, value: Vec) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.LoginResponseData.UiConfig") - } -} +/// C# member: `M:LibreMetaverse.LoginResponseData.#ctor`. +/// C# member: `M:LibreMetaverse.LoginResponseData.Parse(LibreMetaverse.StructuredData.OSDMap)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.Parse(System.Collections.Hashtable)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseInventoryFolders(System.String,LibreMetaverse.UUID,LibreMetaverse.StructuredData.OSDMap)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseInventorySkeleton(System.String,LibreMetaverse.StructuredData.OSDMap)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseInventorySkeleton(System.String,System.Collections.Hashtable)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseMappedUUID(System.String,System.String,LibreMetaverse.StructuredData.OSDMap)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseMappedUUID(System.String,System.String,System.Collections.Hashtable)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseString(System.String,LibreMetaverse.StructuredData.OSDMap)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseString(System.String,System.Collections.Hashtable)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseUInt(System.String,LibreMetaverse.StructuredData.OSDMap)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseUInt(System.String,System.Collections.Hashtable)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseUUID(System.String,LibreMetaverse.StructuredData.OSDMap)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseUUID(System.String,System.Collections.Hashtable)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseVector3(System.String,LibreMetaverse.StructuredData.OSDMap)`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseVector3(System.String,System.Collections.Hashtable)`. +/// C# member: `P:LibreMetaverse.LoginResponseData.AccountLevelBenefits`. +/// C# member: `P:LibreMetaverse.LoginResponseData.AccountType`. +/// C# member: `P:LibreMetaverse.LoginResponseData.AgentAccess`. +/// C# member: `P:LibreMetaverse.LoginResponseData.AgentAccessMax`. +/// C# member: `P:LibreMetaverse.LoginResponseData.AgentAppearanceServiceURL`. +/// C# member: `P:LibreMetaverse.LoginResponseData.AgentID`. +/// C# member: `P:LibreMetaverse.LoginResponseData.AgentRegionAccess`. +/// C# member: `P:LibreMetaverse.LoginResponseData.BuddyList`. +/// C# member: `P:LibreMetaverse.LoginResponseData.COFVersion`. +/// C# member: `P:LibreMetaverse.LoginResponseData.CircuitCode`. +/// C# member: `P:LibreMetaverse.LoginResponseData.ClassifiedCategories`. +/// C# member: `P:LibreMetaverse.LoginResponseData.EventCategories`. +/// C# member: `P:LibreMetaverse.LoginResponseData.FirstLogin`. +/// C# member: `P:LibreMetaverse.LoginResponseData.FirstName`. +/// C# member: `P:LibreMetaverse.LoginResponseData.Gestures`. +/// C# member: `P:LibreMetaverse.LoginResponseData.GlobalTextures`. +/// C# member: `P:LibreMetaverse.LoginResponseData.Home`. +/// C# member: `P:LibreMetaverse.LoginResponseData.InitialOutfit`. +/// C# member: `P:LibreMetaverse.LoginResponseData.InventoryRoot`. +/// C# member: `P:LibreMetaverse.LoginResponseData.InventorySkeleton`. +/// C# member: `P:LibreMetaverse.LoginResponseData.LastName`. +/// C# member: `P:LibreMetaverse.LoginResponseData.LibraryOwner`. +/// C# member: `P:LibreMetaverse.LoginResponseData.LibraryRoot`. +/// C# member: `P:LibreMetaverse.LoginResponseData.LibrarySkeleton`. +/// C# member: `P:LibreMetaverse.LoginResponseData.Login`. +/// C# member: `P:LibreMetaverse.LoginResponseData.LookAt`. +/// C# member: `P:LibreMetaverse.LoginResponseData.MapServerUrl`. +/// C# member: `P:LibreMetaverse.LoginResponseData.MaxAgentGroups`. +/// C# member: `P:LibreMetaverse.LoginResponseData.Message`. +/// C# member: `P:LibreMetaverse.LoginResponseData.MfaHash`. +/// C# member: `P:LibreMetaverse.LoginResponseData.NextDuration`. +/// C# member: `P:LibreMetaverse.LoginResponseData.NextMethod`. +/// C# member: `P:LibreMetaverse.LoginResponseData.NextOptions`. +/// C# member: `P:LibreMetaverse.LoginResponseData.NextUrl`. +/// C# member: `P:LibreMetaverse.LoginResponseData.OpenIDUrl`. +/// C# member: `P:LibreMetaverse.LoginResponseData.PremiumPackages`. +/// C# member: `P:LibreMetaverse.LoginResponseData.Reason`. +/// C# member: `P:LibreMetaverse.LoginResponseData.RegionSizeX`. +/// C# member: `P:LibreMetaverse.LoginResponseData.RegionSizeY`. +/// C# member: `P:LibreMetaverse.LoginResponseData.RegionX`. +/// C# member: `P:LibreMetaverse.LoginResponseData.RegionY`. +/// C# member: `P:LibreMetaverse.LoginResponseData.SecondsSinceEpoch`. +/// C# member: `P:LibreMetaverse.LoginResponseData.SecureSessionID`. +/// C# member: `P:LibreMetaverse.LoginResponseData.SeedCapability`. +/// C# member: `P:LibreMetaverse.LoginResponseData.SessionID`. +/// C# member: `P:LibreMetaverse.LoginResponseData.SimIP`. +/// C# member: `P:LibreMetaverse.LoginResponseData.SimPort`. +/// C# member: `P:LibreMetaverse.LoginResponseData.SnapshotConfigUrl`. +/// C# member: `P:LibreMetaverse.LoginResponseData.StartLocation`. +/// C# member: `P:LibreMetaverse.LoginResponseData.Success`. +/// C# member: `P:LibreMetaverse.LoginResponseData.UDPBlacklist`. +/// C# member: `P:LibreMetaverse.LoginResponseData.UiConfig`. +pub use crate::login::LoginResponseData; /// C# type: `T:LibreMetaverse.LoginResponseData.ParseMessage`. -pub struct LoginResponseDataParseMessage; -impl LoginResponseDataParseMessage { - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseMessage.#ctor(LibreMetaverse.LoginResponseData.ParseMessageLevel,System.String,System.Exception)`. - pub fn new( - level: libremetaverse::LoginResponseDataParseMessageLevel, - message: String, - ex: Option, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseMessage.#ctor(LibreMetaverse.LoginResponseData.ParseMessageLevel,System.String,System.Exception)", - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.ParseMessage.Exception`. - pub fn exception(&self) -> Option { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.ParseMessage.Exception" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.ParseMessage.Level`. - pub fn level(&self) -> libremetaverse::LoginResponseDataParseMessageLevel { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.ParseMessage.Level" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.ParseMessage.Message`. - pub fn message(&self) -> String { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.ParseMessage.Message" - ) - } -} +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseMessage.#ctor(LibreMetaverse.LoginResponseData.ParseMessageLevel,System.String,System.Exception)`. +/// C# member: `P:LibreMetaverse.LoginResponseData.ParseMessage.Exception`. +/// C# member: `P:LibreMetaverse.LoginResponseData.ParseMessage.Level`. +/// C# member: `P:LibreMetaverse.LoginResponseData.ParseMessage.Message`. +pub use crate::login::LoginResponseDataParseMessage; /// C# type: `T:LibreMetaverse.LoginResponseData.ParseMessageLevel`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -17711,56 +16790,12 @@ pub enum LoginResponseDataParseMessageLevel { } /// C# type: `T:LibreMetaverse.LoginResponseData.ParseResult`. -pub struct LoginResponseDataParseResult; -impl LoginResponseDataParseResult { - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseResult.#ctor`. - pub fn new() -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseResult.#ctor", - ) - } - /// C# member: `M:LibreMetaverse.LoginResponseData.ParseResult.Add(LibreMetaverse.LoginResponseData.ParseMessageLevel,System.String,System.Exception)`. - pub fn add( - &self, - level: libremetaverse::LoginResponseDataParseMessageLevel, - message: String, - ex: Option, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.LoginResponseData.ParseResult.Add(LibreMetaverse.LoginResponseData.ParseMessageLevel,System.String,System.Exception)", - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.ParseResult.Messages`. - pub fn messages(&self) -> Vec { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.ParseResult.Messages" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.ParseResult.ParsedData`. - pub fn parsed_data(&self) -> Option { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.ParseResult.ParsedData" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.ParseResult.ParsedData`. - pub fn set_parsed_data(&mut self, value: Option) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.ParseResult.ParsedData" - ) - } - /// C# member: `P:LibreMetaverse.LoginResponseData.ParseResult.Success`. - pub fn success(&self) -> bool { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.ParseResult.Success" - ) - } - /// Setter for C# member: `P:LibreMetaverse.LoginResponseData.ParseResult.Success`. - pub fn set_success(&mut self, value: bool) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.LoginResponseData.ParseResult.Success" - ) - } -} +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseResult.#ctor`. +/// C# member: `M:LibreMetaverse.LoginResponseData.ParseResult.Add(LibreMetaverse.LoginResponseData.ParseMessageLevel,System.String,System.Exception)`. +/// C# member: `P:LibreMetaverse.LoginResponseData.ParseResult.Messages`. +/// C# member: `P:LibreMetaverse.LoginResponseData.ParseResult.ParsedData`. +/// C# member: `P:LibreMetaverse.LoginResponseData.ParseResult.Success`. +pub use crate::login::LoginResponseDataParseResult; /// C# type: `T:LibreMetaverse.LoginState`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -18612,7 +17647,8 @@ impl NetworkManager { &self, handler: libremetaverse_types::compat::EventHandler, ) -> libremetaverse_types::compat::Subscription { - libremetaverse_types::unimplemented_api!("E:LibreMetaverse.NetworkManager.LoginProgress") + /* native network implementation */ + self.native_subscribe_login_progress(handler) } /// C# member: `E:LibreMetaverse.NetworkManager.PacketSent`. pub fn subscribe_packet_sent( @@ -18663,16 +17699,16 @@ impl NetworkManager { } /// C# member: `M:LibreMetaverse.NetworkManager.AbortLogin`. pub fn abort_login(&self) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented("M:LibreMetaverse.NetworkManager.AbortLogin") + /* native network implementation */ + self.native_abort_login() } /// C# member: `M:LibreMetaverse.NetworkManager.BeginLogin(LibreMetaverse.LoginParams)`. pub fn begin_login( &self, login_params: libremetaverse::LoginParams, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.BeginLogin(LibreMetaverse.LoginParams)", - ) + /* native network implementation */ + self.native_begin_login(login_params) } /// C# member: `M:LibreMetaverse.NetworkManager.BeginLogout`. pub fn begin_logout(&self) -> Result<(), crate::Error> { @@ -18792,9 +17828,8 @@ impl NetworkManager { channel: String, version: String, ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.DefaultLoginParams(System.String,System.String,System.String,System.String,System.String)", - ) + /* native network implementation */ + self.native_default_login_params(first_name, last_name, password, channel, version) } /// C# member: `M:LibreMetaverse.NetworkManager.DisconnectSim(LibreMetaverse.Simulator,System.Boolean)`. pub fn disconnect_sim( @@ -18839,28 +17874,31 @@ impl NetworkManager { } /// C# member: `M:LibreMetaverse.NetworkManager.GetHashedMAC`. pub fn get_hashed_mac() -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.NetworkManager.GetHashedMAC") + /* native network implementation */ + crate::login::hashed_mac() } /// C# member: `M:LibreMetaverse.NetworkManager.GetMAC`. pub fn get_mac() -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.NetworkManager.GetMAC") + /* native network implementation */ + Ok(crate::login::mac_address()) } /// C# member: `M:LibreMetaverse.NetworkManager.GetPlatform`. pub fn get_platform() -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.NetworkManager.GetPlatform") + /* native network implementation */ + Ok(crate::login::platform()) } /// C# member: `M:LibreMetaverse.NetworkManager.GetPlatformVersion`. pub fn get_platform_version() -> Result { - libremetaverse_types::not_implemented("M:LibreMetaverse.NetworkManager.GetPlatformVersion") + /* native network implementation */ + Ok(crate::login::platform_version()) } /// C# member: `M:LibreMetaverse.NetworkManager.Login(LibreMetaverse.LoginResponseData)`. pub fn login_with_login_response_data( &self, response: libremetaverse::LoginResponseData, ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.Login(LibreMetaverse.LoginResponseData)", - ) + /* native network implementation */ + self.native_login_response(response) } /// C# member: `M:LibreMetaverse.NetworkManager.LoginAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.String,System.Threading.CancellationToken)`. pub async fn login_with_login_credential_string_string_string_cancellation_token( @@ -18871,9 +17909,15 @@ impl NetworkManager { version: String, cancellation_token: Option, ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.String,System.Threading.CancellationToken)", + /* native network implementation */ + self.native_login_credential( + credential, + channel, + Some(start), + version, + cancellation_token, ) + .await } /// C# member: `M:LibreMetaverse.NetworkManager.LoginAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.Threading.CancellationToken)`. pub async fn login_with_login_credential_string_string_cancellation_token( @@ -18883,9 +17927,9 @@ impl NetworkManager { version: String, cancellation_token: Option, ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.Threading.CancellationToken)", - ) + /* native network implementation */ + self.native_login_credential(credential, channel, None, version, cancellation_token) + .await } /// C# member: `M:LibreMetaverse.NetworkManager.LoginAsync(LibreMetaverse.LoginParams,System.Threading.CancellationToken)`. pub async fn login_with_login_params_cancellation_token( @@ -18893,9 +17937,8 @@ impl NetworkManager { login_params: libremetaverse::LoginParams, cancellation_token: Option, ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginAsync(LibreMetaverse.LoginParams,System.Threading.CancellationToken)", - ) + /* native network implementation */ + self.native_login(login_params, cancellation_token).await } /// C# member: `M:LibreMetaverse.NetworkManager.LoginAsync(System.String,System.String,System.String,System.String,System.String,System.String,System.Threading.CancellationToken)`. pub async fn login_with_string_string_string_string_string_string_cancellation_token( @@ -18908,9 +17951,17 @@ impl NetworkManager { version: String, cancellation_token: Option, ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginAsync(System.String,System.String,System.String,System.String,System.String,System.String,System.Threading.CancellationToken)", + /* native network implementation */ + self.native_login_strings( + first_name, + last_name, + password, + channel, + start, + version, + cancellation_token, ) + .await } /// C# member: `M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.String,System.Threading.CancellationToken)`. pub async fn login_with_response_with_login_credential_string_string_string_cancellation_token( @@ -18921,9 +17972,15 @@ impl NetworkManager { version: String, cancellation_token: Option, ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.String,System.Threading.CancellationToken)", + /* native network implementation */ + self.native_login_credential_response( + credential, + channel, + Some(start), + version, + cancellation_token, ) + .await } /// C# member: `M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.Threading.CancellationToken)`. pub async fn login_with_response_with_login_credential_string_string_cancellation_token( @@ -18933,9 +17990,15 @@ impl NetworkManager { version: String, cancellation_token: Option, ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.Threading.CancellationToken)", + /* native network implementation */ + self.native_login_credential_response( + credential, + channel, + None, + version, + cancellation_token, ) + .await } /// C# member: `M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(LibreMetaverse.LoginParams,System.Threading.CancellationToken)`. pub async fn login_with_response_with_login_params_cancellation_token( @@ -18943,9 +18006,9 @@ impl NetworkManager { login_params: libremetaverse::LoginParams, cancellation_token: Option, ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(LibreMetaverse.LoginParams,System.Threading.CancellationToken)", - ) + /* native network implementation */ + self.native_login_response_async(login_params, cancellation_token) + .await } /// C# member: `M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(System.String,System.String,System.String,System.String,System.String,System.String,System.Threading.CancellationToken)`. pub async fn login_with_response_with_string_string_string_string_string_string_cancellation_token( @@ -18958,9 +18021,17 @@ impl NetworkManager { version: String, cancellation_token: Option, ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(System.String,System.String,System.String,System.String,System.String,System.String,System.Threading.CancellationToken)", + /* native network implementation */ + self.native_login_strings_response( + first_name, + last_name, + password, + channel, + start, + version, + cancellation_token, ) + .await } /// C# member: `M:LibreMetaverse.NetworkManager.Logout`. pub fn logout_with_method(&self) -> Result<(), crate::Error> { @@ -18977,7 +18048,8 @@ impl NetworkManager { } /// C# member: `M:LibreMetaverse.NetworkManager.RefreshMac`. pub fn refresh_mac() -> Result<(), crate::Error> { - libremetaverse_types::not_implemented("M:LibreMetaverse.NetworkManager.RefreshMac") + /* native network implementation */ + Ok(crate::login::refresh_mac()) } /// C# member: `M:LibreMetaverse.NetworkManager.RegisterCallback(LibreMetaverse.Packets.PacketType,System.EventHandler{LibreMetaverse.PacketReceivedEventArgs})`. pub fn register_callback_with_packet_type_event_handler( @@ -19016,9 +18088,8 @@ impl NetworkManager { &self, callback: libremetaverse::NetworkManagerLoginResponseCallback, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.RegisterLoginResponseCallback(LibreMetaverse.NetworkManager.LoginResponseCallback)", - ) + /* native network implementation */ + self.native_register_login_response_callback(callback, None) } /// C# member: `M:LibreMetaverse.NetworkManager.RegisterLoginResponseCallback(LibreMetaverse.NetworkManager.LoginResponseCallback,System.String[])`. pub fn register_login_response_callback_with_login_response_callback_string_array( @@ -19026,9 +18097,8 @@ impl NetworkManager { callback: libremetaverse::NetworkManagerLoginResponseCallback, options: Option>, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.RegisterLoginResponseCallback(LibreMetaverse.NetworkManager.LoginResponseCallback,System.String[])", - ) + /* native network implementation */ + self.native_register_login_response_callback(callback, options) } /// C# member: `M:LibreMetaverse.NetworkManager.RequestLogout`. pub fn request_logout(&self) -> Result<(), crate::Error> { @@ -19079,9 +18149,8 @@ impl NetworkManager { } /// C# member: `M:LibreMetaverse.NetworkManager.StartLocation(System.String,System.Int32,System.Int32,System.Int32)`. pub fn start_location(sim: String, x: i32, y: i32, z: i32) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.StartLocation(System.String,System.Int32,System.Int32,System.Int32)", - ) + /* native network implementation */ + Ok(crate::login::start_location(&sim, x, y, z)) } /// C# member: `M:LibreMetaverse.NetworkManager.UnregisterCallback(LibreMetaverse.Packets.PacketType,System.EventHandler{LibreMetaverse.PacketReceivedEventArgs})`. pub fn unregister_callback( @@ -19108,21 +18177,18 @@ impl NetworkManager { &self, callback: libremetaverse::NetworkManagerLoginResponseCallback, ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.UnregisterLoginResponseCallback(LibreMetaverse.NetworkManager.LoginResponseCallback)", - ) + /* native network implementation */ + self.native_unregister_login_response_callback(&callback) } /// C# member: `P:LibreMetaverse.NetworkManager.AccountLevelBenefits`. pub fn account_level_benefits(&self) -> Option { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.NetworkManager.AccountLevelBenefits" - ) + /* native network implementation */ + self.native_account_level_benefits() } /// C# member: `P:LibreMetaverse.NetworkManager.AgentAppearanceServiceURL`. pub fn agent_appearance_service_url(&self) -> Option { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.NetworkManager.AgentAppearanceServiceURL" - ) + /* native network implementation */ + self.native_agent_appearance_service_url() } /// C# member: `P:LibreMetaverse.NetworkManager.CircuitCode`. pub fn circuit_code(&self) -> u32 { @@ -19161,43 +18227,48 @@ impl NetworkManager { } /// C# member: `P:LibreMetaverse.NetworkManager.LoginErrorKey`. pub fn login_error_key(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.NetworkManager.LoginErrorKey") + /* native network implementation */ + self.native_login_error_key() } /// Setter for C# member: `P:LibreMetaverse.NetworkManager.LoginErrorKey`. pub fn set_login_error_key(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.NetworkManager.LoginErrorKey") + /* native network implementation */ + self.native_set_login_error_key(value) } /// C# member: `P:LibreMetaverse.NetworkManager.LoginMessage`. pub fn login_message(&self) -> String { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.NetworkManager.LoginMessage") + /* native network implementation */ + self.native_login_message() } /// Setter for C# member: `P:LibreMetaverse.NetworkManager.LoginMessage`. pub fn set_login_message(&mut self, value: String) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.NetworkManager.LoginMessage") + /* native network implementation */ + self.native_set_login_message(value) } /// C# member: `P:LibreMetaverse.NetworkManager.LoginSeedCapability`. pub fn login_seed_capability(&self) -> Option { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.NetworkManager.LoginSeedCapability" - ) + /* native network implementation */ + self.native_login_seed_capability() } /// Setter for C# member: `P:LibreMetaverse.NetworkManager.LoginSeedCapability`. pub fn set_login_seed_capability(&mut self, value: Option) { - libremetaverse_types::unimplemented_api!( - "P:LibreMetaverse.NetworkManager.LoginSeedCapability" - ) + /* native network implementation */ + self.native_set_login_seed_capability(value) } /// C# member: `P:LibreMetaverse.NetworkManager.LoginStatusCode`. pub fn login_status_code(&self) -> libremetaverse::LoginStatus { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.NetworkManager.LoginStatusCode") + /* native network implementation */ + self.native_login_status_code() } /// Setter for C# member: `P:LibreMetaverse.NetworkManager.LoginStatusCode`. pub fn set_login_status_code(&mut self, value: libremetaverse::LoginStatus) { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.NetworkManager.LoginStatusCode") + /* native network implementation */ + self.native_set_login_status_code(value) } /// C# member: `P:LibreMetaverse.NetworkManager.MaxAgentGroups`. pub fn max_agent_groups(&self) -> i32 { - libremetaverse_types::unimplemented_api!("P:LibreMetaverse.NetworkManager.MaxAgentGroups") + /* native network implementation */ + self.native_max_agent_groups() } /// C# member: `P:LibreMetaverse.NetworkManager.OutboxCount`. pub fn outbox_count(&self) -> i32 { @@ -19227,55 +18298,11 @@ pub enum NetworkManagerDisconnectType { pub use crate::network_manager::NetworkManagerIncomingPacket; /// C# type: `T:LibreMetaverse.NetworkManager.LoginResponseCallback`. -pub struct NetworkManagerLoginResponseCallback; -impl NetworkManagerLoginResponseCallback { - /// C# member: `M:LibreMetaverse.NetworkManager.LoginResponseCallback.#ctor(System.Object,System.IntPtr)`. - pub fn new( - object: libremetaverse_types::compat::Object, - method: isize, - ) -> Result { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginResponseCallback.#ctor(System.Object,System.IntPtr)", - ) - } - /// C# member: `M:LibreMetaverse.NetworkManager.LoginResponseCallback.BeginInvoke(System.Boolean,System.Boolean,System.String,System.String,LibreMetaverse.LoginResponseData,System.AsyncCallback,System.Object)`. - pub fn begin_invoke( - &self, - login_success: bool, - redirect: bool, - message: String, - reason: String, - reply_data: Option, - callback: Box, - object: libremetaverse_types::compat::Object, - ) -> Result, crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginResponseCallback.BeginInvoke(System.Boolean,System.Boolean,System.String,System.String,LibreMetaverse.LoginResponseData,System.AsyncCallback,System.Object)", - ) - } - /// C# member: `M:LibreMetaverse.NetworkManager.LoginResponseCallback.EndInvoke(System.IAsyncResult)`. - pub fn end_invoke( - &self, - result: Box, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginResponseCallback.EndInvoke(System.IAsyncResult)", - ) - } - /// C# member: `M:LibreMetaverse.NetworkManager.LoginResponseCallback.Invoke(System.Boolean,System.Boolean,System.String,System.String,LibreMetaverse.LoginResponseData)`. - pub fn invoke( - &self, - login_success: bool, - redirect: bool, - message: String, - reason: String, - reply_data: Option, - ) -> Result<(), crate::Error> { - libremetaverse_types::not_implemented( - "M:LibreMetaverse.NetworkManager.LoginResponseCallback.Invoke(System.Boolean,System.Boolean,System.String,System.String,LibreMetaverse.LoginResponseData)", - ) - } -} +/// C# member: `M:LibreMetaverse.NetworkManager.LoginResponseCallback.#ctor(System.Object,System.IntPtr)`. +/// C# member: `M:LibreMetaverse.NetworkManager.LoginResponseCallback.BeginInvoke(System.Boolean,System.Boolean,System.String,System.String,LibreMetaverse.LoginResponseData,System.AsyncCallback,System.Object)`. +/// C# member: `M:LibreMetaverse.NetworkManager.LoginResponseCallback.EndInvoke(System.IAsyncResult)`. +/// C# member: `M:LibreMetaverse.NetworkManager.LoginResponseCallback.Invoke(System.Boolean,System.Boolean,System.String,System.String,LibreMetaverse.LoginResponseData)`. +pub use crate::login::NetworkManagerLoginResponseCallback; /// C# type: `T:LibreMetaverse.NetworkManager.OutgoingPacket`. /// C# member: `F:LibreMetaverse.NetworkManager.OutgoingPacket.Buffer`. diff --git a/crates/libremetaverse/src/lib.rs b/crates/libremetaverse/src/lib.rs index 3f0caad..e957d77 100644 --- a/crates/libremetaverse/src/lib.rs +++ b/crates/libremetaverse/src/lib.rs @@ -16,6 +16,7 @@ mod generated; #[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator. mod genepool_catalog; mod j2k; +mod login; mod message_codec; mod message_decoder; mod network_manager; diff --git a/crates/libremetaverse/src/login.rs b/crates/libremetaverse/src/login.rs new file mode 100644 index 0000000..65dee3b --- /dev/null +++ b/crates/libremetaverse/src/login.rs @@ -0,0 +1,1687 @@ +//! Native login request and response models. +//! +//! Login credentials are deliberately excluded from every Debug view. The +//! request flow consumes owned parameters and clears secrets after building +//! the wire request. + +#![allow(clippy::missing_errors_doc)] +#![allow(clippy::needless_pass_by_value)] // Owned parameters are fixed by the mapped C# API. +#![allow(clippy::too_many_arguments)] // Delegate signatures are fixed by the mapped C# API. +#![allow(clippy::too_many_lines)] +#![allow(clippy::type_complexity)] // Delegate signatures are fixed by the mapped C# API. + +use crate::{ + BuddyListEntry, Error, GridClient, HomeInfo, InventoryFolder, LastExecStatus, + LoginResponseDataParseMessageLevel, LoginState, LoginStatus, +}; +use libremetaverse_structured_data::{OSD, OSDMap, OSDParser}; +use libremetaverse_types::compat::{ExternalError, Hashtable, Object}; +use libremetaverse_types::{FolderType, UUID, Utils, Vector3}; +use std::collections::HashMap; +use std::fmt; +use std::net::IpAddr; +use std::sync::{Arc, Mutex, OnceLock, RwLock}; +use std::time::{Duration, Instant}; + +fn mutex(value: &Mutex) -> std::sync::MutexGuard<'_, T> { + value + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) +} + +fn read(value: &RwLock) -> std::sync::RwLockReadGuard<'_, T> { + value + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) +} + +fn write(value: &RwLock) -> std::sync::RwLockWriteGuard<'_, T> { + value + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner) +} + +#[derive(Clone)] +pub struct LoginCredential { + first_name: String, + last_name: String, + password: String, + token: String, + mfa_hash: String, +} + +impl fmt::Debug for LoginCredential { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("LoginCredential") + .field("first_name", &self.first_name) + .field("last_name", &self.last_name) + .field("password", &"") + .field("token", &"") + .field("mfa_hash", &"") + .finish() + } +} + +impl LoginCredential { + pub fn new_with_string_string_string( + first: String, + last: String, + passwd: String, + ) -> Result { + Self::new_with_string_string_string_string_string( + first, + last, + passwd, + String::new(), + String::new(), + ) + } + + pub fn new_with_string_string_string_string_string( + first: String, + last: String, + passwd: String, + token: String, + mfa_hash: String, + ) -> Result { + Ok(Self { + first_name: first, + last_name: last, + password: passwd, + token, + mfa_hash, + }) + } + + #[must_use] + pub fn first_name(&self) -> String { + self.first_name.clone() + } + #[must_use] + pub fn last_name(&self) -> String { + self.last_name.clone() + } + #[must_use] + pub fn password(&self) -> String { + self.password.clone() + } + #[must_use] + pub fn token(&self) -> String { + self.token.clone() + } + #[must_use] + pub fn mfa_hash(&self) -> String { + self.mfa_hash.clone() + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LoginException { + message: String, + inner: Option, +} + +impl LoginException { + pub fn new_with_string(message: String) -> Result { + Ok(Self { + message, + inner: None, + }) + } + + pub fn new_with_string_exception( + message: String, + inner_exception: ExternalError, + ) -> Result { + Ok(Self { + message, + inner: Some(inner_exception), + }) + } +} + +#[derive(Clone)] +pub struct LoginParams { + pub agree_to_tos: bool, + pub author: String, + pub channel: String, + pub first_name: String, + pub id0: String, + pub last_exec_event: LastExecStatus, + pub last_name: String, + pub login_location: String, + pub mac: String, + pub method_name: String, + pub mfa_enabled: bool, + pub mfa_hash: String, + pub options: Vec, + pub password: String, + pub platform: String, + pub platform_version: String, + pub read_critical: bool, + pub start: String, + pub timeout: i32, + pub token: String, + pub uri: String, + pub user_agent: String, + pub version: String, + pub viewer_digest: String, +} + +impl fmt::Debug for LoginParams { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("LoginParams") + .field("uri", &redact_uri(&self.uri)) + .field("timeout", &self.timeout) + .field("method_name", &self.method_name) + .field("first_name", &self.first_name) + .field("last_name", &self.last_name) + .field("password", &"") + .field("token", &"") + .field("mfa_hash", &"") + .field("start", &self.start) + .field("channel", &self.channel) + .field("version", &self.version) + .field("options", &self.options) + .finish_non_exhaustive() + } +} + +impl LoginParams { + pub fn new_with_constructor() -> Result { + let hashed = hashed_mac()?; + Ok(Self { + agree_to_tos: true, + author: String::new(), + channel: String::new(), + first_name: String::new(), + id0: hashed.clone(), + last_exec_event: LastExecStatus::Normal, + last_name: String::new(), + login_location: String::new(), + mac: hashed, + method_name: "login_to_simulator".to_owned(), + mfa_enabled: false, + mfa_hash: String::new(), + options: default_options(), + password: String::new(), + platform: platform(), + platform_version: platform_version(), + read_critical: true, + start: "last".to_owned(), + timeout: 0, + token: String::new(), + uri: String::new(), + user_agent: String::new(), + version: String::new(), + viewer_digest: String::new(), + }) + } + + pub fn new_with_grid_client_login_credential_string_string( + client: GridClient, + credential: LoginCredential, + channel: String, + version: String, + ) -> Result { + let mut result = Self::new_with_constructor()?; + result + .uri + .clone_from(&client.settings_ref().connection.login_server); + result.timeout = client.settings_ref().timing.login_timeout; + result.mfa_enabled = client.settings_ref().connection.mfa_enabled; + result.first_name = credential.first_name; + result.last_name = credential.last_name; + result.password = credential.password; + result.token = credential.token; + result.mfa_hash = credential.mfa_hash; + result.channel = channel; + result.version = version; + Ok(result) + } + + pub fn new_with_grid_client_login_credential_string_string_string( + client: GridClient, + credential: LoginCredential, + channel: String, + version: String, + login_uri: String, + ) -> Result { + let mut result = Self::new_with_grid_client_login_credential_string_string( + client, credential, channel, version, + )?; + result.uri = login_uri; + Ok(result) + } + + pub fn new_with_grid_client_string_string_string_string_string( + client: GridClient, + first_name: String, + last_name: String, + password: String, + channel: String, + version: String, + ) -> Result { + Self::new_with_grid_client_login_credential_string_string( + client, + LoginCredential::new_with_string_string_string(first_name, last_name, password)?, + channel, + version, + ) + } + + pub fn new_with_grid_client_string_string_string_string_string_string( + client: GridClient, + first_name: String, + last_name: String, + password: String, + channel: String, + version: String, + login_uri: String, + ) -> Result { + let mut result = Self::new_with_grid_client_string_string_string_string_string( + client, first_name, last_name, password, channel, version, + )?; + result.uri = login_uri; + Ok(result) + } + + pub(crate) fn clear_secrets(&mut self) { + self.password.clear(); + self.token.clear(); + self.mfa_hash.clear(); + } +} + +fn default_options() -> Vec { + [ + "inventory-root", + "inventory-skeleton", + "initial-outfit", + "gestures", + "display_names", + "event_categories", + "event_notifications", + "classified_categories", + "adult_compliant", + "buddy-list", + "newuser-config", + "ui-config", + "advanced-mode", + "max-agent-groups", + "map-server-url", + "voice-config", + "tutorial_settings", + "login-flags", + "global-textures", + ] + .into_iter() + .map(str::to_owned) + .collect() +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LoginProgressEventArgs { + status: LoginStatus, + message: String, + fail_reason: String, +} + +impl LoginProgressEventArgs { + pub fn new(login: LoginStatus, message: String, fail_reason: String) -> Result { + Ok(Self { + status: login, + message, + fail_reason, + }) + } + + #[must_use] + pub fn status(&self) -> LoginStatus { + self.status + } + #[must_use] + pub fn message(&self) -> String { + self.message.clone() + } + #[must_use] + pub fn fail_reason(&self) -> String { + self.fail_reason.clone() + } +} + +#[derive(Clone, PartialEq)] +pub struct AccountLevelBenefits { + integers: HashMap<&'static str, i32>, + estate_access_token: String, + linden_buy_fee: f64, + lastname_change_cost: f64, + large_texture_upload_cost: Vec, +} + +impl fmt::Debug for AccountLevelBenefits { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("AccountLevelBenefits") + .field("integers", &self.integers) + .field("estate_access_token", &"[REDACTED]") + .field("linden_buy_fee", &self.linden_buy_fee) + .field("lastname_change_cost", &self.lastname_change_cost) + .field("large_texture_upload_cost", &self.large_texture_upload_cost) + .finish() + } +} + +const BENEFIT_INTEGER_KEYS: &[&str] = &[ + "mesh_upload_cost", + "one_time_event_allowed", + "repeating_events_cost", + "lastname_change_rate", + "marketplace_concierge_support", + "phone_support", + "partner_fee", + "create_repeating_events", + "texture_upload_cost", + "priority_entry", + "object_account_level", + "use_animesh", + "beta_grid_land", + "create_group_cost", + "live_chat", + "land_auctions_allowed", + "mainland_tier", + "local_experiences", + "one_time_event_cost", + "marketplace_ple_limit", + "unpartner_fee", + "group_membership_limit", + "animation_upload_cost", + "script_limit", + "transaction_history_limit", + "animated_object_limit", + "gridwide_experience_limit", + "lastname_change_allowed", + "sound_upload_cost", + "premium_alts", + "picks_limit", + "place_pages", + "premium_access", + "marketplace_listing_limit", + "premium_gifts", + "voice_morphing", + "stored_im_limit", + "stipend", + "signup_bonus", + "attachment_limit", +]; + +impl AccountLevelBenefits { + pub fn new_with_osd_map(reply: OSDMap) -> Result { + Self::from_map(&reply.snapshot()) + } + + pub fn new_with_hashtable(reply: Hashtable) -> Result { + Self::from_map(&hashtable_to_map(&reply)?) + } + + fn from_map(reply: &HashMap) -> Result { + let mut integers = HashMap::new(); + for &key in BENEFIT_INTEGER_KEYS { + let default = if key == "attachment_limit" { 38 } else { -1 }; + let value = reply + .get(key) + .map(OSD::as_integer) + .transpose()? + .unwrap_or(default); + integers.insert(key, value); + } + let mut large_texture_upload_cost = match reply.get("large_texture_upload_cost") { + Some(OSD::Array(values)) => values + .iter() + .map(OSD::as_integer) + .collect::, _>>()?, + _ => Vec::new(), + }; + if large_texture_upload_cost.is_empty() { + large_texture_upload_cost.push(integers["texture_upload_cost"]); + } else { + large_texture_upload_cost.sort_unstable(); + } + Ok(Self { + integers, + estate_access_token: osd_string(reply, "estate_access_token")?, + linden_buy_fee: osd_real_or(reply, "linden_buy_fee", -1.0)?, + lastname_change_cost: osd_real_or(reply, "lastname_change_cost", -1.0)?, + large_texture_upload_cost, + }) + } + + fn integer(&self, key: &'static str) -> i32 { + self.integers.get(key).copied().unwrap_or(-1) + } + + #[must_use] + pub fn estate_access_token(&self) -> String { + self.estate_access_token.clone() + } + #[must_use] + pub const fn linden_buy_fee(&self) -> f64 { + self.linden_buy_fee + } + #[must_use] + pub const fn lastname_change_cost(&self) -> f64 { + self.lastname_change_cost + } + #[must_use] + pub fn large_texture_upload_cost(&self) -> Vec { + self.large_texture_upload_cost.clone() + } +} + +macro_rules! benefit_getters { + ($($method:ident => $key:literal),+ $(,)?) => {$( + #[must_use] + pub fn $method(&self) -> i32 { self.integer($key) } + )+}; +} + +impl AccountLevelBenefits { + benefit_getters! { + animated_object_limit => "animated_object_limit", + animation_upload_cost => "animation_upload_cost", + attachment_limit => "attachment_limit", + beta_grid_land => "beta_grid_land", + create_group_cost => "create_group_cost", + create_repeating_events => "create_repeating_events", + gridwide_experience_limit => "gridwide_experience_limit", + group_membership_limit => "group_membership_limit", + land_auctions_allowed => "land_auctions_allowed", + lastname_change_allowed => "lastname_change_allowed", + lastname_change_rate => "lastname_change_rate", + live_chat => "live_chat", + local_experiences => "local_experiences", + mainland_tier => "mainland_tier", + marketplace_concierge_support => "marketplace_concierge_support", + marketplace_listing_limit => "marketplace_listing_limit", + marketplace_ple_limit => "marketplace_ple_limit", + mesh_upload_cost => "mesh_upload_cost", + object_account_level => "object_account_level", + one_time_event_allowed => "one_time_event_allowed", + one_time_event_cost => "one_time_event_cost", + partner_fee => "partner_fee", + phone_support => "phone_support", + picks_limit => "picks_limit", + place_pages => "place_pages", + premium_access => "premium_access", + premium_alts => "premium_alts", + premium_gifts => "premium_gifts", + priority_entry => "priority_entry", + repeating_events_cost => "repeating_events_cost", + script_limit => "script_limit", + signup_bonus => "signup_bonus", + sound_upload_cost => "sound_upload_cost", + stipend => "stipend", + stored_im_limit => "stored_im_limit", + texture_upload_cost => "texture_upload_cost", + transaction_history_limit => "transaction_history_limit", + unpartner_fee => "unpartner_fee", + use_animesh => "use_animesh", + voice_morphing => "voice_morphing", + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LoginResponseDataParseMessage { + level: LoginResponseDataParseMessageLevel, + message: String, + exception: Option, +} + +impl LoginResponseDataParseMessage { + pub fn new( + level: LoginResponseDataParseMessageLevel, + message: String, + ex: Option, + ) -> Result { + Ok(Self { + level, + message, + exception: ex, + }) + } + + #[must_use] + pub fn level(&self) -> LoginResponseDataParseMessageLevel { + self.level + } + #[must_use] + pub fn message(&self) -> String { + self.message.clone() + } + #[must_use] + pub fn exception(&self) -> Option { + self.exception.clone() + } +} + +#[derive(Clone, Default)] +pub struct LoginResponseDataParseResult { + inner: Arc>, +} + +#[derive(Clone, Default)] +struct ParseResultFields { + success: bool, + messages: Vec, + parsed_data: Option, +} + +impl LoginResponseDataParseResult { + pub fn new() -> Result { + Ok(Self::default()) + } + + pub fn add( + &self, + level: LoginResponseDataParseMessageLevel, + message: String, + ex: Option, + ) -> Result<(), Error> { + mutex(&self.inner) + .messages + .push(LoginResponseDataParseMessage::new(level, message, ex)?); + Ok(()) + } + + #[must_use] + pub fn messages(&self) -> Vec { + mutex(&self.inner).messages.clone() + } + #[must_use] + pub fn parsed_data(&self) -> Option { + mutex(&self.inner).parsed_data.clone() + } + pub fn set_parsed_data(&mut self, value: Option) { + mutex(&self.inner).parsed_data = value; + } + #[must_use] + pub fn success(&self) -> bool { + mutex(&self.inner).success + } + pub fn set_success(&mut self, value: bool) { + mutex(&self.inner).success = value; + } +} + +#[derive(Clone)] +pub struct LoginResponseData(Arc>); + +#[derive(Clone)] +struct LoginResponseFields { + account_level_benefits: Option, + account_type: String, + agent_access: String, + agent_access_max: String, + agent_appearance_service_url: String, + agent_id: UUID, + agent_region_access: String, + buddy_list: Vec, + cof_version: u32, + circuit_code: i32, + classified_categories: Vec, + event_categories: Vec, + first_login: bool, + first_name: String, + gestures: HashMap, + global_textures: Vec, + home: HomeInfo, + initial_outfit: String, + inventory_root: UUID, + inventory_skeleton: Vec, + last_name: String, + library_owner: UUID, + library_root: UUID, + library_skeleton: Vec, + login: LoginState, + look_at: Vector3, + map_server_url: String, + max_agent_groups: i32, + message: String, + mfa_hash: String, + next_duration: i32, + next_method: String, + next_options: Vec, + next_url: String, + open_id_url: String, + premium_packages: HashMap>, + reason: String, + region_size_x: u32, + region_size_y: u32, + region_x: u32, + region_y: u32, + seconds_since_epoch: i32, + secure_session_id: UUID, + seed_capability: String, + session_id: UUID, + sim_ip: Option, + sim_port: u16, + snapshot_config_url: String, + start_location: String, + udp_blacklist: String, + ui_config: Vec, +} + +impl Default for LoginResponseFields { + fn default() -> Self { + Self { + account_level_benefits: None, + account_type: String::new(), + agent_access: String::new(), + agent_access_max: String::new(), + agent_appearance_service_url: String::new(), + agent_id: UUID::zero(), + agent_region_access: String::new(), + buddy_list: Vec::new(), + cof_version: 0, + circuit_code: 0, + classified_categories: Vec::new(), + event_categories: Vec::new(), + first_login: false, + first_name: String::new(), + gestures: HashMap::new(), + global_textures: Vec::new(), + home: HomeInfo { + look_at: Vector3::zero(), + position: Vector3::zero(), + region_handle: 0, + }, + initial_outfit: String::new(), + inventory_root: UUID::zero(), + inventory_skeleton: Vec::new(), + last_name: String::new(), + library_owner: UUID::zero(), + library_root: UUID::zero(), + library_skeleton: Vec::new(), + login: LoginState::False, + look_at: Vector3::zero(), + map_server_url: String::new(), + max_agent_groups: -1, + message: String::new(), + mfa_hash: String::new(), + next_duration: 0, + next_method: String::new(), + next_options: Vec::new(), + next_url: String::new(), + open_id_url: String::new(), + premium_packages: HashMap::new(), + reason: String::new(), + region_size_x: crate::network_manager::Simulator::DEFAULT_REGION_SIZE_X, + region_size_y: crate::network_manager::Simulator::DEFAULT_REGION_SIZE_Y, + region_x: 0, + region_y: 0, + seconds_since_epoch: 0, + secure_session_id: UUID::zero(), + seed_capability: String::new(), + session_id: UUID::zero(), + sim_ip: None, + sim_port: 0, + snapshot_config_url: String::new(), + start_location: String::new(), + udp_blacklist: String::new(), + ui_config: Vec::new(), + } + } +} + +impl fmt::Debug for LoginResponseData { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let fields = read(&self.0); + formatter + .debug_struct("LoginResponseData") + .field("login", &fields.login) + .field("reason", &fields.reason) + .field("message", &fields.message) + .field("agent_id", &fields.agent_id) + .field("session_id", &"") + .field("secure_session_id", &"") + .field("mfa_hash", &"") + .field("seed_capability", &"") + .field("sim_ip", &fields.sim_ip) + .field("sim_port", &fields.sim_port) + .finish_non_exhaustive() + } +} + +impl LoginResponseData { + pub fn new() -> Result { + Ok(Self(Arc::new(RwLock::new(LoginResponseFields::default())))) + } + + pub(crate) fn copy_from(&self, other: &Self) { + if Arc::ptr_eq(&self.0, &other.0) { + return; + } + *write(&self.0) = read(&other.0).clone(); + } + + pub(crate) fn clear_secrets(&self) { + let mut fields = write(&self.0); + fields.session_id = UUID::zero(); + fields.secure_session_id = UUID::zero(); + fields.mfa_hash.clear(); + fields.seed_capability.clear(); + fields.next_url.clear(); + fields.account_level_benefits = None; + } + + pub fn parse_with_osd_map(&self, reply: OSDMap) -> Result { + self.parse_map(reply.snapshot(), false) + } + + pub fn parse_with_hashtable( + &self, + reply: Hashtable, + ) -> Result { + self.parse_map(hashtable_to_map(&reply)?, true) + } + + fn parse_map( + &self, + reply: HashMap, + early_return_if_not_success: bool, + ) -> Result { + let mut parsed = LoginResponseFields { + agent_id: osd_uuid(&reply, "agent_id")?, + session_id: osd_uuid(&reply, "session_id")?, + secure_session_id: osd_uuid(&reply, "secure_session_id")?, + first_name: osd_string(&reply, "first_name")? + .trim_matches('"') + .trim() + .to_owned(), + last_name: osd_string(&reply, "last_name")? + .trim_matches('"') + .trim() + .to_owned(), + start_location: osd_string(&reply, "start_location")?, + agent_access: osd_string(&reply, "agent_access")?, + agent_access_max: osd_string(&reply, "agent_access_max")?, + agent_region_access: osd_string(&reply, "agent_region_access")?, + look_at: osd_vector3(&reply, "look_at")?, + reason: osd_string(&reply, "reason")?, + message: osd_string(&reply, "message")?, + ..LoginResponseFields::default() + }; + let login = osd_string(&reply, "login")?; + parsed.login = if login.eq_ignore_ascii_case("indeterminate") { + LoginState::Indeterminate + } else if login.eq_ignore_ascii_case("true") + || reply + .get("login") + .is_some_and(|value| value.as_boolean().unwrap_or(false)) + { + LoginState::True + } else { + LoginState::False + }; + parsed.next_url = osd_string(&reply, "next_url")?; + parsed.next_duration = + i32::try_from(osd_uint(&reply, "next_duration")?).unwrap_or(i32::MAX); + parsed.next_method = osd_string(&reply, "next_method")?; + parsed.next_options = osd_string_array(&reply, "next_options")?; + if early_return_if_not_success && parsed.login != LoginState::True { + *write(&self.0) = parsed; + return Ok(parse_result_for(self)); + } + + parsed.home = parse_home(&reply)?; + parsed.circuit_code = osd_uint(&reply, "circuit_code")?.cast_signed(); + parsed.region_x = osd_uint(&reply, "region_x")?; + parsed.region_y = osd_uint(&reply, "region_y")?; + parsed.region_size_x = optional_uint(&reply, "region_size_x")? + .unwrap_or(crate::network_manager::Simulator::DEFAULT_REGION_SIZE_X); + parsed.region_size_y = optional_uint(&reply, "region_size_y")? + .unwrap_or(crate::network_manager::Simulator::DEFAULT_REGION_SIZE_Y); + parsed.sim_port = u16::try_from(osd_uint(&reply, "sim_port")?).unwrap_or(0); + parsed.sim_ip = osd_string(&reply, "sim_ip")?.parse().ok(); + parsed.seed_capability = osd_string(&reply, "seed_capability")?; + parsed.seconds_since_epoch = osd_uint(&reply, "seconds_since_epoch")?.cast_signed(); + parsed.buddy_list = parse_buddy_list(&reply)?; + parsed.inventory_root = parse_mapped_uuid_map("inventory-root", "folder_id", &reply)?; + parsed.library_owner = parse_mapped_uuid_map("inventory-lib-owner", "agent_id", &reply)?; + parsed.library_root = parse_mapped_uuid_map("inventory-lib-root", "folder_id", &reply)?; + parsed.inventory_skeleton = + parse_inventory_skeleton_map("inventory-skeleton", parsed.agent_id, &reply)?; + parsed.library_skeleton = + parse_inventory_skeleton_map("inventory-skel-lib", parsed.library_owner, &reply)?; + parsed.account_type = osd_string(&reply, "account_type")?; + parsed.agent_appearance_service_url = osd_string(&reply, "agent_appearance_service")?; + parsed.cof_version = osd_uint(&reply, "cof_version")?; + parsed.map_server_url = osd_string(&reply, "map-server-url")?; + parsed.open_id_url = osd_string(&reply, "openid_url")?; + parsed.snapshot_config_url = osd_string(&reply, "snapshot_config_url")?; + parsed.udp_blacklist = osd_string(&reply, "udp_blacklist")?; + parsed.mfa_hash = osd_string(&reply, "mfa_hash")?; + if let Some(OSD::Map(map)) = reply.get("account_level_benefits") { + parsed.account_level_benefits = Some(AccountLevelBenefits::from_map(map)?); + } + parsed.classified_categories = osd_object_array(&reply, "classified_categories")?; + parsed.event_categories = osd_object_array(&reply, "event_categories")?; + parsed.global_textures = osd_object_array(&reply, "global-textures")?; + parsed.ui_config = osd_object_array(&reply, "ui-config")?; + if let Some(OSD::Map(map)) = reply.get("premium_packages") { + parsed.premium_packages = map + .iter() + .map(|(key, value)| Ok((key.clone(), Some(osd_to_object(value.clone())?)))) + .collect::>()?; + } + parsed.max_agent_groups = optional_uint(&reply, "max-agent-groups")? + .map_or(-1, |value| i32::try_from(value).unwrap_or(i32::MAX)); + parsed.initial_outfit = match reply.get("initial-outfit") { + Some(OSD::Array(items)) => items + .iter() + .filter_map(|item| match item { + OSD::Map(map) => map.get("item_id"), + _ => None, + }) + .map(OSD::as_string) + .collect::, _>>()? + .join(","), + _ => String::new(), + }; + *write(&self.0) = parsed; + Ok(parse_result_for(self)) + } + + pub fn parse_inventory_folders( + key: String, + owner: UUID, + reply: OSDMap, + ) -> Result, Error> { + parse_inventory_skeleton_map(&key, owner, &reply.snapshot()) + } + + pub fn parse_inventory_skeleton_with_string_osd_map( + &self, + key: String, + reply: OSDMap, + ) -> Result, Error> { + let owner = if key == "inventory-skel-lib" { + self.library_owner() + } else { + self.agent_id() + }; + parse_inventory_skeleton_map(&key, owner, &reply.snapshot()) + } + + pub fn parse_inventory_skeleton_with_string_hashtable( + &self, + key: String, + reply: Hashtable, + ) -> Result, Error> { + let owner = if key == "inventory-skel-lib" { + self.library_owner() + } else { + self.agent_id() + }; + parse_inventory_skeleton_map(&key, owner, &hashtable_to_map(&reply)?) + } + + pub fn parse_mapped_uuid_with_string_string_osd_map( + key: String, + key2: String, + reply: OSDMap, + ) -> Result { + parse_mapped_uuid_map(&key, &key2, &reply.snapshot()) + } + + pub fn parse_mapped_uuid_with_string_string_hashtable( + key: String, + key2: String, + reply: Hashtable, + ) -> Result { + parse_mapped_uuid_map(&key, &key2, &hashtable_to_map(&reply)?) + } + + pub fn parse_string_with_string_osd_map(key: String, reply: OSDMap) -> Result { + osd_string(&reply.snapshot(), &key) + } + pub fn parse_string_with_string_hashtable( + key: String, + reply: Hashtable, + ) -> Result { + osd_string(&hashtable_to_map(&reply)?, &key) + } + pub fn parse_u_int_with_string_osd_map(key: String, reply: OSDMap) -> Result { + osd_uint(&reply.snapshot(), &key) + } + pub fn parse_u_int_with_string_hashtable(key: String, reply: Hashtable) -> Result { + osd_uint(&hashtable_to_map(&reply)?, &key) + } + pub fn parse_uuid_with_string_osd_map(key: String, reply: OSDMap) -> Result { + osd_uuid(&reply.snapshot(), &key) + } + pub fn parse_uuid_with_string_hashtable(key: String, reply: Hashtable) -> Result { + osd_uuid(&hashtable_to_map(&reply)?, &key) + } + pub fn parse_vector3_with_string_osd_map(key: String, reply: OSDMap) -> Result { + osd_vector3(&reply.snapshot(), &key) + } + pub fn parse_vector3_with_string_hashtable( + key: String, + reply: Hashtable, + ) -> Result { + osd_vector3(&hashtable_to_map(&reply)?, &key) + } +} + +fn parse_result_for(data: &LoginResponseData) -> LoginResponseDataParseResult { + LoginResponseDataParseResult { + inner: Arc::new(Mutex::new(ParseResultFields { + success: data.success(), + messages: Vec::new(), + parsed_data: Some(data.clone()), + })), + } +} + +macro_rules! response_accessors { + ($($get:ident, $set:ident, $field:ident, $ty:ty),+ $(,)?) => {$( + #[must_use] + pub fn $get(&self) -> $ty { read(&self.0).$field.clone() } + pub fn $set(&mut self, value: $ty) { write(&self.0).$field = value; } + )+}; +} + +impl LoginResponseData { + response_accessors! { + account_level_benefits, set_account_level_benefits, account_level_benefits, Option, + account_type, set_account_type, account_type, String, + agent_access, set_agent_access, agent_access, String, + agent_access_max, set_agent_access_max, agent_access_max, String, + agent_appearance_service_url, set_agent_appearance_service_url, agent_appearance_service_url, String, + agent_id, set_agent_id, agent_id, UUID, + agent_region_access, set_agent_region_access, agent_region_access, String, + buddy_list, set_buddy_list, buddy_list, Vec, + cof_version, set_cof_version, cof_version, u32, + circuit_code, set_circuit_code, circuit_code, i32, + classified_categories, set_classified_categories, classified_categories, Vec, + event_categories, set_event_categories, event_categories, Vec, + first_login, set_first_login, first_login, bool, + first_name, set_first_name, first_name, String, + gestures, set_gestures, gestures, HashMap, + global_textures, set_global_textures, global_textures, Vec, + initial_outfit, set_initial_outfit, initial_outfit, String, + inventory_root, set_inventory_root, inventory_root, UUID, + inventory_skeleton, set_inventory_skeleton, inventory_skeleton, Vec, + last_name, set_last_name, last_name, String, + library_owner, set_library_owner, library_owner, UUID, + library_root, set_library_root, library_root, UUID, + library_skeleton, set_library_skeleton, library_skeleton, Vec, + login, set_login, login, LoginState, + look_at, set_look_at, look_at, Vector3, + map_server_url, set_map_server_url, map_server_url, String, + max_agent_groups, set_max_agent_groups, max_agent_groups, i32, + message, set_message, message, String, + mfa_hash, set_mfa_hash, mfa_hash, String, + next_duration, set_next_duration, next_duration, i32, + next_method, set_next_method, next_method, String, + next_options, set_next_options, next_options, Vec, + next_url, set_next_url, next_url, String, + open_id_url, set_open_id_url, open_id_url, String, + premium_packages, set_premium_packages, premium_packages, HashMap>, + reason, set_reason, reason, String, + region_size_x, set_region_size_x, region_size_x, u32, + region_size_y, set_region_size_y, region_size_y, u32, + region_x, set_region_x, region_x, u32, + region_y, set_region_y, region_y, u32, + seconds_since_epoch, set_seconds_since_epoch, seconds_since_epoch, i32, + secure_session_id, set_secure_session_id, secure_session_id, UUID, + seed_capability, set_seed_capability, seed_capability, String, + session_id, set_session_id, session_id, UUID, + sim_ip, set_sim_ip, sim_ip, Option, + sim_port, set_sim_port, sim_port, u16, + snapshot_config_url, set_snapshot_config_url, snapshot_config_url, String, + start_location, set_start_location, start_location, String, + udp_blacklist, set_udp_blacklist, udp_blacklist, String, + ui_config, set_ui_config, ui_config, Vec, + } + + #[must_use] + pub fn home(&self) -> HomeInfo { + read(&self.0).home + } + #[must_use] + pub fn success(&self) -> bool { + self.login() == LoginState::True + } +} + +#[derive(Clone)] +pub struct NetworkManagerLoginResponseCallback { + handler: Arc) + Send + Sync>, +} + +impl fmt::Debug for NetworkManagerLoginResponseCallback { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("NetworkManagerLoginResponseCallback") + .finish_non_exhaustive() + } +} + +impl NetworkManagerLoginResponseCallback { + pub fn from_handler( + handler: impl Fn(bool, bool, String, String, Option) + Send + Sync + 'static, + ) -> Self { + Self { + handler: Arc::new(handler), + } + } + + pub fn new(object: Object, method: isize) -> Result { + if method == 0 { + return Err(Error::Argument); + } + object + .downcast_arc::() + .map(|callback| (*callback).clone()) + .ok_or(Error::Argument) + } + + pub fn invoke( + &self, + login_success: bool, + redirect: bool, + message: String, + reason: String, + reply_data: Option, + ) -> Result<(), Error> { + let handler = Arc::clone(&self.handler); + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + handler(login_success, redirect, message, reason, reply_data); + })) + .map_err(|_| Error::InvalidOperation) + } + + pub fn begin_invoke( + &self, + login_success: bool, + redirect: bool, + message: String, + reason: String, + reply_data: Option, + callback: Box, + object: Object, + ) -> Result, Error> { + self.invoke(login_success, redirect, message, reason, reply_data)?; + callback(&object); + Ok(Box::new(())) + } + + pub fn end_invoke(&self, result: Box) -> Result<(), Error> { + result + .downcast::<()>() + .map(|_| ()) + .map_err(|_| Error::Argument) + } + + pub(crate) fn ptr_eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.handler, &other.handler) + } +} + +pub(crate) fn start_location(sim: &str, x: i32, y: i32, z: i32) -> String { + format!("uri:{sim}&{x}&{y}&{z}") +} + +pub(crate) fn normalize_start(value: &str) -> Result { + let value = value.trim(); + if value == "home" || value == "last" { + return Ok(value.to_owned()); + } + if value.is_empty() { + return Err(Error::Argument); + } + let location = value.strip_prefix("secondlife://").unwrap_or(value); + if let Some(legacy) = location.strip_prefix("uri:") { + let mut fields = legacy.split('&'); + let sim = fields + .next() + .filter(|value| !value.is_empty()) + .ok_or(Error::Argument)?; + let x = fields + .next() + .and_then(|value| value.parse().ok()) + .unwrap_or(128); + let y = fields + .next() + .and_then(|value| value.parse().ok()) + .unwrap_or(128); + let z = fields + .next() + .and_then(|value| value.parse().ok()) + .unwrap_or(0); + return Ok(start_location(sim, x, y, z)); + } + let path = location + .split('?') + .next() + .unwrap_or(location) + .trim_end_matches('/'); + let mut fields = path.split('/'); + let sim = fields + .next() + .filter(|value| !value.is_empty()) + .ok_or(Error::Argument)?; + let x = fields + .next() + .and_then(|value| value.parse().ok()) + .unwrap_or(128); + let y = fields + .next() + .and_then(|value| value.parse().ok()) + .unwrap_or(128); + let z = fields + .next() + .and_then(|value| value.parse().ok()) + .unwrap_or(0); + Ok(start_location(sim, x, y, z)) +} + +#[must_use] +pub(crate) fn platform() -> String { + match std::env::consts::OS { + "macos" => "mac", + "linux" | "android" => "lnx", + _ => "win", + } + .to_owned() +} + +#[must_use] +pub(crate) fn platform_version() -> String { + os_info::get().version().to_string() +} + +const MAC_CACHE_TTL: Duration = Duration::from_mins(5); + +struct CachedMac { + value: String, + observed_at: Instant, +} + +static MAC_ADDRESS: OnceLock>> = OnceLock::new(); + +pub(crate) fn mac_address() -> String { + let cache = MAC_ADDRESS.get_or_init(|| Mutex::new(None)); + let mut cached = mutex(cache); + if let Some(entry) = cached.as_ref() + && entry.observed_at.elapsed() < MAC_CACHE_TTL + { + return entry.value.clone(); + } + let value = detected_mac_address(); + *cached = Some(CachedMac { + value: value.clone(), + observed_at: Instant::now(), + }); + value +} + +fn detected_mac_address() -> String { + let bytes = mac_address2::get_mac_address() + .ok() + .flatten() + .map(mac_address2::MacAddress::bytes) + .filter(|bytes| bytes.iter().any(|byte| *byte != 0)) + .unwrap_or_else(|| { + let random = UUID::random() + .unwrap_or_else(|_| UUID::zero()) + .get_bytes() + .unwrap_or_default(); + random[random.len().saturating_sub(6)..] + .try_into() + .unwrap_or([0; 6]) + }); + bytes + .iter() + .map(|byte| format!("{byte:02X}")) + .collect::>() + .join(":") +} + +pub(crate) fn refresh_mac() { + *mutex(MAC_ADDRESS.get_or_init(|| Mutex::new(None))) = None; + let _ = mac_address(); +} + +pub(crate) fn hashed_mac() -> Result { + Ok(Utils::md5_string(mac_address())?.to_uppercase()) +} + +fn redact_uri(uri: &str) -> String { + let Ok(mut parsed) = reqwest::Url::parse(uri) else { + return "".to_owned(); + }; + let _ = parsed.set_username(""); + let _ = parsed.set_password(None); + parsed.set_query(None); + parsed.set_fragment(None); + parsed.to_string() +} + +fn osd_string(map: &HashMap, key: &str) -> Result { + map.get(key) + .map(OSD::as_string) + .transpose() + .map(Option::unwrap_or_default) +} + +fn osd_uint(map: &HashMap, key: &str) -> Result { + map.get(key) + .map(OSD::as_u_integer) + .transpose() + .map(Option::unwrap_or_default) +} + +fn optional_uint(map: &HashMap, key: &str) -> Result, Error> { + map.get(key).map(OSD::as_u_integer).transpose() +} + +fn osd_uuid(map: &HashMap, key: &str) -> Result { + map.get(key) + .map(OSD::as_uuid) + .transpose() + .map(|value| value.unwrap_or_else(UUID::zero)) +} + +fn osd_real_or(map: &HashMap, key: &str, default: f64) -> Result { + map.get(key) + .map(OSD::as_real) + .transpose() + .map(|value| value.unwrap_or(default)) +} + +fn osd_vector3(map: &HashMap, key: &str) -> Result { + let Some(value) = map.get(key) else { + return Ok(Vector3::zero()); + }; + match value { + OSD::Array(_) => value.as_vector3(), + OSD::String(notation) => { + OSDParser::deserialize_llsd_notation_with_string(notation.clone())?.as_vector3() + } + _ => Ok(Vector3::zero()), + } +} + +fn osd_string_array(map: &HashMap, key: &str) -> Result, Error> { + match map.get(key) { + Some(OSD::Array(values)) => values.iter().map(OSD::as_string).collect(), + _ => Ok(Vec::new()), + } +} + +fn osd_object_array(map: &HashMap, key: &str) -> Result, Error> { + match map.get(key) { + Some(OSD::Array(values)) => values.iter().cloned().map(osd_to_object).collect(), + _ => Ok(Vec::new()), + } +} + +fn parse_home(map: &HashMap) -> Result { + let home_map = match map.get("home_info") { + Some(OSD::Map(map)) => Some(map.clone()), + _ => match map.get("home") { + Some(OSD::Map(map)) => Some(map.clone()), + Some(OSD::String(notation)) => { + match OSDParser::deserialize_llsd_notation_with_string(notation.clone())? { + OSD::Map(map) => Some(map), + _ => None, + } + } + _ => None, + }, + }; + let Some(home_map) = home_map else { + return Ok(HomeInfo { + look_at: Vector3::zero(), + position: Vector3::zero(), + region_handle: 0, + }); + }; + let region_handle = match home_map.get("region_handle") { + Some(OSD::Array(coords)) if coords.len() == 2 => { + (u64::from(coords[0].as_u_integer()?) << 32) | u64::from(coords[1].as_u_integer()?) + } + Some(value) => { + let notation = value.as_string()?; + match OSDParser::deserialize_llsd_notation_with_string(notation)? { + OSD::Array(coords) if coords.len() == 2 => { + (u64::from(coords[0].as_u_integer()?) << 32) + | u64::from(coords[1].as_u_integer()?) + } + _ => 0, + } + } + None => 0, + }; + Ok(HomeInfo { + look_at: osd_vector3(&home_map, "look_at")?, + position: osd_vector3(&home_map, "position")?, + region_handle, + }) +} + +fn parse_buddy_list(map: &HashMap) -> Result, Error> { + let Some(OSD::Array(values)) = map.get("buddy-list") else { + return Ok(Vec::new()); + }; + values + .iter() + .filter_map(|value| match value { + OSD::Map(map) => Some(map), + _ => None, + }) + .map(|buddy| { + Ok(BuddyListEntry { + buddy_id: osd_string(buddy, "buddy_id")?, + buddy_rights_given: osd_uint(buddy, "buddy_rights_given")?.cast_signed(), + buddy_rights_has: osd_uint(buddy, "buddy_rights_has")?.cast_signed(), + }) + }) + .collect() +} + +fn parse_mapped_uuid_map( + key: &str, + nested_key: &str, + reply: &HashMap, +) -> Result { + match reply.get(key) { + Some(OSD::Array(values)) if values.len() == 1 => match &values[0] { + OSD::Map(map) => osd_uuid(map, nested_key), + _ => Ok(UUID::zero()), + }, + _ => Ok(UUID::zero()), + } +} + +fn parse_inventory_skeleton_map( + key: &str, + owner: UUID, + reply: &HashMap, +) -> Result, Error> { + let Some(OSD::Array(values)) = reply.get(key) else { + return Ok(Vec::new()); + }; + values + .iter() + .filter_map(|value| match value { + OSD::Map(map) => Some(map), + _ => None, + }) + .map(|map| { + let mut folder = InventoryFolder::new(osd_uuid(map, "folder_id")?)?; + folder.base.set_name(osd_string(map, "name")?); + folder.base.set_parent_uuid(osd_uuid(map, "parent_id")?); + folder.base.set_owner_id(owner); + let folder_type_value = osd_uint(map, "type_default")?.to_le_bytes()[0].cast_signed(); + folder.set_preferred_type(folder_type(folder_type_value)); + folder.set_version(osd_uint(map, "version")?.cast_signed()); + Ok(folder) + }) + .collect() +} + +fn folder_type(value: i8) -> FolderType { + match value { + 0 => FolderType::Texture, + 1 => FolderType::Sound, + 2 => FolderType::CallingCard, + 3 => FolderType::Landmark, + 5 => FolderType::Clothing, + 6 => FolderType::Object, + 7 => FolderType::Notecard, + 8 => FolderType::Root, + 9 => FolderType::OldRoot, + 10 => FolderType::LSLText, + 13 => FolderType::BodyPart, + 14 => FolderType::Trash, + 15 => FolderType::Snapshot, + 16 => FolderType::LostAndFound, + 20 => FolderType::Animation, + 21 => FolderType::Gesture, + 23 => FolderType::Favorites, + 26 => FolderType::EnsembleStart, + 45 => FolderType::EnsembleEnd, + 46 => FolderType::CurrentOutfit, + 47 => FolderType::Outfit, + 48 => FolderType::MyOutfits, + 49 => FolderType::Mesh, + 50 => FolderType::Inbox, + 51 => FolderType::Outbox, + 52 => FolderType::BasicRoot, + 53 => FolderType::MarketplaceListings, + 54 => FolderType::MarketplaceStock, + 55 => FolderType::MarketplaceVersion, + 56 => FolderType::Settings, + 57 => FolderType::Material, + 100 => FolderType::Suitcase, + _ => FolderType::None, + } +} + +fn hashtable_to_map(table: &Hashtable) -> Result, Error> { + table + .0 + .iter() + .filter_map(|(key, value)| match key { + Object::String(key) => { + Some(object_to_osd(value.clone()).map(|value| (key.clone(), value))) + } + _ => None, + }) + .collect() +} + +fn object_to_osd(value: Object) -> Result { + Ok(match value { + Object::Boolean(value) => OSD::Boolean(value), + Object::Integer(value) => OSD::Integer(value), + Object::UInteger(value) => OSD::Binary(value.to_be_bytes().to_vec()), + Object::Long(value) => OSD::Binary(value.to_be_bytes().to_vec()), + Object::ULong(value) => OSD::Binary(value.to_be_bytes().to_vec()), + Object::Real(value) => OSD::Real(value), + Object::Bytes(value) => OSD::Binary(value), + Object::Uri(value) => OSD::Uri(value), + Object::Array(values) => OSD::Array( + values + .into_iter() + .map(object_to_osd) + .collect::>()?, + ), + Object::Map(values) => OSD::Map( + values + .into_iter() + .map(|(key, value)| Ok((key, object_to_osd(value)?))) + .collect::>()?, + ), + Object::String(value) => OSD::String(value), + Object::UUID(value) => OSD::UUID(value), + Object::Vector2(value) => OSD::from_vector2(value)?, + Object::Vector3(value) => OSD::from_vector3(value)?, + Object::Vector3d(value) => OSD::from_vector3d(value)?, + Object::Vector4(value) => OSD::from_vector4(value)?, + Object::Quaternion(value) => OSD::from_quaternion(value)?, + Object::Color4(value) => OSD::from_color4(value)?, + Object::Date(value) => OSD::Date(value), + Object::Undefined | Object::Matrix4(_) | Object::Opaque(_) => OSD::Undefined, + }) +} + +fn osd_to_object(value: OSD) -> Result { + Ok(match value { + OSD::Boolean(value) => Object::Boolean(value), + OSD::Integer(value) => Object::Integer(value), + OSD::Real(value) => Object::Real(value), + OSD::String(value) | OSD::LlsdXml(value) => Object::String(value), + OSD::UUID(value) => Object::UUID(value), + OSD::Date(value) => Object::Date(value), + OSD::Uri(value) => Object::Uri(value), + OSD::Binary(value) => Object::Bytes(value), + OSD::Array(values) => Object::Array( + values + .into_iter() + .map(osd_to_object) + .collect::>()?, + ), + OSD::Map(values) => Object::Map( + values + .into_iter() + .map(|(key, value)| Ok((key, osd_to_object(value)?))) + .collect::>()?, + ), + _ => Object::Undefined, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn credential_and_params_debug_redact_all_secrets() { + let credential = LoginCredential::new_with_string_string_string_string_string( + "Ada".into(), + "Lovelace".into(), + "plain-secret".into(), + "123456".into(), + "saved-mfa-secret".into(), + ) + .unwrap(); + let rendered = format!("{credential:?}"); + assert!(!rendered.contains("plain-secret")); + assert!(!rendered.contains("123456")); + assert!(!rendered.contains("saved-mfa-secret")); + + let mut params = LoginParams::new_with_constructor().unwrap(); + params.uri = "https://user:password@example.test/login?token=cap-secret".into(); + params.password = "plain-secret".into(); + params.token = "123456".into(); + params.mfa_hash = "saved-mfa-secret".into(); + let rendered = format!("{params:?}"); + assert!(!rendered.contains("user:password")); + assert!(!rendered.contains("cap-secret")); + assert!(!rendered.contains("plain-secret")); + assert!(!rendered.contains("123456")); + assert!(!rendered.contains("saved-mfa-secret")); + } + + #[test] + fn response_parser_covers_redirect_home_inventory_and_benefits() { + let agent = UUID::new_with_string("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa".into()).unwrap(); + let folder = UUID::new_with_string("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb".into()).unwrap(); + let response = LoginResponseData::new().unwrap(); + let map = OSDMap::new_with_dictionary(HashMap::from([ + ("login".into(), OSD::Boolean(true)), + ("agent_id".into(), OSD::UUID(agent)), + ( + "home_info".into(), + OSD::Map(HashMap::from([ + ( + "region_handle".into(), + OSD::Array(vec![OSD::Integer(1000), OSD::Integer(2000)]), + ), + ( + "position".into(), + OSD::Array(vec![OSD::Real(128.0), OSD::Real(129.0), OSD::Real(30.0)]), + ), + ( + "look_at".into(), + OSD::Array(vec![OSD::Real(1.0), OSD::Real(0.0), OSD::Real(0.0)]), + ), + ])), + ), + ( + "inventory-skeleton".into(), + OSD::Array(vec![OSD::Map(HashMap::from([ + ("folder_id".into(), OSD::UUID(folder)), + ("parent_id".into(), OSD::UUID(UUID::zero())), + ("name".into(), OSD::String("Objects".into())), + ("type_default".into(), OSD::Integer(6)), + ("version".into(), OSD::Integer(4)), + ]))]), + ), + ( + "account_level_benefits".into(), + OSD::Map(HashMap::from([ + ("texture_upload_cost".into(), OSD::Integer(10)), + ( + "large_texture_upload_cost".into(), + OSD::Array(vec![OSD::Integer(30), OSD::Integer(20)]), + ), + ("attachment_limit".into(), OSD::Integer(42)), + ( + "estate_access_token".into(), + OSD::String("estate-secret".into()), + ), + ])), + ), + ])) + .unwrap(); + let result = response.parse_with_osd_map(map).unwrap(); + assert!(result.success()); + assert_eq!(response.home().region_handle, (1000_u64 << 32) | 2000); + assert_eq!(response.inventory_skeleton().len(), 1); + let inventory = response.inventory_skeleton(); + assert_eq!(inventory[0].base.uuid(), folder); + assert_eq!(inventory[0].base.owner_id(), agent); + assert_eq!(inventory[0].base.name(), "Objects"); + let benefits = response.account_level_benefits().unwrap(); + assert_eq!(benefits.attachment_limit(), 42); + assert_eq!(benefits.large_texture_upload_cost(), [20, 30]); + assert!(!format!("{benefits:?}").contains("estate-secret")); + } + + #[test] + fn mac_identity_is_cached_and_uses_reference_format() { + let first = mac_address(); + assert_eq!(first.len(), 17); + assert_eq!(first.as_bytes()[2], b':'); + assert_eq!(first.as_bytes()[5], b':'); + assert_eq!(first.as_bytes()[8], b':'); + assert_eq!(first.as_bytes()[11], b':'); + assert_eq!(first.as_bytes()[14], b':'); + assert!( + first + .bytes() + .all(|byte| byte == b':' || byte.is_ascii_hexdigit()) + ); + assert_eq!(mac_address(), first); + assert_eq!( + hashed_mac().unwrap(), + Utils::md5_string(first).unwrap().to_uppercase() + ); + assert!(matches!(platform().as_str(), "mac" | "lnx" | "win")); + assert!(!platform_version().is_empty()); + refresh_mac(); + assert_eq!(mac_address().len(), 17); + } + + #[test] + fn start_locations_match_legacy_reference_encoding() { + assert_eq!(normalize_start("home").unwrap(), "home"); + assert_eq!( + normalize_start("secondlife://Hooper/1/2/3/").unwrap(), + "uri:Hooper&1&2&3" + ); + assert_eq!(normalize_start("Hooper").unwrap(), "uri:Hooper&128&128&0"); + assert_eq!( + normalize_start("uri:Hooper&4&5&6").unwrap(), + "uri:Hooper&4&5&6" + ); + } +} diff --git a/crates/libremetaverse/src/network_manager.rs b/crates/libremetaverse/src/network_manager.rs index 55d4ca7..27aef40 100644 --- a/crates/libremetaverse/src/network_manager.rs +++ b/crates/libremetaverse/src/network_manager.rs @@ -7,6 +7,7 @@ #![allow(clippy::missing_errors_doc)] // Result shapes are fixed by the compatibility map. #![allow(clippy::needless_pass_by_value)] // Owned mapped parameters preserve the public API. +#![allow(clippy::too_many_arguments)] // Login overloads preserve the mapped public API. #![allow(clippy::too_many_lines)] // Packet dispatch keeps the complete protocol switch together. #![allow(clippy::type_complexity)] // Delegate signatures are fixed by the compatibility map. @@ -18,11 +19,15 @@ use crate::packets::{ }; use crate::udp_transport::{UDPBase, UDPPacketBuffer, UdpPacketHandler, UdpTransportConfig}; use crate::{ - Avatar, Caps, Error, GenericStreamingMethod, GridClient, Helpers, LoginResponseData, Primitive, - RegionFlags, RegionProtocols, SimAccess, SimulatorDataPool, SimulatorFeatures, - SimulatorSimStats, TerrainPatch, + AccountLevelBenefits, Avatar, Caps, Error, GenericStreamingMethod, GridClient, Helpers, + LoginCredential, LoginParams, LoginProgressEventArgs, LoginResponseData, LoginStatus, + NetworkManagerLoginResponseCallback, Primitive, RegionFlags, RegionProtocols, SimAccess, + SimulatorDataPool, SimulatorFeatures, SimulatorSimStats, TerrainPatch, +}; +use libremetaverse_structured_data::{OSD, OSDFormat, OSDMap, OSDParser}; +use libremetaverse_types::compat::{ + CancellationToken, CancellationTokenSource, EventHandler, Object, Subscription, Uri, }; -use libremetaverse_types::compat::{EventHandler, Object, Subscription, Uri}; use libremetaverse_types::{UUID, Vector2}; use std::collections::HashMap; use std::fmt; @@ -90,6 +95,76 @@ fn positive_millisecond_duration(value: i32) -> Duration { Duration::from_millis(u64::try_from(value.max(1)).unwrap_or(1)) } +fn validate_login_uri(value: &str) -> Result { + let parsed = reqwest::Url::parse(value).map_err(|_| Error::Argument)?; + if !matches!(parsed.scheme(), "http" | "https") || parsed.host_str().is_none() { + return Err(Error::Argument); + } + Ok(Uri(parsed.to_string())) +} + +struct LoginWireSecrets { + password: String, + token: String, + mfa_hash: String, +} + +impl Drop for LoginWireSecrets { + fn drop(&mut self) { + // Overwrite the live buffers before releasing them. This cannot erase + // allocator copies, but it prevents the owned request state surviving + // the login/redirect sequence. + self.password + .replace_range(.., &"\0".repeat(self.password.len())); + self.token.replace_range(.., &"\0".repeat(self.token.len())); + self.mfa_hash + .replace_range(.., &"\0".repeat(self.mfa_hash.len())); + self.password.clear(); + self.token.clear(); + self.mfa_hash.clear(); + } +} + +fn login_request(params: &LoginParams, secrets: &LoginWireSecrets) -> OSD { + let mut map = HashMap::from([ + ("first".to_owned(), OSD::String(params.first_name.clone())), + ("last".to_owned(), OSD::String(params.last_name.clone())), + ("passwd".to_owned(), OSD::String(secrets.password.clone())), + ("start".to_owned(), OSD::String(params.start.clone())), + ("channel".to_owned(), OSD::String(params.channel.clone())), + ("version".to_owned(), OSD::String(params.version.clone())), + ("platform".to_owned(), OSD::String(params.platform.clone())), + ( + "platform_version".to_owned(), + OSD::String(params.platform_version.clone()), + ), + ("mac".to_owned(), OSD::String(params.mac.clone())), + ("agree_to_tos".to_owned(), OSD::Boolean(params.agree_to_tos)), + ( + "read_critical".to_owned(), + OSD::Boolean(params.read_critical), + ), + ( + "viewer_digest".to_owned(), + OSD::String(params.viewer_digest.clone()), + ), + ("id0".to_owned(), OSD::String(params.id0.clone())), + ( + "last_exec_event".to_owned(), + OSD::Integer(params.last_exec_event as i32), + ), + ( + "options".to_owned(), + OSD::Array(params.options.iter().cloned().map(OSD::String).collect()), + ), + ]); + if params.mfa_enabled { + map.insert("token".to_owned(), OSD::String(secrets.token.clone())); + map.insert("mfa_hash".to_owned(), OSD::String(secrets.mfa_hash.clone())); + } + OSD::Map(map) +} + async fn run_blocking_compat( name: &str, operation: impl FnOnce() -> T + Send + 'static, @@ -857,6 +932,8 @@ pub struct SimulatorData { handshake_wait_lock: Mutex<()>, disconnect_candidate: AtomicBool, circuit_code: AtomicU32, + agent_id: RwLock, + session_id: RwLock, seed_caps: RwLock>, caps_state: RwLock>>, event_queue_generation: Mutex, @@ -1018,6 +1095,8 @@ impl Simulator { handshake_wait_lock: Mutex::new(()), disconnect_candidate: AtomicBool::new(false), circuit_code: AtomicU32::new(0), + agent_id: RwLock::new(UUID::zero()), + session_id: RwLock::new(UUID::zero()), seed_caps: RwLock::new(None), caps_state: RwLock::new(None), event_queue_generation: Mutex::new(0), @@ -1058,6 +1137,8 @@ impl Simulator { fn attach_manager(&self, manager: &Arc, circuit_code: u32) { *mutex(&self.manager) = Arc::downgrade(manager); self.circuit_code.store(circuit_code, Ordering::Release); + *write(&self.agent_id) = *read(&manager.agent_id); + *write(&self.session_id) = *read(&manager.session_id); } pub fn native_connect(&self, _move_to_sim: bool) -> Result { @@ -1414,8 +1495,8 @@ impl Simulator { fn use_circuit_code_bytes(&self) -> Result, Error> { let mut packet = UseCircuitCodePacket::new_with_constructor()?; packet.circuit_code.code = self.circuit_code.load(Ordering::Acquire); - packet.circuit_code.id = UUID::zero(); - packet.circuit_code.session_id = UUID::zero(); + packet.circuit_code.id = *read(&self.agent_id); + packet.circuit_code.session_id = *read(&self.session_id); packet.to_bytes_with_method() } @@ -1566,6 +1647,7 @@ struct NetworkEvents { disconnected: EventRegistry, event_queue_running: EventRegistry, generic_streaming_message: EventRegistry, + login_progress: EventRegistry, packet_sent: EventRegistry, sim_changed: EventRegistry, sim_connected: EventRegistry, @@ -1573,7 +1655,23 @@ struct NetworkEvents { sim_disconnected: EventRegistry, } -struct NetworkManagerInner { +struct LoginRuntimeState { + next_generation: u64, + active: Option<(u64, CancellationTokenSource)>, + response_callbacks: Vec<(NetworkManagerLoginResponseCallback, Option>)>, +} + +impl Default for LoginRuntimeState { + fn default() -> Self { + Self { + next_generation: 1, + active: None, + response_callbacks: Vec::new(), + } + } +} + +pub(crate) struct NetworkManagerInner { client: GridClient, simulators: SimulatorCollection, packet_events: PacketEventDictionary, @@ -1582,6 +1680,14 @@ struct NetworkManagerInner { current_sim: RwLock>, connected: AtomicBool, circuit_code: AtomicU32, + agent_id: RwLock, + session_id: RwLock, + login_status: RwLock, + login_error_key: RwLock, + login_message: RwLock, + login_seed_capability: RwLock>, + login_response: LoginResponseData, + login_runtime: Mutex, inbox_count: AtomicI32, outbox_count: AtomicI32, workers: Mutex>, @@ -2039,10 +2145,23 @@ impl fmt::Debug for NetworkManager { } impl NetworkManager { + pub(crate) fn native_from_inner(inner: Arc) -> Self { + Self { + login_response_data: Some(inner.login_response.clone()), + simulators: inner.simulators.clone(), + inner, + } + } + + pub(crate) fn native_inner_weak(&self) -> Weak { + Arc::downgrade(&self.inner) + } + pub fn native_new(client: GridClient) -> Result { let simulators = SimulatorCollection::default(); let packet_events = PacketEventDictionary::new(client.clone())?; let caps_events = CapsEventDictionary::new(client.clone())?; + let login_response = LoginResponseData::new()?; let inner = Arc::new(NetworkManagerInner { client, simulators: simulators.clone(), @@ -2052,6 +2171,14 @@ impl NetworkManager { current_sim: RwLock::new(None), connected: AtomicBool::new(false), circuit_code: AtomicU32::new(0), + agent_id: RwLock::new(UUID::zero()), + session_id: RwLock::new(UUID::zero()), + login_status: RwLock::new(LoginStatus::None), + login_error_key: RwLock::new(String::new()), + login_message: RwLock::new(String::new()), + login_seed_capability: RwLock::new(None), + login_response: login_response.clone(), + login_runtime: Mutex::new(LoginRuntimeState::default()), inbox_count: AtomicI32::new(0), outbox_count: AtomicI32::new(0), workers: Mutex::new(None), @@ -2075,7 +2202,7 @@ impl NetworkManager { return; }; let manager = NetworkManager { - login_response_data: None, + login_response_data: Some(inner.login_response.clone()), simulators: inner.simulators.clone(), inner: Arc::clone(&inner), }; @@ -2099,12 +2226,623 @@ impl NetworkManager { }), )?; Ok(Self { - login_response_data: None, + login_response_data: Some(login_response), simulators, inner, }) } + pub fn native_subscribe_login_progress( + &self, + handler: EventHandler, + ) -> Subscription { + self.inner.events.login_progress.subscribe(handler) + } + + fn update_login_status(&self, status: LoginStatus, message: String) { + if status == LoginStatus::Failed { + *write(&self.inner.agent_id) = UUID::zero(); + *write(&self.inner.session_id) = UUID::zero(); + *write(&self.inner.login_seed_capability) = None; + self.inner.login_response.clear_secrets(); + } + *write(&self.inner.login_status) = status; + write(&self.inner.login_message).clone_from(&message); + let fail_reason = read(&self.inner.login_error_key).clone(); + if let Ok(args) = LoginProgressEventArgs::new(status, message, fail_reason) { + self.inner.events.login_progress.emit_with(|| args.clone()); + } + } + + fn set_login_error(&self, value: &str) { + let mut error = write(&self.inner.login_error_key); + error.clear(); + error.push_str(value); + } + + #[must_use] + pub fn native_login_status_code(&self) -> LoginStatus { + *read(&self.inner.login_status) + } + + pub fn native_set_login_status_code(&self, value: LoginStatus) { + *write(&self.inner.login_status) = value; + } + + #[must_use] + pub fn native_login_error_key(&self) -> String { + read(&self.inner.login_error_key).clone() + } + + pub fn native_set_login_error_key(&self, value: String) { + *write(&self.inner.login_error_key) = value; + } + + #[must_use] + pub fn native_login_message(&self) -> String { + read(&self.inner.login_message).clone() + } + + pub fn native_set_login_message(&self, value: String) { + *write(&self.inner.login_message) = value; + } + + #[must_use] + pub fn native_login_seed_capability(&self) -> Option { + read(&self.inner.login_seed_capability).clone() + } + + pub fn native_set_login_seed_capability(&self, value: Option) { + *write(&self.inner.login_seed_capability) = value; + } + + #[must_use] + pub fn native_account_level_benefits(&self) -> Option { + self.inner.login_response.account_level_benefits() + } + + #[must_use] + pub fn native_agent_appearance_service_url(&self) -> Option { + let value = self.inner.login_response.agent_appearance_service_url(); + (!value.is_empty()).then_some(value) + } + + #[must_use] + pub fn native_max_agent_groups(&self) -> i32 { + let fallback = self.inner.login_response.max_agent_groups(); + self.inner + .login_response + .account_level_benefits() + .map_or(fallback, |benefits| { + let limit = benefits.group_membership_limit(); + if limit > 0 { limit } else { fallback } + }) + } + + pub fn native_default_login_params( + &self, + first_name: String, + last_name: String, + password: String, + channel: String, + version: String, + ) -> Result { + LoginParams::new_with_grid_client_string_string_string_string_string( + self.inner.client.clone(), + first_name, + last_name, + password, + channel, + version, + ) + } + + pub fn native_register_login_response_callback( + &self, + callback: NetworkManagerLoginResponseCallback, + options: Option>, + ) -> Result<(), Error> { + let mut state = mutex(&self.inner.login_runtime); + if state + .response_callbacks + .iter() + .any(|(candidate, _)| candidate.ptr_eq(&callback)) + { + return Err(Error::Argument); + } + state.response_callbacks.push((callback, options)); + Ok(()) + } + + pub fn native_unregister_login_response_callback( + &self, + callback: &NetworkManagerLoginResponseCallback, + ) -> Result<(), Error> { + let mut state = mutex(&self.inner.login_runtime); + state + .response_callbacks + .retain(|(candidate, _)| !candidate.ptr_eq(callback)); + Ok(()) + } + + fn reserve_login( + &self, + cancellation_token: Option, + ) -> Result<(u64, CancellationTokenSource), Error> { + let mut runtime = mutex(&self.inner.login_runtime); + if runtime.active.is_some() { + return Err(Error::InvalidOperation); + } + let caller = cancellation_token.unwrap_or_default(); + let source = + CancellationTokenSource::new_linked(&[caller, self.inner.client.cancellation_token()]); + let generation = runtime.next_generation; + runtime.next_generation = runtime.next_generation.wrapping_add(1).max(1); + runtime.active = Some((generation, source.clone())); + Ok((generation, source)) + } + + fn release_login(&self, generation: u64) { + let mut runtime = mutex(&self.inner.login_runtime); + if runtime + .active + .as_ref() + .is_some_and(|(active, _)| *active == generation) + { + runtime.active = None; + } + } + + pub fn native_abort_login(&self) -> Result<(), Error> { + let active = mutex(&self.inner.login_runtime).active.take(); + if let Some((_, source)) = active { + source.cancel(); + } + self.update_login_status(LoginStatus::Failed, "Abort Requested".to_owned()); + Ok(()) + } + + pub fn native_begin_login(&self, login_params: LoginParams) -> Result<(), Error> { + let (generation, source) = self.reserve_login(None)?; + let manager = Self { + login_response_data: Some(self.inner.login_response.clone()), + simulators: self.simulators.clone(), + inner: Arc::clone(&self.inner), + }; + thread::Builder::new() + .name("libremetaverse-login".to_owned()) + .spawn(move || { + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build(); + let result = match runtime { + Ok(runtime) => { + runtime.block_on(manager.perform_login(login_params, source.token())) + } + Err(_) => Err(Error::InvalidOperation), + }; + if let Err(error) = result { + manager.login_transport_failure(&error); + } + manager.release_login(generation); + }) + .map_err(|_| { + self.release_login(generation); + Error::InvalidOperation + })?; + Ok(()) + } + + pub async fn native_login( + &self, + login_params: LoginParams, + cancellation_token: Option, + ) -> Result { + Ok(self + .native_login_response_async(login_params, cancellation_token) + .await? + .is_some_and(|response| response.success())) + } + + pub async fn native_login_response_async( + &self, + login_params: LoginParams, + cancellation_token: Option, + ) -> Result, Error> { + let timeout = positive_millisecond_duration(login_params.timeout); + let (generation, source) = self.reserve_login(cancellation_token)?; + let token = source.token(); + let result = tokio::select! { + result = tokio::time::timeout(timeout, self.perform_login(login_params, token.clone())) => { + if let Ok(result) = result { + result + } else { + source.cancel(); + self.set_login_error("timeout"); + self.update_login_status(LoginStatus::Failed, "Login timed out".to_owned()); + Ok(None) + } + } + () = token.cancelled() => { + self.set_login_error("canceled"); + self.update_login_status(LoginStatus::Failed, "Canceled".to_owned()); + Ok(None) + } + }; + self.release_login(generation); + match result { + Ok(value) => Ok(value), + Err(error) => { + self.login_transport_failure(&error); + Ok(None) + } + } + } + + pub async fn native_login_credential( + &self, + credential: LoginCredential, + channel: String, + start: Option, + version: String, + cancellation_token: Option, + ) -> Result { + let mut login_params = LoginParams::new_with_grid_client_login_credential_string_string( + self.inner.client.clone(), + credential, + channel, + version, + )?; + if let Some(start) = start { + login_params.start = start; + } + self.native_login(login_params, cancellation_token).await + } + + pub async fn native_login_credential_response( + &self, + credential: LoginCredential, + channel: String, + start: Option, + version: String, + cancellation_token: Option, + ) -> Result, Error> { + let mut login_params = LoginParams::new_with_grid_client_login_credential_string_string( + self.inner.client.clone(), + credential, + channel, + version, + )?; + if let Some(start) = start { + login_params.start = start; + } + self.native_login_response_async(login_params, cancellation_token) + .await + } + + pub async fn native_login_strings( + &self, + first_name: String, + last_name: String, + password: String, + channel: String, + start: String, + version: String, + cancellation_token: Option, + ) -> Result { + let mut params = + self.native_default_login_params(first_name, last_name, password, channel, version)?; + params.start = start; + self.native_login(params, cancellation_token).await + } + + pub async fn native_login_strings_response( + &self, + first_name: String, + last_name: String, + password: String, + channel: String, + start: String, + version: String, + cancellation_token: Option, + ) -> Result, Error> { + let mut params = + self.native_default_login_params(first_name, last_name, password, channel, version)?; + params.start = start; + self.native_login_response_async(params, cancellation_token) + .await + } + + async fn perform_login( + &self, + mut params: LoginParams, + cancellation_token: CancellationToken, + ) -> Result, Error> { + if params.password.len() != 35 && !params.password.starts_with("$1$") { + params.password = libremetaverse_types::Utils::md5_with_string(params.password)?; + } + if params.channel.trim().is_empty() { + params.channel = crate::Settings::user_agent(); + } + if params.version.trim().is_empty() { + params.version = String::from("?.?.?"); + } + let wire_secrets = LoginWireSecrets { + password: std::mem::take(&mut params.password), + token: std::mem::take(&mut params.token), + mfa_hash: std::mem::take(&mut params.mfa_hash), + }; + params.clear_secrets(); + let requested_start = if params.login_location.trim().is_empty() { + params.start.clone() + } else { + params.login_location.clone() + }; + params.start = crate::login::normalize_start(&requested_start)?; + + let callback_options: Vec = mutex(&self.inner.login_runtime) + .response_callbacks + .iter() + .filter_map(|(_, options)| options.as_ref()) + .flatten() + .cloned() + .collect(); + for option in callback_options { + if !params.options.contains(&option) { + params.options.push(option); + } + } + + let mut redirects = 0_usize; + loop { + cancellation_token.throw_if_cancellation_requested()?; + let login_uri = validate_login_uri(¶ms.uri)?; + self.update_login_status( + LoginStatus::ConnectingToLogin, + format!( + "Logging in as {} {}...", + params.first_name, params.last_name + ), + ); + let request = login_request(¶ms, &wire_secrets); + let response = self + .inner + .client + .native_http_caps_client() + .post_with_uri_osd_format_osd_cancellation_token_i_progress( + login_uri, + OSDFormat::Xml, + request, + cancellation_token.clone(), + None, + ) + .await; + let (http_response, response_data) = match response { + Ok(response) => response, + Err(Error::Cancelled) => { + self.set_login_error("canceled"); + self.update_login_status(LoginStatus::Failed, "Login canceled".to_owned()); + return Ok(None); + } + Err(error) => { + self.login_transport_failure(&error); + return Ok(None); + } + }; + if !http_response.is_success_status_code() { + self.set_login_error("bad response"); + self.update_login_status( + LoginStatus::Failed, + format!("Login service returned HTTP {}", http_response.status_code), + ); + return Ok(None); + } + self.update_login_status( + LoginStatus::ReadingResponse, + "Reading login response...".to_owned(), + ); + let parsed = match OSDParser::deserialize_with_bytes(response_data) { + Ok(OSD::Map(map)) => OSDMap::new_with_dictionary(map), + _ => Err(Error::Argument), + }; + let Ok(parsed) = parsed else { + self.set_login_error("bad response"); + self.update_login_status( + LoginStatus::Failed, + "Empty or corrupt login response".to_owned(), + ); + return Ok(None); + }; + let has_login = parsed.get("login").is_some(); + self.inner.login_response.parse_with_osd_map(parsed)?; + let response = self.inner.login_response.clone(); + if !has_login { + self.update_login_status( + LoginStatus::Failed, + "Login parameter missing in the response".to_owned(), + ); + return Ok(None); + } + match response.login() { + crate::LoginState::Indeterminate => { + redirects += 1; + if redirects > 10 { + self.set_login_error("redirect loop"); + self.update_login_status( + LoginStatus::Failed, + "Too many login redirects".to_owned(), + ); + return Ok(None); + } + self.update_login_status(LoginStatus::Redirecting, response.message()); + params.uri = response.next_url(); + validate_login_uri(¶ms.uri)?; + let delay = Duration::from_secs( + u64::try_from(response.next_duration().clamp(0, 300)).unwrap_or(0), + ); + tokio::select! { + () = tokio::time::sleep(delay) => {} + () = cancellation_token.cancelled() => { + self.set_login_error("canceled"); + self.update_login_status(LoginStatus::Failed, "Login canceled".to_owned()); + return Ok(None); + } + } + } + crate::LoginState::True => { + self.invoke_login_response(true, false, &response); + if response.seed_capability().is_empty() { + self.set_login_error("bad response"); + self.update_login_status( + LoginStatus::Failed, + "Login response did not include a seed capability".to_owned(), + ); + return Ok(None); + } + if self.initialize_login_response( + response.clone(), + response.message(), + "Unable to establish a UDP connection to the simulator", + "Login server did not return a simulator address", + )? { + return Ok(Some(response)); + } + return Ok(None); + } + crate::LoginState::False => { + let reason = response.reason(); + self.set_login_error(if reason.is_empty() { + "unknown" + } else { + &reason + }); + self.update_login_status(LoginStatus::Failed, response.message()); + return Ok(None); + } + } + } + } + + fn invoke_login_response(&self, success: bool, redirect: bool, response: &LoginResponseData) { + let callbacks: Vec<_> = mutex(&self.inner.login_runtime) + .response_callbacks + .iter() + .map(|(callback, _)| callback.clone()) + .collect(); + for callback in callbacks { + let _ = callback.invoke( + success, + redirect, + response.message(), + response.reason(), + Some(response.clone()), + ); + } + } + + fn login_transport_failure(&self, error: &Error) { + let (key, message) = match error { + Error::Cancelled => ("canceled", "Login canceled"), + Error::HttpRequest => ("no connection", "Unable to contact login service"), + Error::Argument => ("bad response", "Invalid login request or response"), + _ => ("no connection", "Login failed"), + }; + self.set_login_error(key); + self.update_login_status(LoginStatus::Failed, message.to_owned()); + } + + pub fn native_login_response(&self, response: LoginResponseData) -> Result { + self.initialize_login_response( + response, + "Login success".to_owned(), + "Unable to connect to simulator", + "Unable to connect to simulator", + ) + } + + fn initialize_login_response( + &self, + response: LoginResponseData, + success_message: String, + connect_failure_message: &str, + address_failure_message: &str, + ) -> Result { + self.inner.login_response.copy_from(&response); + self.inner + .circuit_code + .store(response.circuit_code().cast_unsigned(), Ordering::Release); + *write(&self.inner.agent_id) = response.agent_id(); + *write(&self.inner.session_id) = response.session_id(); + let seed = if response.seed_capability().is_empty() { + None + } else { + Some(validate_login_uri(&response.seed_capability())?) + }; + write(&self.inner.login_seed_capability).clone_from(&seed); + self.update_login_status( + LoginStatus::ConnectingToSim, + "Connecting to simulator...".to_owned(), + ); + let Some(ip) = response.sim_ip() else { + self.set_login_error("bad response"); + self.update_login_status(LoginStatus::Failed, address_failure_message.to_owned()); + return Ok(false); + }; + if response.sim_port() == 0 { + self.set_login_error("bad response"); + self.update_login_status(LoginStatus::Failed, address_failure_message.to_owned()); + return Ok(false); + } + let handle = (u64::from(response.region_x()) << 32) | u64::from(response.region_y()); + let endpoint = std::net::SocketAddr::new(ip, response.sim_port()); + let Ok(connected) = self.native_connect( + endpoint, + handle, + true, + seed, + response.region_size_x(), + response.region_size_y(), + ) else { + self.cleanup_failed_login_connection(endpoint); + self.set_login_error("no connection"); + self.update_login_status(LoginStatus::Failed, connect_failure_message.to_owned()); + return Ok(false); + }; + if let Some(simulator) = connected { + let economy_request = + Packet::build_packet_with_packet_type(PacketType::EconomyDataRequest); + if economy_request + .and_then(|packet| simulator.native_send_packet(packet)) + .is_err() + { + self.cleanup_failed_login_connection(endpoint); + self.set_login_error("no connection"); + self.update_login_status(LoginStatus::Failed, connect_failure_message.to_owned()); + return Ok(false); + } + self.update_login_status(LoginStatus::Success, success_message); + Ok(true) + } else { + self.cleanup_failed_login_connection(endpoint); + self.update_login_status(LoginStatus::Failed, connect_failure_message.to_owned()); + Ok(false) + } + } + + fn cleanup_failed_login_connection(&self, endpoint: std::net::SocketAddr) { + if let Some(simulator) = self.native_find_endpoint(endpoint) { + let _ = simulator.native_disconnect(false); + self.simulators.remove(&simulator); + let mut current = write(&self.inner.current_sim); + if current.as_ref() == Some(&simulator) { + *current = None; + } + } + if self.simulators.is_empty() { + self.inner.connected.store(false, Ordering::Release); + } + } + pub fn native_subscribe_disconnected( &self, handler: EventHandler, @@ -2299,7 +3037,7 @@ impl NetworkManager { size_y: u32, ) -> Result, Error> { let manager = Self { - login_response_data: None, + login_response_data: Some(self.inner.login_response.clone()), simulators: self.simulators.clone(), inner: Arc::clone(&self.inner), }; diff --git a/crates/libremetaverse/tests/network_manager.rs b/crates/libremetaverse/tests/network_manager.rs index 78029fb..aac9fc9 100644 --- a/crates/libremetaverse/tests/network_manager.rs +++ b/crates/libremetaverse/tests/network_manager.rs @@ -7,10 +7,16 @@ use libremetaverse::packets::{ RegionHandshakePacket, RegionHandshakeReplyPacket, StartPingCheckPacket, UseCircuitCodePacket, }; use libremetaverse::{ - CapsEventDictionary, CapsEventQueueCallback, GridClient, Helpers, NetworkManager, - NetworkManagerDisconnectType, PacketEventDictionary, Simulator, + CapsEventDictionary, CapsEventQueueCallback, GridClient, Helpers, HttpCapsClient, LoginState, + LoginStatus, NetworkManager, NetworkManagerDisconnectType, NetworkManagerLoginResponseCallback, + PacketEventDictionary, Simulator, }; -use libremetaverse_types::compat::{EventHandler, Uri}; +use libremetaverse_structured_data::{OSD, OSDParser}; +use libremetaverse_types::compat::{ + CancellationTokenSource, EventHandler, HttpMessageHandler, HttpRequest, HttpResponse, Uri, +}; +use libremetaverse_types::{UUID, Utils}; +use std::collections::{BTreeMap, HashMap}; use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::mpsc::{Receiver, Sender, channel}; @@ -204,6 +210,35 @@ fn synchronous_specific_dispatch_suppresses_default_async_chain_like_reference() ); } +#[test] +fn abort_and_callback_unregistration_preserve_reference_noop_behavior() { + let client = GridClient::new().unwrap(); + let manager = client.network(); + let progress = Arc::new(Mutex::new(Vec::new())); + let observed = Arc::clone(&progress); + let _guard = manager.subscribe_login_progress(Arc::new(move |args| { + observed.lock().unwrap().push(args); + })); + let callback = NetworkManagerLoginResponseCallback::from_handler(|_, _, _, _, _| {}); + manager + .register_login_response_callback_with_login_response_callback(callback.clone()) + .unwrap(); + manager + .unregister_login_response_callback(callback.clone()) + .unwrap(); + manager + .unregister_login_response_callback(callback) + .unwrap(); + + manager.abort_login().unwrap(); + assert_eq!(manager.login_status_code(), LoginStatus::Failed); + assert_eq!(manager.login_message(), "Abort Requested"); + let progress = progress.lock().unwrap(); + assert_eq!(progress.len(), 1); + assert_eq!(progress[0].status(), LoginStatus::Failed); + assert_eq!(progress[0].message(), "Abort Requested"); +} + struct TestCapsMessage; impl IMessage for TestCapsMessage {} @@ -319,6 +354,7 @@ fn sim_connecting_can_cancel_without_leaking_collection_state() { enum ServerCommand { Ping, Disable, + ExpectEconomy, Stop, } @@ -327,6 +363,7 @@ enum ServerReport { Ack(u32), Circuit(u32), HandshakeReply(u32), + Economy, Ping(u8), } @@ -472,6 +509,13 @@ fn spawn_fake_server() -> FakeServer { bytes[0] &= !(Helpers::MSG_RELIABLE | Helpers::MSG_ZEROCODED); socket.send_to(&bytes, client_endpoint).unwrap(); } + ServerCommand::ExpectEconomy => loop { + let (length, _) = socket.recv_from(&mut buffer).expect("EconomyDataRequest"); + if packet_type(&buffer[..length]) == Some(PacketType::EconomyDataRequest) { + report_sender.send(ServerReport::Economy).unwrap(); + break; + } + }, ServerCommand::Stop => break, } } @@ -620,7 +664,6 @@ fn fake_server_drives_circuit_handshake_ping_disable_and_disconnect_reasons() { server.reports.recv_timeout(Duration::from_secs(1)).unwrap(), ServerReport::HandshakeReply(0x1 | 0x2 | 0x4) ); - assert_reuse_preserves_connected_override(&mut manager, &server, &sim); assert_ping_exchange(&server, &packet_receiver); @@ -897,3 +940,396 @@ fn handshake_timeout_preserves_reference_connect_result_and_shutdown_tears_down_ assert_eq!(sim_disconnects.load(Ordering::Relaxed), 1); drop(guard); } + +fn login_http_response(body: OSD) -> HttpResponse { + let body = OSDParser::serialize_llsd_xml_bytes(body).expect("serialize LLSD"); + HttpResponse { + status_code: 200, + headers: BTreeMap::from([("Content-Length".into(), body.len().to_string())]), + content_type: Some("application/llsd+xml".into()), + body, + } +} + +fn successful_login_response(endpoint: SocketAddr, seed: &str) -> OSD { + OSD::Map(HashMap::from([ + ("login".into(), OSD::Boolean(true)), + ("message".into(), OSD::String("Welcome".into())), + ( + "agent_id".into(), + OSD::UUID( + UUID::new_with_string("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa".into()).unwrap(), + ), + ), + ( + "session_id".into(), + OSD::UUID( + UUID::new_with_string("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb".into()).unwrap(), + ), + ), + ( + "secure_session_id".into(), + OSD::UUID( + UUID::new_with_string("cccccccc-cccc-cccc-cccc-cccccccccccc".into()).unwrap(), + ), + ), + ("circuit_code".into(), OSD::Integer(0x1234)), + ("region_x".into(), OSD::Integer(1000)), + ("region_y".into(), OSD::Integer(2000)), + ("region_size_x".into(), OSD::Integer(256)), + ("region_size_y".into(), OSD::Integer(256)), + ("sim_ip".into(), OSD::String(endpoint.ip().to_string())), + ("sim_port".into(), OSD::Integer(i32::from(endpoint.port()))), + ("seed_capability".into(), OSD::String(seed.into())), + ( + "account_level_benefits".into(), + OSD::Map(HashMap::from([( + "group_membership_limit".into(), + OSD::Integer(70), + )])), + ), + ])) +} + +#[tokio::test(flavor = "current_thread")] +async fn login_hashes_credentials_parses_response_and_hands_off_session_to_udp_and_caps() { + let server = spawn_fake_server(); + let requests = Arc::new(Mutex::new(Vec::::new())); + let recorded = Arc::clone(&requests); + let endpoint = server.endpoint; + let handler = HttpMessageHandler::new(move |request, _| { + let recorded = Arc::clone(&recorded); + async move { + let response = if request.uri.0.contains("/login") { + login_http_response(successful_login_response( + endpoint, + "http://caps.test/seed?token=secret", + )) + } else { + login_http_response(OSD::Map(HashMap::new())) + }; + recorded.lock().unwrap().push(request); + response + } + }); + let mut client = GridClient::new().unwrap(); + client.settings().timing().login_timeout = 2_000; + client.settings().connection_mut().mfa_enabled = true; + client.set_http_caps_client(HttpCapsClient::new(handler).unwrap()); + let manager = client.network(); + let callback_calls = Arc::new(AtomicUsize::new(0)); + let observed_calls = Arc::clone(&callback_calls); + let callback = NetworkManagerLoginResponseCallback::from_handler( + move |success, redirect, message, reason, response| { + assert!(success); + assert!(!redirect); + assert_eq!(message, "Welcome"); + assert!(reason.is_empty()); + assert!(response.is_some_and(|value| value.success())); + observed_calls.fetch_add(1, Ordering::Relaxed); + }, + ); + manager + .register_login_response_callback_with_login_response_callback_string_array( + callback, + Some(vec!["custom-login-option".into()]), + ) + .unwrap(); + let progress = Arc::new(Mutex::new(Vec::new())); + let observed = Arc::clone(&progress); + let _progress_guard = manager.subscribe_login_progress(Arc::new(move |args| { + observed.lock().unwrap().push(args.status()); + })); + let mut params = manager + .default_login_params( + "Ada".into(), + "Lovelace".into(), + "plain-secret".into(), + "MetaCrate".into(), + "1.0".into(), + ) + .unwrap(); + params.uri = "http://login.test/login".into(); + params.token = "123456".into(); + params.mfa_hash = "saved-mfa-hash".into(); + + let response = manager + .login_with_response_with_login_params_cancellation_token(params, None) + .await + .unwrap() + .expect("successful response"); + assert_eq!(response.login(), LoginState::True); + assert_eq!(manager.login_status_code(), LoginStatus::Success); + assert_eq!(manager.login_message(), "Welcome"); + assert_eq!(manager.max_agent_groups(), 70); + assert_eq!(callback_calls.load(Ordering::Relaxed), 1); + assert!(manager.connected()); + assert!(manager.current_sim().is_some()); + assert_eq!( + manager.login_seed_capability(), + Some(Uri("http://caps.test/seed?token=secret".into())) + ); + assert_eq!( + server.reports.recv_timeout(Duration::from_secs(1)).unwrap(), + ServerReport::Circuit(0x1234) + ); + assert_eq!( + server.reports.recv_timeout(Duration::from_secs(1)).unwrap(), + ServerReport::HandshakeReply(0x1 | 0x2 | 0x4) + ); + server.commands.send(ServerCommand::ExpectEconomy).unwrap(); + assert_eq!( + server.reports.recv_timeout(Duration::from_secs(1)).unwrap(), + ServerReport::Economy + ); + + let login_request = requests + .lock() + .unwrap() + .iter() + .find(|request| request.uri.0.contains("/login")) + .cloned() + .expect("login request"); + let request = OSDParser::deserialize_with_bytes(login_request.body).unwrap(); + let OSD::Map(request) = request else { + panic!("login request must be an LLSD map"); + }; + assert_eq!( + request["passwd"].as_string().unwrap(), + Utils::md5_with_string("plain-secret".into()).unwrap() + ); + assert_eq!(request["token"].as_string().unwrap(), "123456"); + assert_eq!(request["mfa_hash"].as_string().unwrap(), "saved-mfa-hash"); + assert_eq!(request["start"].as_string().unwrap(), "last"); + assert_eq!(request["channel"].as_string().unwrap(), "MetaCrate"); + assert_eq!(request["version"].as_string().unwrap(), "1.0"); + assert_eq!( + request["platform"].as_string().unwrap(), + NetworkManager::get_platform().unwrap() + ); + assert!(!request["platform_version"].as_string().unwrap().is_empty()); + assert_eq!( + request["mac"].as_string().unwrap(), + request["id0"].as_string().unwrap() + ); + let OSD::Array(options) = &request["options"] else { + panic!("login options must be an LLSD array"); + }; + assert!( + options + .iter() + .any(|option| option.as_string().as_deref() == Ok("custom-login-option")) + ); + assert_eq!( + *progress.lock().unwrap(), + [ + LoginStatus::ConnectingToLogin, + LoginStatus::ReadingResponse, + LoginStatus::ConnectingToSim, + LoginStatus::Success, + ] + ); + manager + .shutdown_with_disconnect_type(NetworkManagerDisconnectType::ClientInitiated) + .unwrap(); +} + +#[tokio::test(flavor = "current_thread")] +async fn redirect_reuses_hashed_credentials_and_failure_is_typed_without_partial_session() { + let requests = Arc::new(Mutex::new(Vec::::new())); + let recorded = Arc::clone(&requests); + let calls = Arc::new(AtomicUsize::new(0)); + let handler_calls = Arc::clone(&calls); + let handler = HttpMessageHandler::new(move |request, _| { + let recorded = Arc::clone(&recorded); + let index = handler_calls.fetch_add(1, Ordering::Relaxed); + async move { + recorded.lock().unwrap().push(request); + if index == 0 { + login_http_response(OSD::Map(HashMap::from([ + ("login".into(), OSD::String("indeterminate".into())), + ( + "next_url".into(), + OSD::String("http://login.test/redirected".into()), + ), + ("next_duration".into(), OSD::Integer(0)), + ("message".into(), OSD::String("Redirecting".into())), + ]))) + } else { + login_http_response(OSD::Map(HashMap::from([ + ("login".into(), OSD::Boolean(false)), + ("reason".into(), OSD::String("key".into())), + ("message".into(), OSD::String("Denied".into())), + ]))) + } + } + }); + let mut client = GridClient::new().unwrap(); + client.settings().timing().login_timeout = 1_000; + client.set_http_caps_client(HttpCapsClient::new(handler).unwrap()); + let manager = client.network(); + let callback_calls = Arc::new(AtomicUsize::new(0)); + let observed_calls = Arc::clone(&callback_calls); + manager + .register_login_response_callback_with_login_response_callback( + NetworkManagerLoginResponseCallback::from_handler(move |_, _, _, _, _| { + observed_calls.fetch_add(1, Ordering::Relaxed); + }), + ) + .unwrap(); + let mut params = manager + .default_login_params( + "Ada".into(), + "Lovelace".into(), + "plain-secret".into(), + "MetaCrate".into(), + "1.0".into(), + ) + .unwrap(); + params.uri = "http://login.test/login".into(); + + assert!( + !manager + .login_with_login_params_cancellation_token(params, None) + .await + .unwrap() + ); + assert_eq!(manager.login_status_code(), LoginStatus::Failed); + assert_eq!(manager.login_error_key(), "key"); + assert_eq!(manager.login_message(), "Denied"); + assert!(!manager.connected()); + assert!(manager.current_sim().is_none()); + assert_eq!( + callback_calls.load(Ordering::Relaxed), + 0, + "the C# callback only runs for a successful login" + ); + let passwords: Vec<_> = requests + .lock() + .unwrap() + .iter() + .map(|request| { + let OSD::Map(map) = OSDParser::deserialize_with_bytes(request.body.clone()).unwrap() + else { + panic!("request must be map"); + }; + map["passwd"].as_string().unwrap() + }) + .collect(); + assert_eq!(passwords.len(), 2); + assert_eq!(passwords[0], passwords[1]); + assert_ne!(passwords[0], "plain-secret"); +} + +#[tokio::test(flavor = "current_thread")] +async fn successful_authentication_with_invalid_simulator_clears_partial_session() { + let handler = HttpMessageHandler::new(|_, _| async move { + let mut response = successful_login_response( + "127.0.0.1:1".parse().unwrap(), + "http://caps.test/seed?token=must-not-survive", + ); + let OSD::Map(ref mut fields) = response else { + unreachable!(); + }; + fields.insert("sim_port".into(), OSD::Integer(0)); + login_http_response(response) + }); + let mut client = GridClient::new().unwrap(); + client.settings().timing().login_timeout = 1_000; + client.set_http_caps_client(HttpCapsClient::new(handler).unwrap()); + let manager = client.network(); + let mut params = manager + .default_login_params( + "Ada".into(), + "Lovelace".into(), + "plain-secret".into(), + "MetaCrate".into(), + "1.0".into(), + ) + .unwrap(); + params.uri = "http://login.test/login".into(); + + assert!( + !manager + .login_with_login_params_cancellation_token(params, None) + .await + .unwrap() + ); + assert_eq!(manager.login_status_code(), LoginStatus::Failed); + assert!(manager.current_sim().is_none()); + assert!(manager.simulators.is_empty()); + assert!(!manager.connected()); + assert!(manager.login_seed_capability().is_none()); + let response = manager.login_response_data.as_ref().unwrap(); + assert_eq!(response.session_id(), UUID::zero()); + assert_eq!(response.secure_session_id(), UUID::zero()); + assert!(response.seed_capability().is_empty()); + assert!(response.account_level_benefits().is_none()); +} + +#[tokio::test(flavor = "current_thread", start_paused = true)] +async fn login_timeout_is_typed_and_leaves_no_partial_session() { + let handler = + HttpMessageHandler::new(|_, _| async move { std::future::pending::().await }); + let mut client = GridClient::new().unwrap(); + client.settings().timing().login_timeout = 25; + client.set_http_caps_client(HttpCapsClient::new(handler).unwrap()); + let manager = client.network(); + let mut params = manager + .default_login_params( + "Ada".into(), + "Lovelace".into(), + "plain-secret".into(), + "MetaCrate".into(), + "1.0".into(), + ) + .unwrap(); + params.uri = "http://login.test/login".into(); + + assert!( + !manager + .login_with_login_params_cancellation_token(params, None) + .await + .unwrap() + ); + assert_eq!(manager.login_status_code(), LoginStatus::Failed); + assert_eq!(manager.login_error_key(), "timeout"); + assert_eq!(manager.login_message(), "Login timed out"); + assert!(manager.current_sim().is_none()); + assert!(manager.login_seed_capability().is_none()); +} + +#[tokio::test(flavor = "current_thread")] +async fn canceled_login_finishes_failed_without_retaining_simulator_work() { + let handler = HttpMessageHandler::new(|_, cancellation| async move { + cancellation.cancelled().await; + login_http_response(OSD::Map(HashMap::new())) + }); + let mut client = GridClient::new().unwrap(); + client.settings().timing().login_timeout = 1_000; + client.set_http_caps_client(HttpCapsClient::new(handler).unwrap()); + let manager = client.network(); + let mut params = manager + .default_login_params( + "Ada".into(), + "Lovelace".into(), + "plain-secret".into(), + "MetaCrate".into(), + "1.0".into(), + ) + .unwrap(); + params.uri = "http://login.test/login".into(); + let cancellation = CancellationTokenSource::new(); + cancellation.cancel(); + + assert!( + !manager + .login_with_login_params_cancellation_token(params, Some(cancellation.token())) + .await + .unwrap() + ); + assert_eq!(manager.login_status_code(), LoginStatus::Failed); + assert_eq!(manager.login_error_key(), "canceled"); + assert!(!manager.connected()); + assert!(manager.simulators.is_empty()); +} diff --git a/docs/network-manager.md b/docs/network-manager.md index 9141174..f904e8c 100644 --- a/docs/network-manager.md +++ b/docs/network-manager.md @@ -66,14 +66,32 @@ the last simulator reports `SimShutdown`. Explicit shutdown preserves the caller-provided reason/message and sends `CloseCircuit` only for client or network-timeout shutdowns. +## Login behavior + +The native login path constructs the reference LLSD request, including the C# +MD5 password form, token/MFA fields, viewer identity, platform metadata, +requested options, and normalized home/last/region start locations. It uses the +client's injectable capability HTTP transport, limits redirects and server +delays, observes linked caller/client cancellation, and reports the mapped +`LoginStatus` transitions. Only a successful response invokes registered login +callbacks and proceeds to an ACK-gated initial simulator connection. + +`LoginResponseData` parses the session and circuit identifiers, home and look-at +vectors, simulator endpoint and dimensions, seed capability, inventory roots and +skeletons, buddy rights, account benefits, service URLs, category/config arrays, +premium packages, and initial outfit. A rejected, malformed, timed-out, or +canceled login leaves no simulator installed. Passwords, login tokens, MFA +hashes, session identifiers, seed capabilities, and estate access tokens are +excluded from `Debug` output and diagnostic messages. + The deterministic tests use loopback fake simulators and require no live grid: ```sh cargo test -p libremetaverse --test network_manager ``` -They cover ACK-gated circuit setup, typed handshake reply/state, ping response, -CAPS enable, UDP disable, current/seed selection, callback ordering and -filtering, RAII unregistration, runtime-neutral async calls, concurrent -registration/removal, reentrant/concurrent disconnect, and keepalive timeout -reasons. +They cover login hashing, redirects, cancellation and initial handoff alongside +ACK-gated circuit setup, typed handshake reply/state, ping response, CAPS enable, +UDP disable, current/seed selection, callback ordering and filtering, RAII +unregistration, runtime-neutral async calls, concurrent registration/removal, +reentrant/concurrent disconnect, and keepalive timeout reasons. diff --git a/tests/api-compile/Cargo.lock b/tests/api-compile/Cargo.lock index ca3ac6b..9ec3c1d 100644 --- a/tests/api-compile/Cargo.lock +++ b/tests/api-compile/Cargo.lock @@ -8,12 +8,27 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + [[package]] name = "aws-lc-rs" version = "1.18.0" @@ -73,6 +88,15 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + [[package]] name = "bumpalo" version = "3.20.3" @@ -103,6 +127,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "cfg_aliases" version = "0.2.2" @@ -228,6 +258,16 @@ dependencies = [ "crypto-common 0.2.2", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.7" @@ -606,7 +646,7 @@ dependencies = [ "jni-sys", "log", "simd_cesu8", - "thiserror", + "thiserror 2.0.20", "walkdir", "windows-link", ] @@ -680,6 +720,8 @@ dependencies = [ "libremetaverse-imaging", "libremetaverse-structured-data", "libremetaverse-types", + "mac_address2", + "os_info", "reqwest", "roxmltree", "tokio", @@ -809,6 +851,18 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "mac_address2" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc5e04aa39a4dc96076bca67bf60b63201ff06f9568e62f7904293d39de8cb0b" +dependencies = [ + "nix 0.28.0", + "thiserror 1.0.69", + "widestring", + "windows-sys 0.52.0", +] + [[package]] name = "md-5" version = "0.10.6" @@ -825,6 +879,15 @@ version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "metacrate-api-compile" version = "0.0.0" @@ -865,6 +928,190 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases 0.1.1", + "libc", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases 0.2.2", + "libc", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -877,6 +1124,21 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "os_info" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" +dependencies = [ + "android_system_properties", + "log", + "nix 0.31.3", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "windows-sys 0.61.2", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -920,14 +1182,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", - "cfg_aliases", + "cfg_aliases 0.2.2", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash", "rustls", "socket2", - "thiserror", + "thiserror 2.0.20", "tokio", "tracing", "web-time", @@ -950,7 +1212,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.20", "tinyvec", "tracing", "web-time", @@ -962,7 +1224,7 @@ version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ - "cfg_aliases", + "cfg_aliases 0.2.2", "libc", "once_cell", "socket2", @@ -1383,13 +1645,33 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.20", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] @@ -1722,6 +2004,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + [[package]] name = "winapi-util" version = "0.1.11" diff --git a/tests/compat/tests/core_runtime_shims.rs b/tests/compat/tests/core_runtime_shims.rs index bdd1df6..b0f8c0e 100644 --- a/tests/compat/tests/core_runtime_shims.rs +++ b/tests/compat/tests/core_runtime_shims.rs @@ -7,14 +7,6 @@ use libremetaverse::{ }; use libremetaverse_types::compat::{CancellationToken, EventHandler, IProgress, Subscription, Uri}; -fn member_id(result: Result) -> &'static str { - result - .err() - .expect("failure-only shim unexpectedly returned a value") - .csharp_member() - .expect("failure-only shim error") -} - async fn compile_login_shutdown_flows( network: &NetworkManager, limiter: &CapsRateLimiter, @@ -102,10 +94,7 @@ fn client_and_settings_construct_without_starting_runtime_work() { libremetaverse::ClientLifecycleState::Disposed ); - assert_eq!( - member_id(CapsRateLimiter::new_with_constructor()), - "M:LibreMetaverse.CapsRateLimiter.#ctor" - ); + let _limiter = CapsRateLimiter::new_with_constructor().expect("CAPS rate limiter"); let udp_buffer = UDPPacketBuffer::new_with_constructor().expect("UDP packet buffer"); assert_eq!(udp_buffer.data.len(), 4096); assert_eq!(udp_buffer.data_length, 0); diff --git a/tools/generate_api_shims.py b/tools/generate_api_shims.py index a49d340..9f2dbb7 100644 --- a/tools/generate_api_shims.py +++ b/tools/generate_api_shims.py @@ -38,6 +38,7 @@ TARGETS = { # implementations. The generated module keeps catalog markers and re-exports # the hand-written type so coverage remains deterministic. NATIVE_TYPES = { + "T:LibreMetaverse.AccountLevelBenefits": "crate::login::AccountLevelBenefits", "T:LibreMetaverse.AgentDisplayName": "crate::message_decoder::AgentDisplayName", "T:LibreMetaverse.CapabilitiesReceivedEventArgs": "crate::caps::CapabilitiesReceivedEventArgs", "T:LibreMetaverse.Caps": "crate::caps::Caps", @@ -50,6 +51,13 @@ NATIVE_TYPES = { "T:LibreMetaverse.Http.EventQueueClient.EventCallback": "crate::event_queue::EventQueueClientEventCallback", "T:LibreMetaverse.HttpCapsClient": "crate::caps_http::HttpCapsClient", "T:LibreMetaverse.HttpCapsClient.ProgressReport": "crate::caps_http::HttpCapsClientProgressReport", + "T:LibreMetaverse.LoginCredential": "crate::login::LoginCredential", + "T:LibreMetaverse.LoginException": "crate::login::LoginException", + "T:LibreMetaverse.LoginParams": "crate::login::LoginParams", + "T:LibreMetaverse.LoginProgressEventArgs": "crate::login::LoginProgressEventArgs", + "T:LibreMetaverse.LoginResponseData": "crate::login::LoginResponseData", + "T:LibreMetaverse.LoginResponseData.ParseMessage": "crate::login::LoginResponseDataParseMessage", + "T:LibreMetaverse.LoginResponseData.ParseResult": "crate::login::LoginResponseDataParseResult", "T:LibreMetaverse.MediaEntry": "crate::message_decoder::MediaEntry", "T:LibreMetaverse.Caps.EventQueueCallback": "crate::network_manager::CapsEventQueueCallback", "T:LibreMetaverse.CapsEventDictionary": "crate::network_manager::CapsEventDictionary", @@ -58,6 +66,7 @@ NATIVE_TYPES = { "T:LibreMetaverse.GenericStreamingMessageEventArgs": "crate::network_manager::GenericStreamingMessageEventArgs", "T:LibreMetaverse.NetworkManager.IncomingPacket": "crate::network_manager::NetworkManagerIncomingPacket", "T:LibreMetaverse.NetworkManager.OutgoingPacket": "crate::network_manager::NetworkManagerOutgoingPacket", + "T:LibreMetaverse.NetworkManager.LoginResponseCallback": "crate::login::NetworkManagerLoginResponseCallback", "T:LibreMetaverse.PacketEventDictionary": "crate::network_manager::PacketEventDictionary", "T:LibreMetaverse.PacketReceivedEventArgs": "crate::network_manager::PacketReceivedEventArgs", "T:LibreMetaverse.PacketSentEventArgs": "crate::network_manager::PacketSentEventArgs", @@ -207,12 +216,18 @@ NATIVE_MEMBER_BODIES = { "self.native_http_caps_client()", "P:LibreMetaverse.GridClient.HttpCapsClient#set": "self.native_set_http_caps_client(value)", + "P:LibreMetaverse.GridClient.Network": + "self.native_network().unwrap_or_else(|_| panic!(\"failed to construct NetworkManager\"))", + "P:LibreMetaverse.GridClient.Network#set": + "self.native_set_network(value)", "E:LibreMetaverse.NetworkManager.Disconnected": "self.native_subscribe_disconnected(handler)", "E:LibreMetaverse.NetworkManager.EventQueueRunning": "self.native_subscribe_event_queue_running(handler)", "E:LibreMetaverse.NetworkManager.GenericStreamingMessage": "self.native_subscribe_generic_streaming_message(handler)", + "E:LibreMetaverse.NetworkManager.LoginProgress": + "self.native_subscribe_login_progress(handler)", "E:LibreMetaverse.NetworkManager.PacketSent": "self.native_subscribe_packet_sent(handler)", "E:LibreMetaverse.NetworkManager.SimChanged": @@ -225,6 +240,10 @@ NATIVE_MEMBER_BODIES = { "self.native_subscribe_sim_disconnected(handler)", "M:LibreMetaverse.NetworkManager.#ctor(LibreMetaverse.GridClient)": "crate::network_manager::NetworkManager::native_new(client)", + "M:LibreMetaverse.NetworkManager.AbortLogin": + "self.native_abort_login()", + "M:LibreMetaverse.NetworkManager.BeginLogin(LibreMetaverse.LoginParams)": + "self.native_begin_login(login_params)", "M:LibreMetaverse.NetworkManager.Connect(System.Net.IPAddress,System.UInt16,System.UInt64,System.Boolean,System.Uri)": "self.native_connect(std::net::SocketAddr::new(ip, port), handle, set_default, seedcaps, crate::network_manager::Simulator::DEFAULT_REGION_SIZE_X, crate::network_manager::Simulator::DEFAULT_REGION_SIZE_Y)", "M:LibreMetaverse.NetworkManager.Connect(System.Net.IPAddress,System.UInt16,System.UInt64,System.Boolean,System.Uri,System.UInt32,System.UInt32)": @@ -261,6 +280,74 @@ NATIVE_MEMBER_BODIES = { "crate::message_decoder::to_ip(osd)", "M:LibreMetaverse.NetworkManager.FindSimulator(System.UInt64)": "Ok(self.native_find_handle(handle))", + "M:LibreMetaverse.NetworkManager.DefaultLoginParams(System.String,System.String,System.String,System.String,System.String)": + "self.native_default_login_params(first_name, last_name, password, channel, version)", + "M:LibreMetaverse.NetworkManager.GetHashedMAC": + "crate::login::hashed_mac()", + "M:LibreMetaverse.NetworkManager.GetMAC": + "Ok(crate::login::mac_address())", + "M:LibreMetaverse.NetworkManager.GetPlatform": + "Ok(crate::login::platform())", + "M:LibreMetaverse.NetworkManager.GetPlatformVersion": + "Ok(crate::login::platform_version())", + "M:LibreMetaverse.NetworkManager.RefreshMac": + "Ok(crate::login::refresh_mac())", + "M:LibreMetaverse.NetworkManager.Login(LibreMetaverse.LoginResponseData)": + "self.native_login_response(response)", + "M:LibreMetaverse.NetworkManager.LoginAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.String,System.Threading.CancellationToken)": + "self.native_login_credential(credential, channel, Some(start), version, cancellation_token).await", + "M:LibreMetaverse.NetworkManager.LoginAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.Threading.CancellationToken)": + "self.native_login_credential(credential, channel, None, version, cancellation_token).await", + "M:LibreMetaverse.NetworkManager.LoginAsync(LibreMetaverse.LoginParams,System.Threading.CancellationToken)": + "self.native_login(login_params, cancellation_token).await", + "M:LibreMetaverse.NetworkManager.LoginAsync(System.String,System.String,System.String,System.String,System.String,System.String,System.Threading.CancellationToken)": + "self.native_login_strings(first_name, last_name, password, channel, start, version, cancellation_token).await", + "M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.String,System.Threading.CancellationToken)": + "self.native_login_credential_response(credential, channel, Some(start), version, cancellation_token).await", + "M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(LibreMetaverse.LoginCredential,System.String,System.String,System.Threading.CancellationToken)": + "self.native_login_credential_response(credential, channel, None, version, cancellation_token).await", + "M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(LibreMetaverse.LoginParams,System.Threading.CancellationToken)": + "self.native_login_response_async(login_params, cancellation_token).await", + "M:LibreMetaverse.NetworkManager.LoginWithResponseAsync(System.String,System.String,System.String,System.String,System.String,System.String,System.Threading.CancellationToken)": + "self.native_login_strings_response(first_name, last_name, password, channel, start, version, cancellation_token).await", + "M:LibreMetaverse.NetworkManager.RegisterLoginResponseCallback(LibreMetaverse.NetworkManager.LoginResponseCallback)": + "self.native_register_login_response_callback(callback, None)", + "M:LibreMetaverse.NetworkManager.RegisterLoginResponseCallback(LibreMetaverse.NetworkManager.LoginResponseCallback,System.String[])": + "self.native_register_login_response_callback(callback, options)", + "M:LibreMetaverse.NetworkManager.StartLocation(System.String,System.Int32,System.Int32,System.Int32)": + "Ok(crate::login::start_location(&sim, x, y, z))", + "M:LibreMetaverse.NetworkManager.UnregisterLoginResponseCallback(LibreMetaverse.NetworkManager.LoginResponseCallback)": + "self.native_unregister_login_response_callback(&callback)", + "M:LibreMetaverse.InventoryFolder.#ctor(LibreMetaverse.UUID)": + "Ok(Self { base: crate::InventoryBase { native_uuid: uuid, native_owner_id: libremetaverse_types::UUID::zero(), native_parent_uuid: libremetaverse_types::UUID::zero(), native_name: String::new() }, native_descendent_count: 0, native_preferred_type: libremetaverse_types::FolderType::None, native_version: Self::VERSION_UNKNOWN })", + "P:LibreMetaverse.InventoryBase.Name": + "self.native_name.clone()", + "P:LibreMetaverse.InventoryBase.Name#set": + "self.native_name = value", + "P:LibreMetaverse.InventoryBase.OwnerID": + "self.native_owner_id", + "P:LibreMetaverse.InventoryBase.OwnerID#set": + "self.native_owner_id = value", + "P:LibreMetaverse.InventoryBase.ParentUUID": + "self.native_parent_uuid", + "P:LibreMetaverse.InventoryBase.ParentUUID#set": + "self.native_parent_uuid = value", + "P:LibreMetaverse.InventoryBase.UUID": + "self.native_uuid", + "P:LibreMetaverse.InventoryBase.UUID#set": + "self.native_uuid = value", + "P:LibreMetaverse.InventoryFolder.DescendentCount": + "self.native_descendent_count", + "P:LibreMetaverse.InventoryFolder.DescendentCount#set": + "self.native_descendent_count = value", + "P:LibreMetaverse.InventoryFolder.PreferredType": + "self.native_preferred_type", + "P:LibreMetaverse.InventoryFolder.PreferredType#set": + "self.native_preferred_type = value", + "P:LibreMetaverse.InventoryFolder.Version": + "self.native_version", + "P:LibreMetaverse.InventoryFolder.Version#set": + "self.native_version = value", "M:LibreMetaverse.NetworkManager.RegisterCallback(LibreMetaverse.Packets.PacketType,System.EventHandler{LibreMetaverse.PacketReceivedEventArgs})": "self.native_register_callback(type_, callback, true)", "M:LibreMetaverse.NetworkManager.RegisterCallback(LibreMetaverse.Packets.PacketType,System.EventHandler{LibreMetaverse.PacketReceivedEventArgs},System.Boolean)": @@ -295,6 +382,28 @@ NATIVE_MEMBER_BODIES = { "self.native_set_current_sim(value)", "P:LibreMetaverse.NetworkManager.InboxCount": "self.native_inbox_count()", + "P:LibreMetaverse.NetworkManager.AccountLevelBenefits": + "self.native_account_level_benefits()", + "P:LibreMetaverse.NetworkManager.AgentAppearanceServiceURL": + "self.native_agent_appearance_service_url()", + "P:LibreMetaverse.NetworkManager.LoginErrorKey": + "self.native_login_error_key()", + "P:LibreMetaverse.NetworkManager.LoginErrorKey#set": + "self.native_set_login_error_key(value)", + "P:LibreMetaverse.NetworkManager.LoginMessage": + "self.native_login_message()", + "P:LibreMetaverse.NetworkManager.LoginMessage#set": + "self.native_set_login_message(value)", + "P:LibreMetaverse.NetworkManager.LoginSeedCapability": + "self.native_login_seed_capability()", + "P:LibreMetaverse.NetworkManager.LoginSeedCapability#set": + "self.native_set_login_seed_capability(value)", + "P:LibreMetaverse.NetworkManager.LoginStatusCode": + "self.native_login_status_code()", + "P:LibreMetaverse.NetworkManager.LoginStatusCode#set": + "self.native_set_login_status_code(value)", + "P:LibreMetaverse.NetworkManager.MaxAgentGroups": + "self.native_max_agent_groups()", "P:LibreMetaverse.NetworkManager.OutboxCount": "self.native_outbox_count()", "M:LibreMetaverse.Simulator.#ctor(LibreMetaverse.GridClient,System.Net.IPEndPoint,System.UInt64,System.UInt32,System.UInt32)": @@ -516,6 +625,8 @@ CLIENT_CORE_NATIVE_OWNERS = ( ) NETWORK_NATIVE_OWNERS = ( + "InventoryBase", + "InventoryFolder", "NetworkManager", "Simulator", ) @@ -906,6 +1017,17 @@ PRIVATE_LAYOUTS = { ], "T:LibreMetaverse.EmptyRemovalStrategy`1": [("keys", "Vec")], "T:LibreMetaverse.ExpiringCache`2": [("entries", "std::collections::HashMap")], + "T:LibreMetaverse.InventoryBase": [ + ("native_uuid", "libremetaverse_types::UUID"), + ("native_owner_id", "libremetaverse_types::UUID"), + ("native_parent_uuid", "libremetaverse_types::UUID"), + ("native_name", "String"), + ], + "T:LibreMetaverse.InventoryFolder": [ + ("native_descendent_count", "i32"), + ("native_preferred_type", "libremetaverse_types::FolderType"), + ("native_version", "i32"), + ], "T:LibreMetaverse.LruRemovalStrategy`1": [("keys", "Vec")], "T:LibreMetaverse.MruRemovalStrategy`1": [("keys", "Vec")], "T:LibreMetaverse.MultiValueDictionary`2": [("entries", "std::collections::HashMap>")], @@ -926,8 +1048,12 @@ PRIVATE_LAYOUTS = { VALUE_DERIVES = { "T:LibreMetaverse.AgentSettings": "Clone, Debug, Eq, PartialEq", "T:LibreMetaverse.AssetCacheSettings": "Clone, Debug, Eq, PartialEq", + "T:LibreMetaverse.BuddyListEntry": "Clone, Debug, Eq, PartialEq", "T:LibreMetaverse.ConnectionSettings": "Clone, Eq, PartialEq", "T:LibreMetaverse.LoggingSettings": "Clone, Debug, Eq, PartialEq", + "T:LibreMetaverse.HomeInfo": "Clone, Copy, Debug, PartialEq", + "T:LibreMetaverse.InventoryBase": "Clone, Debug", + "T:LibreMetaverse.InventoryFolder": "Clone, Debug", "T:LibreMetaverse.Matrix4": "Clone, Copy, Debug", "T:LibreMetaverse.Quaternion": "Clone, Copy, Debug", "T:LibreMetaverse.PacketSettings": "Clone, Debug, Eq, PartialEq", diff --git a/tools/generate_rust_mapping.py b/tools/generate_rust_mapping.py index e50da89..efd081f 100644 --- a/tools/generate_rust_mapping.py +++ b/tools/generate_rust_mapping.py @@ -992,6 +992,7 @@ def validate_generated_shims() -> None: "crate::genepool_catalog::", "crate::json_codec::", "crate::message_codec::", + "crate::message_decoder::", "crate::notation::", "crate::packet_catalog::", "crate::packet_wire::",