Implement native login and session initialization (#56)
All checks were successful
Native code generation / deterministic (push) Successful in 11m53s
Imaging and meshing gate / native (push) Successful in 4m3s
JPEG 2000 feature / linux (push) Successful in 2m33s
Native Rust workspace compile / compile (push) Successful in 4m3s
Skia feature / linux (push) Successful in 31m24s
All checks were successful
Native code generation / deterministic (push) Successful in 11m53s
Imaging and meshing gate / native (push) Successful in 4m3s
JPEG 2000 feature / linux (push) Successful in 2m33s
Native Rust workspace compile / compile (push) Successful in 4m3s
Skia feature / linux (push) Successful in 31m24s
This commit is contained in:
300
Cargo.lock
generated
300
Cargo.lock
generated
@@ -17,12 +17,27 @@ dependencies = [
|
|||||||
"memchr",
|
"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]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-lc-rs"
|
name = "aws-lc-rs"
|
||||||
version = "1.18.0"
|
version = "1.18.0"
|
||||||
@@ -102,6 +117,15 @@ dependencies = [
|
|||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block2"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
|
||||||
|
dependencies = [
|
||||||
|
"objc2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.20.3"
|
version = "3.20.3"
|
||||||
@@ -141,6 +165,12 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg_aliases"
|
name = "cfg_aliases"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -277,6 +307,16 @@ dependencies = [
|
|||||||
"crypto-common 0.2.2",
|
"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]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
@@ -724,7 +764,7 @@ dependencies = [
|
|||||||
"jni-sys",
|
"jni-sys",
|
||||||
"log",
|
"log",
|
||||||
"simd_cesu8",
|
"simd_cesu8",
|
||||||
"thiserror",
|
"thiserror 2.0.20",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
@@ -808,6 +848,8 @@ dependencies = [
|
|||||||
"libremetaverse-imaging",
|
"libremetaverse-imaging",
|
||||||
"libremetaverse-structured-data",
|
"libremetaverse-structured-data",
|
||||||
"libremetaverse-types",
|
"libremetaverse-types",
|
||||||
|
"mac_address2",
|
||||||
|
"os_info",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -997,6 +1039,18 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
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]]
|
[[package]]
|
||||||
name = "md-5"
|
name = "md-5"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -1013,6 +1067,15 @@ version = "2.8.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -1040,6 +1103,31 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"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]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
@@ -1050,6 +1138,165 @@ dependencies = [
|
|||||||
"minimal-lexical",
|
"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]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.4"
|
version = "1.21.4"
|
||||||
@@ -1062,6 +1309,21 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
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]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
@@ -1115,14 +1377,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8"
|
checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"cfg_aliases",
|
"cfg_aliases 0.2.2",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"quinn-proto",
|
"quinn-proto",
|
||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls",
|
"rustls",
|
||||||
"socket2",
|
"socket2",
|
||||||
"thiserror",
|
"thiserror 2.0.20",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-time",
|
"web-time",
|
||||||
@@ -1145,7 +1407,7 @@ dependencies = [
|
|||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"slab",
|
"slab",
|
||||||
"thiserror",
|
"thiserror 2.0.20",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-time",
|
"web-time",
|
||||||
@@ -1157,7 +1419,7 @@ version = "0.5.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
|
checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg_aliases",
|
"cfg_aliases 0.2.2",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2",
|
"socket2",
|
||||||
@@ -1694,13 +1956,33 @@ dependencies = [
|
|||||||
"xattr",
|
"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]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.20"
|
version = "2.0.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
|
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
@@ -2078,6 +2360,12 @@ dependencies = [
|
|||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "widestring"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
|
|||||||
@@ -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,
|
URLs and tokens are absent from errors and diagnostics. Ownership, limits,
|
||||||
injection, and offline fake-server coverage are documented in
|
injection, and offline fake-server coverage are documented in
|
||||||
[`docs/caps-http.md`](docs/caps-http.md).
|
[`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
|
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
|
capability list is posted as LLSD/XML, accepted URIs are rate-categorized, and
|
||||||
bounded long polls preserve ack IDs, reconnect retries, shutdown `done`, and
|
bounded long polls preserve ack IDs, reconnect retries, shutdown `done`, and
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Generated by `python3 tools/generate_api_shims.py`; do not edit by hand.
|
|||||||
|
|
||||||
| Assembly | Types | Members | Status |
|
| 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.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.Imaging.Skia` | 1 | 3 | native implementation: 1 type / 3 members; no generated shims remain |
|
||||||
| `LibreMetaverse.LslTools` | 164 | 768 | callable failure-only shim |
|
| `LibreMetaverse.LslTools` | 164 | 768 | callable failure-only shim |
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ futures-util = "0.3.31"
|
|||||||
libremetaverse-imaging = { path = "../libremetaverse-imaging" }
|
libremetaverse-imaging = { path = "../libremetaverse-imaging" }
|
||||||
libremetaverse-structured-data = { path = "../libremetaverse-structured-data" }
|
libremetaverse-structured-data = { path = "../libremetaverse-structured-data" }
|
||||||
libremetaverse-types = { path = "../libremetaverse-types" }
|
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"] }
|
reqwest = { version = "0.13.4", default-features = false, features = ["rustls", "stream"] }
|
||||||
roxmltree = "0.21.1"
|
roxmltree = "0.21.1"
|
||||||
tokio = { version = "1.47.1", features = ["macros", "net", "rt", "sync", "time"] }
|
tokio = { version = "1.47.1", features = ["macros", "net", "rt", "sync", "time"] }
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ struct ClientRuntime {
|
|||||||
agent_throttle_sender: Mutex<Option<Arc<dyn crate::udp_transport::AgentThrottleSender>>>,
|
agent_throttle_sender: Mutex<Option<Arc<dyn crate::udp_transport::AgentThrottleSender>>>,
|
||||||
http_caps_client: Mutex<crate::caps_http::HttpCapsClient>,
|
http_caps_client: Mutex<crate::caps_http::HttpCapsClient>,
|
||||||
caps_rate_limiter: Mutex<crate::caps_http::CapsRateLimiter>,
|
caps_rate_limiter: Mutex<crate::caps_http::CapsRateLimiter>,
|
||||||
|
network_manager: Mutex<std::sync::Weak<crate::network_manager::NetworkManagerInner>>,
|
||||||
shutdown_complete: Condvar,
|
shutdown_complete: Condvar,
|
||||||
shutdown_wait: Mutex<()>,
|
shutdown_wait: Mutex<()>,
|
||||||
}
|
}
|
||||||
@@ -142,6 +143,7 @@ impl ClientRuntime {
|
|||||||
agent_throttle_sender: Mutex::new(None),
|
agent_throttle_sender: Mutex::new(None),
|
||||||
http_caps_client: Mutex::new(http_caps_client),
|
http_caps_client: Mutex::new(http_caps_client),
|
||||||
caps_rate_limiter: Mutex::new(caps_rate_limiter),
|
caps_rate_limiter: Mutex::new(caps_rate_limiter),
|
||||||
|
network_manager: Mutex::new(std::sync::Weak::new()),
|
||||||
shutdown_complete: Condvar::new(),
|
shutdown_complete: Condvar::new(),
|
||||||
shutdown_wait: Mutex::new(()),
|
shutdown_wait: Mutex::new(()),
|
||||||
}
|
}
|
||||||
@@ -350,6 +352,31 @@ impl GridClient {
|
|||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn native_network(&self) -> Result<crate::NetworkManager, crate::Error> {
|
||||||
|
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(
|
pub(crate) fn native_set_caps_rate_limiter(
|
||||||
&mut self,
|
&mut self,
|
||||||
value: crate::caps_http::CapsRateLimiter,
|
value: crate::caps_http::CapsRateLimiter,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ mod generated;
|
|||||||
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
|
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
|
||||||
mod genepool_catalog;
|
mod genepool_catalog;
|
||||||
mod j2k;
|
mod j2k;
|
||||||
|
mod login;
|
||||||
mod message_codec;
|
mod message_codec;
|
||||||
mod message_decoder;
|
mod message_decoder;
|
||||||
mod network_manager;
|
mod network_manager;
|
||||||
|
|||||||
1687
crates/libremetaverse/src/login.rs
Normal file
1687
crates/libremetaverse/src/login.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#![allow(clippy::missing_errors_doc)] // Result shapes are fixed by the compatibility map.
|
#![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::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::too_many_lines)] // Packet dispatch keeps the complete protocol switch together.
|
||||||
#![allow(clippy::type_complexity)] // Delegate signatures are fixed by the compatibility map.
|
#![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::udp_transport::{UDPBase, UDPPacketBuffer, UdpPacketHandler, UdpTransportConfig};
|
||||||
use crate::{
|
use crate::{
|
||||||
Avatar, Caps, Error, GenericStreamingMethod, GridClient, Helpers, LoginResponseData, Primitive,
|
AccountLevelBenefits, Avatar, Caps, Error, GenericStreamingMethod, GridClient, Helpers,
|
||||||
RegionFlags, RegionProtocols, SimAccess, SimulatorDataPool, SimulatorFeatures,
|
LoginCredential, LoginParams, LoginProgressEventArgs, LoginResponseData, LoginStatus,
|
||||||
SimulatorSimStats, TerrainPatch,
|
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 libremetaverse_types::{UUID, Vector2};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
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))
|
Duration::from_millis(u64::try_from(value.max(1)).unwrap_or(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_login_uri(value: &str) -> Result<Uri, Error> {
|
||||||
|
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<T: Send + 'static>(
|
async fn run_blocking_compat<T: Send + 'static>(
|
||||||
name: &str,
|
name: &str,
|
||||||
operation: impl FnOnce() -> T + Send + 'static,
|
operation: impl FnOnce() -> T + Send + 'static,
|
||||||
@@ -857,6 +932,8 @@ pub struct SimulatorData {
|
|||||||
handshake_wait_lock: Mutex<()>,
|
handshake_wait_lock: Mutex<()>,
|
||||||
disconnect_candidate: AtomicBool,
|
disconnect_candidate: AtomicBool,
|
||||||
circuit_code: AtomicU32,
|
circuit_code: AtomicU32,
|
||||||
|
agent_id: RwLock<UUID>,
|
||||||
|
session_id: RwLock<UUID>,
|
||||||
seed_caps: RwLock<Option<Uri>>,
|
seed_caps: RwLock<Option<Uri>>,
|
||||||
caps_state: RwLock<Option<Arc<crate::caps::CapsInner>>>,
|
caps_state: RwLock<Option<Arc<crate::caps::CapsInner>>>,
|
||||||
event_queue_generation: Mutex<u64>,
|
event_queue_generation: Mutex<u64>,
|
||||||
@@ -1018,6 +1095,8 @@ impl Simulator {
|
|||||||
handshake_wait_lock: Mutex::new(()),
|
handshake_wait_lock: Mutex::new(()),
|
||||||
disconnect_candidate: AtomicBool::new(false),
|
disconnect_candidate: AtomicBool::new(false),
|
||||||
circuit_code: AtomicU32::new(0),
|
circuit_code: AtomicU32::new(0),
|
||||||
|
agent_id: RwLock::new(UUID::zero()),
|
||||||
|
session_id: RwLock::new(UUID::zero()),
|
||||||
seed_caps: RwLock::new(None),
|
seed_caps: RwLock::new(None),
|
||||||
caps_state: RwLock::new(None),
|
caps_state: RwLock::new(None),
|
||||||
event_queue_generation: Mutex::new(0),
|
event_queue_generation: Mutex::new(0),
|
||||||
@@ -1058,6 +1137,8 @@ impl Simulator {
|
|||||||
fn attach_manager(&self, manager: &Arc<NetworkManagerInner>, circuit_code: u32) {
|
fn attach_manager(&self, manager: &Arc<NetworkManagerInner>, circuit_code: u32) {
|
||||||
*mutex(&self.manager) = Arc::downgrade(manager);
|
*mutex(&self.manager) = Arc::downgrade(manager);
|
||||||
self.circuit_code.store(circuit_code, Ordering::Release);
|
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<bool, Error> {
|
pub fn native_connect(&self, _move_to_sim: bool) -> Result<bool, Error> {
|
||||||
@@ -1414,8 +1495,8 @@ impl Simulator {
|
|||||||
fn use_circuit_code_bytes(&self) -> Result<Vec<u8>, Error> {
|
fn use_circuit_code_bytes(&self) -> Result<Vec<u8>, Error> {
|
||||||
let mut packet = UseCircuitCodePacket::new_with_constructor()?;
|
let mut packet = UseCircuitCodePacket::new_with_constructor()?;
|
||||||
packet.circuit_code.code = self.circuit_code.load(Ordering::Acquire);
|
packet.circuit_code.code = self.circuit_code.load(Ordering::Acquire);
|
||||||
packet.circuit_code.id = UUID::zero();
|
packet.circuit_code.id = *read(&self.agent_id);
|
||||||
packet.circuit_code.session_id = UUID::zero();
|
packet.circuit_code.session_id = *read(&self.session_id);
|
||||||
packet.to_bytes_with_method()
|
packet.to_bytes_with_method()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1566,6 +1647,7 @@ struct NetworkEvents {
|
|||||||
disconnected: EventRegistry<DisconnectedEventArgs>,
|
disconnected: EventRegistry<DisconnectedEventArgs>,
|
||||||
event_queue_running: EventRegistry<EventQueueRunningEventArgs>,
|
event_queue_running: EventRegistry<EventQueueRunningEventArgs>,
|
||||||
generic_streaming_message: EventRegistry<GenericStreamingMessageEventArgs>,
|
generic_streaming_message: EventRegistry<GenericStreamingMessageEventArgs>,
|
||||||
|
login_progress: EventRegistry<LoginProgressEventArgs>,
|
||||||
packet_sent: EventRegistry<PacketSentEventArgs>,
|
packet_sent: EventRegistry<PacketSentEventArgs>,
|
||||||
sim_changed: EventRegistry<SimChangedEventArgs>,
|
sim_changed: EventRegistry<SimChangedEventArgs>,
|
||||||
sim_connected: EventRegistry<SimConnectedEventArgs>,
|
sim_connected: EventRegistry<SimConnectedEventArgs>,
|
||||||
@@ -1573,7 +1655,23 @@ struct NetworkEvents {
|
|||||||
sim_disconnected: EventRegistry<SimDisconnectedEventArgs>,
|
sim_disconnected: EventRegistry<SimDisconnectedEventArgs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NetworkManagerInner {
|
struct LoginRuntimeState {
|
||||||
|
next_generation: u64,
|
||||||
|
active: Option<(u64, CancellationTokenSource)>,
|
||||||
|
response_callbacks: Vec<(NetworkManagerLoginResponseCallback, Option<Vec<String>>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LoginRuntimeState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
next_generation: 1,
|
||||||
|
active: None,
|
||||||
|
response_callbacks: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct NetworkManagerInner {
|
||||||
client: GridClient,
|
client: GridClient,
|
||||||
simulators: SimulatorCollection,
|
simulators: SimulatorCollection,
|
||||||
packet_events: PacketEventDictionary,
|
packet_events: PacketEventDictionary,
|
||||||
@@ -1582,6 +1680,14 @@ struct NetworkManagerInner {
|
|||||||
current_sim: RwLock<Option<Simulator>>,
|
current_sim: RwLock<Option<Simulator>>,
|
||||||
connected: AtomicBool,
|
connected: AtomicBool,
|
||||||
circuit_code: AtomicU32,
|
circuit_code: AtomicU32,
|
||||||
|
agent_id: RwLock<UUID>,
|
||||||
|
session_id: RwLock<UUID>,
|
||||||
|
login_status: RwLock<LoginStatus>,
|
||||||
|
login_error_key: RwLock<String>,
|
||||||
|
login_message: RwLock<String>,
|
||||||
|
login_seed_capability: RwLock<Option<Uri>>,
|
||||||
|
login_response: LoginResponseData,
|
||||||
|
login_runtime: Mutex<LoginRuntimeState>,
|
||||||
inbox_count: AtomicI32,
|
inbox_count: AtomicI32,
|
||||||
outbox_count: AtomicI32,
|
outbox_count: AtomicI32,
|
||||||
workers: Mutex<Option<NetworkWorkers>>,
|
workers: Mutex<Option<NetworkWorkers>>,
|
||||||
@@ -2039,10 +2145,23 @@ impl fmt::Debug for NetworkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkManager {
|
impl NetworkManager {
|
||||||
|
pub(crate) fn native_from_inner(inner: Arc<NetworkManagerInner>) -> Self {
|
||||||
|
Self {
|
||||||
|
login_response_data: Some(inner.login_response.clone()),
|
||||||
|
simulators: inner.simulators.clone(),
|
||||||
|
inner,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn native_inner_weak(&self) -> Weak<NetworkManagerInner> {
|
||||||
|
Arc::downgrade(&self.inner)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn native_new(client: GridClient) -> Result<Self, Error> {
|
pub fn native_new(client: GridClient) -> Result<Self, Error> {
|
||||||
let simulators = SimulatorCollection::default();
|
let simulators = SimulatorCollection::default();
|
||||||
let packet_events = PacketEventDictionary::new(client.clone())?;
|
let packet_events = PacketEventDictionary::new(client.clone())?;
|
||||||
let caps_events = CapsEventDictionary::new(client.clone())?;
|
let caps_events = CapsEventDictionary::new(client.clone())?;
|
||||||
|
let login_response = LoginResponseData::new()?;
|
||||||
let inner = Arc::new(NetworkManagerInner {
|
let inner = Arc::new(NetworkManagerInner {
|
||||||
client,
|
client,
|
||||||
simulators: simulators.clone(),
|
simulators: simulators.clone(),
|
||||||
@@ -2052,6 +2171,14 @@ impl NetworkManager {
|
|||||||
current_sim: RwLock::new(None),
|
current_sim: RwLock::new(None),
|
||||||
connected: AtomicBool::new(false),
|
connected: AtomicBool::new(false),
|
||||||
circuit_code: AtomicU32::new(0),
|
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),
|
inbox_count: AtomicI32::new(0),
|
||||||
outbox_count: AtomicI32::new(0),
|
outbox_count: AtomicI32::new(0),
|
||||||
workers: Mutex::new(None),
|
workers: Mutex::new(None),
|
||||||
@@ -2075,7 +2202,7 @@ impl NetworkManager {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let manager = NetworkManager {
|
let manager = NetworkManager {
|
||||||
login_response_data: None,
|
login_response_data: Some(inner.login_response.clone()),
|
||||||
simulators: inner.simulators.clone(),
|
simulators: inner.simulators.clone(),
|
||||||
inner: Arc::clone(&inner),
|
inner: Arc::clone(&inner),
|
||||||
};
|
};
|
||||||
@@ -2099,12 +2226,623 @@ impl NetworkManager {
|
|||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
login_response_data: None,
|
login_response_data: Some(login_response),
|
||||||
simulators,
|
simulators,
|
||||||
inner,
|
inner,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn native_subscribe_login_progress(
|
||||||
|
&self,
|
||||||
|
handler: EventHandler<LoginProgressEventArgs>,
|
||||||
|
) -> 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<Uri> {
|
||||||
|
read(&self.inner.login_seed_capability).clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn native_set_login_seed_capability(&self, value: Option<Uri>) {
|
||||||
|
*write(&self.inner.login_seed_capability) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn native_account_level_benefits(&self) -> Option<AccountLevelBenefits> {
|
||||||
|
self.inner.login_response.account_level_benefits()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn native_agent_appearance_service_url(&self) -> Option<String> {
|
||||||
|
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, Error> {
|
||||||
|
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<Vec<String>>,
|
||||||
|
) -> 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<CancellationToken>,
|
||||||
|
) -> 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<CancellationToken>,
|
||||||
|
) -> Result<bool, Error> {
|
||||||
|
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<CancellationToken>,
|
||||||
|
) -> Result<Option<LoginResponseData>, 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<String>,
|
||||||
|
version: String,
|
||||||
|
cancellation_token: Option<CancellationToken>,
|
||||||
|
) -> Result<bool, 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(login_params, cancellation_token).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn native_login_credential_response(
|
||||||
|
&self,
|
||||||
|
credential: LoginCredential,
|
||||||
|
channel: String,
|
||||||
|
start: Option<String>,
|
||||||
|
version: String,
|
||||||
|
cancellation_token: Option<CancellationToken>,
|
||||||
|
) -> Result<Option<LoginResponseData>, 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<CancellationToken>,
|
||||||
|
) -> Result<bool, Error> {
|
||||||
|
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<CancellationToken>,
|
||||||
|
) -> Result<Option<LoginResponseData>, 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<Option<LoginResponseData>, 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<String> = 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<bool, Error> {
|
||||||
|
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<bool, Error> {
|
||||||
|
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(
|
pub fn native_subscribe_disconnected(
|
||||||
&self,
|
&self,
|
||||||
handler: EventHandler<DisconnectedEventArgs>,
|
handler: EventHandler<DisconnectedEventArgs>,
|
||||||
@@ -2299,7 +3037,7 @@ impl NetworkManager {
|
|||||||
size_y: u32,
|
size_y: u32,
|
||||||
) -> Result<Option<Simulator>, Error> {
|
) -> Result<Option<Simulator>, Error> {
|
||||||
let manager = Self {
|
let manager = Self {
|
||||||
login_response_data: None,
|
login_response_data: Some(self.inner.login_response.clone()),
|
||||||
simulators: self.simulators.clone(),
|
simulators: self.simulators.clone(),
|
||||||
inner: Arc::clone(&self.inner),
|
inner: Arc::clone(&self.inner),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,10 +7,16 @@ use libremetaverse::packets::{
|
|||||||
RegionHandshakePacket, RegionHandshakeReplyPacket, StartPingCheckPacket, UseCircuitCodePacket,
|
RegionHandshakePacket, RegionHandshakeReplyPacket, StartPingCheckPacket, UseCircuitCodePacket,
|
||||||
};
|
};
|
||||||
use libremetaverse::{
|
use libremetaverse::{
|
||||||
CapsEventDictionary, CapsEventQueueCallback, GridClient, Helpers, NetworkManager,
|
CapsEventDictionary, CapsEventQueueCallback, GridClient, Helpers, HttpCapsClient, LoginState,
|
||||||
NetworkManagerDisconnectType, PacketEventDictionary, Simulator,
|
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::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
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;
|
struct TestCapsMessage;
|
||||||
impl IMessage for TestCapsMessage {}
|
impl IMessage for TestCapsMessage {}
|
||||||
|
|
||||||
@@ -319,6 +354,7 @@ fn sim_connecting_can_cancel_without_leaking_collection_state() {
|
|||||||
enum ServerCommand {
|
enum ServerCommand {
|
||||||
Ping,
|
Ping,
|
||||||
Disable,
|
Disable,
|
||||||
|
ExpectEconomy,
|
||||||
Stop,
|
Stop,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,6 +363,7 @@ enum ServerReport {
|
|||||||
Ack(u32),
|
Ack(u32),
|
||||||
Circuit(u32),
|
Circuit(u32),
|
||||||
HandshakeReply(u32),
|
HandshakeReply(u32),
|
||||||
|
Economy,
|
||||||
Ping(u8),
|
Ping(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,6 +509,13 @@ fn spawn_fake_server() -> FakeServer {
|
|||||||
bytes[0] &= !(Helpers::MSG_RELIABLE | Helpers::MSG_ZEROCODED);
|
bytes[0] &= !(Helpers::MSG_RELIABLE | Helpers::MSG_ZEROCODED);
|
||||||
socket.send_to(&bytes, client_endpoint).unwrap();
|
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,
|
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(),
|
server.reports.recv_timeout(Duration::from_secs(1)).unwrap(),
|
||||||
ServerReport::HandshakeReply(0x1 | 0x2 | 0x4)
|
ServerReport::HandshakeReply(0x1 | 0x2 | 0x4)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_reuse_preserves_connected_override(&mut manager, &server, &sim);
|
assert_reuse_preserves_connected_override(&mut manager, &server, &sim);
|
||||||
|
|
||||||
assert_ping_exchange(&server, &packet_receiver);
|
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);
|
assert_eq!(sim_disconnects.load(Ordering::Relaxed), 1);
|
||||||
drop(guard);
|
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::<HttpRequest>::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::<HttpRequest>::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::<HttpResponse>().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());
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,14 +66,32 @@ the last simulator reports `SimShutdown`. Explicit shutdown preserves the
|
|||||||
caller-provided reason/message and sends `CloseCircuit` only for client or
|
caller-provided reason/message and sends `CloseCircuit` only for client or
|
||||||
network-timeout shutdowns.
|
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:
|
The deterministic tests use loopback fake simulators and require no live grid:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo test -p libremetaverse --test network_manager
|
cargo test -p libremetaverse --test network_manager
|
||||||
```
|
```
|
||||||
|
|
||||||
They cover ACK-gated circuit setup, typed handshake reply/state, ping response,
|
They cover login hashing, redirects, cancellation and initial handoff alongside
|
||||||
CAPS enable, UDP disable, current/seed selection, callback ordering and
|
ACK-gated circuit setup, typed handshake reply/state, ping response, CAPS enable,
|
||||||
filtering, RAII unregistration, runtime-neutral async calls, concurrent
|
UDP disable, current/seed selection, callback ordering and filtering, RAII
|
||||||
registration/removal, reentrant/concurrent disconnect, and keepalive timeout
|
unregistration, runtime-neutral async calls, concurrent registration/removal,
|
||||||
reasons.
|
reentrant/concurrent disconnect, and keepalive timeout reasons.
|
||||||
|
|||||||
300
tests/api-compile/Cargo.lock
generated
300
tests/api-compile/Cargo.lock
generated
@@ -8,12 +8,27 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
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]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-lc-rs"
|
name = "aws-lc-rs"
|
||||||
version = "1.18.0"
|
version = "1.18.0"
|
||||||
@@ -73,6 +88,15 @@ dependencies = [
|
|||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block2"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
|
||||||
|
dependencies = [
|
||||||
|
"objc2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.20.3"
|
version = "3.20.3"
|
||||||
@@ -103,6 +127,12 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg_aliases"
|
name = "cfg_aliases"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -228,6 +258,16 @@ dependencies = [
|
|||||||
"crypto-common 0.2.2",
|
"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]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
@@ -606,7 +646,7 @@ dependencies = [
|
|||||||
"jni-sys",
|
"jni-sys",
|
||||||
"log",
|
"log",
|
||||||
"simd_cesu8",
|
"simd_cesu8",
|
||||||
"thiserror",
|
"thiserror 2.0.20",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
@@ -680,6 +720,8 @@ dependencies = [
|
|||||||
"libremetaverse-imaging",
|
"libremetaverse-imaging",
|
||||||
"libremetaverse-structured-data",
|
"libremetaverse-structured-data",
|
||||||
"libremetaverse-types",
|
"libremetaverse-types",
|
||||||
|
"mac_address2",
|
||||||
|
"os_info",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -809,6 +851,18 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
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]]
|
[[package]]
|
||||||
name = "md-5"
|
name = "md-5"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -825,6 +879,15 @@ version = "2.8.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "metacrate-api-compile"
|
name = "metacrate-api-compile"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
@@ -865,6 +928,190 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"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]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.4"
|
version = "1.21.4"
|
||||||
@@ -877,6 +1124,21 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
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]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
@@ -920,14 +1182,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8"
|
checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"cfg_aliases",
|
"cfg_aliases 0.2.2",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"quinn-proto",
|
"quinn-proto",
|
||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls",
|
"rustls",
|
||||||
"socket2",
|
"socket2",
|
||||||
"thiserror",
|
"thiserror 2.0.20",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-time",
|
"web-time",
|
||||||
@@ -950,7 +1212,7 @@ dependencies = [
|
|||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"slab",
|
"slab",
|
||||||
"thiserror",
|
"thiserror 2.0.20",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-time",
|
"web-time",
|
||||||
@@ -962,7 +1224,7 @@ version = "0.5.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
|
checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg_aliases",
|
"cfg_aliases 0.2.2",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2",
|
"socket2",
|
||||||
@@ -1383,13 +1645,33 @@ dependencies = [
|
|||||||
"syn 2.0.119",
|
"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]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.20"
|
version = "2.0.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
|
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
@@ -1722,6 +2004,12 @@ dependencies = [
|
|||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "widestring"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
|
|||||||
@@ -7,14 +7,6 @@ use libremetaverse::{
|
|||||||
};
|
};
|
||||||
use libremetaverse_types::compat::{CancellationToken, EventHandler, IProgress, Subscription, Uri};
|
use libremetaverse_types::compat::{CancellationToken, EventHandler, IProgress, Subscription, Uri};
|
||||||
|
|
||||||
fn member_id<T>(result: Result<T, libremetaverse::Error>) -> &'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(
|
async fn compile_login_shutdown_flows(
|
||||||
network: &NetworkManager,
|
network: &NetworkManager,
|
||||||
limiter: &CapsRateLimiter,
|
limiter: &CapsRateLimiter,
|
||||||
@@ -102,10 +94,7 @@ fn client_and_settings_construct_without_starting_runtime_work() {
|
|||||||
libremetaverse::ClientLifecycleState::Disposed
|
libremetaverse::ClientLifecycleState::Disposed
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
let _limiter = CapsRateLimiter::new_with_constructor().expect("CAPS rate limiter");
|
||||||
member_id(CapsRateLimiter::new_with_constructor()),
|
|
||||||
"M:LibreMetaverse.CapsRateLimiter.#ctor"
|
|
||||||
);
|
|
||||||
let udp_buffer = UDPPacketBuffer::new_with_constructor().expect("UDP packet buffer");
|
let udp_buffer = UDPPacketBuffer::new_with_constructor().expect("UDP packet buffer");
|
||||||
assert_eq!(udp_buffer.data.len(), 4096);
|
assert_eq!(udp_buffer.data.len(), 4096);
|
||||||
assert_eq!(udp_buffer.data_length, 0);
|
assert_eq!(udp_buffer.data_length, 0);
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ TARGETS = {
|
|||||||
# implementations. The generated module keeps catalog markers and re-exports
|
# implementations. The generated module keeps catalog markers and re-exports
|
||||||
# the hand-written type so coverage remains deterministic.
|
# the hand-written type so coverage remains deterministic.
|
||||||
NATIVE_TYPES = {
|
NATIVE_TYPES = {
|
||||||
|
"T:LibreMetaverse.AccountLevelBenefits": "crate::login::AccountLevelBenefits",
|
||||||
"T:LibreMetaverse.AgentDisplayName": "crate::message_decoder::AgentDisplayName",
|
"T:LibreMetaverse.AgentDisplayName": "crate::message_decoder::AgentDisplayName",
|
||||||
"T:LibreMetaverse.CapabilitiesReceivedEventArgs": "crate::caps::CapabilitiesReceivedEventArgs",
|
"T:LibreMetaverse.CapabilitiesReceivedEventArgs": "crate::caps::CapabilitiesReceivedEventArgs",
|
||||||
"T:LibreMetaverse.Caps": "crate::caps::Caps",
|
"T:LibreMetaverse.Caps": "crate::caps::Caps",
|
||||||
@@ -50,6 +51,13 @@ NATIVE_TYPES = {
|
|||||||
"T:LibreMetaverse.Http.EventQueueClient.EventCallback": "crate::event_queue::EventQueueClientEventCallback",
|
"T:LibreMetaverse.Http.EventQueueClient.EventCallback": "crate::event_queue::EventQueueClientEventCallback",
|
||||||
"T:LibreMetaverse.HttpCapsClient": "crate::caps_http::HttpCapsClient",
|
"T:LibreMetaverse.HttpCapsClient": "crate::caps_http::HttpCapsClient",
|
||||||
"T:LibreMetaverse.HttpCapsClient.ProgressReport": "crate::caps_http::HttpCapsClientProgressReport",
|
"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.MediaEntry": "crate::message_decoder::MediaEntry",
|
||||||
"T:LibreMetaverse.Caps.EventQueueCallback": "crate::network_manager::CapsEventQueueCallback",
|
"T:LibreMetaverse.Caps.EventQueueCallback": "crate::network_manager::CapsEventQueueCallback",
|
||||||
"T:LibreMetaverse.CapsEventDictionary": "crate::network_manager::CapsEventDictionary",
|
"T:LibreMetaverse.CapsEventDictionary": "crate::network_manager::CapsEventDictionary",
|
||||||
@@ -58,6 +66,7 @@ NATIVE_TYPES = {
|
|||||||
"T:LibreMetaverse.GenericStreamingMessageEventArgs": "crate::network_manager::GenericStreamingMessageEventArgs",
|
"T:LibreMetaverse.GenericStreamingMessageEventArgs": "crate::network_manager::GenericStreamingMessageEventArgs",
|
||||||
"T:LibreMetaverse.NetworkManager.IncomingPacket": "crate::network_manager::NetworkManagerIncomingPacket",
|
"T:LibreMetaverse.NetworkManager.IncomingPacket": "crate::network_manager::NetworkManagerIncomingPacket",
|
||||||
"T:LibreMetaverse.NetworkManager.OutgoingPacket": "crate::network_manager::NetworkManagerOutgoingPacket",
|
"T:LibreMetaverse.NetworkManager.OutgoingPacket": "crate::network_manager::NetworkManagerOutgoingPacket",
|
||||||
|
"T:LibreMetaverse.NetworkManager.LoginResponseCallback": "crate::login::NetworkManagerLoginResponseCallback",
|
||||||
"T:LibreMetaverse.PacketEventDictionary": "crate::network_manager::PacketEventDictionary",
|
"T:LibreMetaverse.PacketEventDictionary": "crate::network_manager::PacketEventDictionary",
|
||||||
"T:LibreMetaverse.PacketReceivedEventArgs": "crate::network_manager::PacketReceivedEventArgs",
|
"T:LibreMetaverse.PacketReceivedEventArgs": "crate::network_manager::PacketReceivedEventArgs",
|
||||||
"T:LibreMetaverse.PacketSentEventArgs": "crate::network_manager::PacketSentEventArgs",
|
"T:LibreMetaverse.PacketSentEventArgs": "crate::network_manager::PacketSentEventArgs",
|
||||||
@@ -207,12 +216,18 @@ NATIVE_MEMBER_BODIES = {
|
|||||||
"self.native_http_caps_client()",
|
"self.native_http_caps_client()",
|
||||||
"P:LibreMetaverse.GridClient.HttpCapsClient#set":
|
"P:LibreMetaverse.GridClient.HttpCapsClient#set":
|
||||||
"self.native_set_http_caps_client(value)",
|
"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":
|
"E:LibreMetaverse.NetworkManager.Disconnected":
|
||||||
"self.native_subscribe_disconnected(handler)",
|
"self.native_subscribe_disconnected(handler)",
|
||||||
"E:LibreMetaverse.NetworkManager.EventQueueRunning":
|
"E:LibreMetaverse.NetworkManager.EventQueueRunning":
|
||||||
"self.native_subscribe_event_queue_running(handler)",
|
"self.native_subscribe_event_queue_running(handler)",
|
||||||
"E:LibreMetaverse.NetworkManager.GenericStreamingMessage":
|
"E:LibreMetaverse.NetworkManager.GenericStreamingMessage":
|
||||||
"self.native_subscribe_generic_streaming_message(handler)",
|
"self.native_subscribe_generic_streaming_message(handler)",
|
||||||
|
"E:LibreMetaverse.NetworkManager.LoginProgress":
|
||||||
|
"self.native_subscribe_login_progress(handler)",
|
||||||
"E:LibreMetaverse.NetworkManager.PacketSent":
|
"E:LibreMetaverse.NetworkManager.PacketSent":
|
||||||
"self.native_subscribe_packet_sent(handler)",
|
"self.native_subscribe_packet_sent(handler)",
|
||||||
"E:LibreMetaverse.NetworkManager.SimChanged":
|
"E:LibreMetaverse.NetworkManager.SimChanged":
|
||||||
@@ -225,6 +240,10 @@ NATIVE_MEMBER_BODIES = {
|
|||||||
"self.native_subscribe_sim_disconnected(handler)",
|
"self.native_subscribe_sim_disconnected(handler)",
|
||||||
"M:LibreMetaverse.NetworkManager.#ctor(LibreMetaverse.GridClient)":
|
"M:LibreMetaverse.NetworkManager.#ctor(LibreMetaverse.GridClient)":
|
||||||
"crate::network_manager::NetworkManager::native_new(client)",
|
"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)":
|
"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)",
|
"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)":
|
"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)",
|
"crate::message_decoder::to_ip(osd)",
|
||||||
"M:LibreMetaverse.NetworkManager.FindSimulator(System.UInt64)":
|
"M:LibreMetaverse.NetworkManager.FindSimulator(System.UInt64)":
|
||||||
"Ok(self.native_find_handle(handle))",
|
"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})":
|
"M:LibreMetaverse.NetworkManager.RegisterCallback(LibreMetaverse.Packets.PacketType,System.EventHandler{LibreMetaverse.PacketReceivedEventArgs})":
|
||||||
"self.native_register_callback(type_, callback, true)",
|
"self.native_register_callback(type_, callback, true)",
|
||||||
"M:LibreMetaverse.NetworkManager.RegisterCallback(LibreMetaverse.Packets.PacketType,System.EventHandler{LibreMetaverse.PacketReceivedEventArgs},System.Boolean)":
|
"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)",
|
"self.native_set_current_sim(value)",
|
||||||
"P:LibreMetaverse.NetworkManager.InboxCount":
|
"P:LibreMetaverse.NetworkManager.InboxCount":
|
||||||
"self.native_inbox_count()",
|
"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":
|
"P:LibreMetaverse.NetworkManager.OutboxCount":
|
||||||
"self.native_outbox_count()",
|
"self.native_outbox_count()",
|
||||||
"M:LibreMetaverse.Simulator.#ctor(LibreMetaverse.GridClient,System.Net.IPEndPoint,System.UInt64,System.UInt32,System.UInt32)":
|
"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 = (
|
NETWORK_NATIVE_OWNERS = (
|
||||||
|
"InventoryBase",
|
||||||
|
"InventoryFolder",
|
||||||
"NetworkManager",
|
"NetworkManager",
|
||||||
"Simulator",
|
"Simulator",
|
||||||
)
|
)
|
||||||
@@ -906,6 +1017,17 @@ PRIVATE_LAYOUTS = {
|
|||||||
],
|
],
|
||||||
"T:LibreMetaverse.EmptyRemovalStrategy`1": [("keys", "Vec<TKey>")],
|
"T:LibreMetaverse.EmptyRemovalStrategy`1": [("keys", "Vec<TKey>")],
|
||||||
"T:LibreMetaverse.ExpiringCache`2": [("entries", "std::collections::HashMap<TKey, TValue>")],
|
"T:LibreMetaverse.ExpiringCache`2": [("entries", "std::collections::HashMap<TKey, TValue>")],
|
||||||
|
"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<TKey>")],
|
"T:LibreMetaverse.LruRemovalStrategy`1": [("keys", "Vec<TKey>")],
|
||||||
"T:LibreMetaverse.MruRemovalStrategy`1": [("keys", "Vec<TKey>")],
|
"T:LibreMetaverse.MruRemovalStrategy`1": [("keys", "Vec<TKey>")],
|
||||||
"T:LibreMetaverse.MultiValueDictionary`2": [("entries", "std::collections::HashMap<TKey, Vec<TValue>>")],
|
"T:LibreMetaverse.MultiValueDictionary`2": [("entries", "std::collections::HashMap<TKey, Vec<TValue>>")],
|
||||||
@@ -926,8 +1048,12 @@ PRIVATE_LAYOUTS = {
|
|||||||
VALUE_DERIVES = {
|
VALUE_DERIVES = {
|
||||||
"T:LibreMetaverse.AgentSettings": "Clone, Debug, Eq, PartialEq",
|
"T:LibreMetaverse.AgentSettings": "Clone, Debug, Eq, PartialEq",
|
||||||
"T:LibreMetaverse.AssetCacheSettings": "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.ConnectionSettings": "Clone, Eq, PartialEq",
|
||||||
"T:LibreMetaverse.LoggingSettings": "Clone, Debug, 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.Matrix4": "Clone, Copy, Debug",
|
||||||
"T:LibreMetaverse.Quaternion": "Clone, Copy, Debug",
|
"T:LibreMetaverse.Quaternion": "Clone, Copy, Debug",
|
||||||
"T:LibreMetaverse.PacketSettings": "Clone, Debug, Eq, PartialEq",
|
"T:LibreMetaverse.PacketSettings": "Clone, Debug, Eq, PartialEq",
|
||||||
|
|||||||
@@ -992,6 +992,7 @@ def validate_generated_shims() -> None:
|
|||||||
"crate::genepool_catalog::",
|
"crate::genepool_catalog::",
|
||||||
"crate::json_codec::",
|
"crate::json_codec::",
|
||||||
"crate::message_codec::",
|
"crate::message_codec::",
|
||||||
|
"crate::message_decoder::",
|
||||||
"crate::notation::",
|
"crate::notation::",
|
||||||
"crate::packet_catalog::",
|
"crate::packet_catalog::",
|
||||||
"crate::packet_wire::",
|
"crate::packet_wire::",
|
||||||
|
|||||||
Reference in New Issue
Block a user