mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
merge main
This commit is contained in:
commit
49fe5fa66a
27 changed files with 865 additions and 277 deletions
456
Cargo.lock
generated
456
Cargo.lock
generated
|
@ -4,9 +4,9 @@ version = 3
|
|||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.17.0"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
||||
checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
@ -41,9 +41,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
|
||||
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
|
@ -63,9 +63,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.66"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
|
@ -117,9 +117,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-recursion"
|
||||
version = "1.0.0"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
|
||||
checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -134,9 +134,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.59"
|
||||
version = "0.1.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364"
|
||||
checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -149,7 +149,7 @@ version = "0.2.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
@ -177,7 +177,7 @@ dependencies = [
|
|||
"bevy_app",
|
||||
"bevy_ecs",
|
||||
"derive_more",
|
||||
"env_logger",
|
||||
"env_logger 0.10.0",
|
||||
"futures",
|
||||
"iyes_loopless",
|
||||
"log",
|
||||
|
@ -197,7 +197,7 @@ dependencies = [
|
|||
"azalea-buf",
|
||||
"azalea-crypto",
|
||||
"chrono",
|
||||
"env_logger",
|
||||
"env_logger 0.9.3",
|
||||
"log",
|
||||
"num-bigint",
|
||||
"reqwest",
|
||||
|
@ -286,7 +286,7 @@ dependencies = [
|
|||
"bevy_ecs",
|
||||
"bevy_time",
|
||||
"derive_more",
|
||||
"env_logger",
|
||||
"env_logger 0.9.3",
|
||||
"futures",
|
||||
"iyes_loopless",
|
||||
"log",
|
||||
|
@ -339,7 +339,7 @@ dependencies = [
|
|||
name = "azalea-nbt"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"ahash 0.8.2",
|
||||
"ahash 0.8.3",
|
||||
"azalea-buf",
|
||||
"byteorder",
|
||||
"criterion",
|
||||
|
@ -369,6 +369,7 @@ dependencies = [
|
|||
name = "azalea-protocol"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
"async-recursion",
|
||||
"azalea-auth",
|
||||
|
@ -389,11 +390,14 @@ dependencies = [
|
|||
"futures",
|
||||
"futures-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"trust-dns-resolver",
|
||||
"uuid",
|
||||
]
|
||||
|
@ -450,24 +454,24 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.66"
|
||||
version = "0.3.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
|
||||
checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide 0.5.4",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
|
||||
[[package]]
|
||||
name = "bevy_app"
|
||||
|
@ -667,7 +671,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"azalea",
|
||||
"azalea-protocol",
|
||||
"env_logger",
|
||||
"env_logger 0.9.3",
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"tokio",
|
||||
|
@ -688,15 +692,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.1"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.12.3"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f"
|
||||
checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
|
@ -724,9 +728,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.77"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
|
||||
[[package]]
|
||||
name = "cfb8"
|
||||
|
@ -945,9 +949,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.3.2"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
|
||||
checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
|
@ -1018,6 +1022,19 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
|
||||
dependencies = [
|
||||
"humantime",
|
||||
"is-terminal",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.3.24"
|
||||
|
@ -1027,6 +1044,27 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
|
@ -1055,7 +1093,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide 0.6.2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1226,9 +1264,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.26.2"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
||||
checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec"
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
|
@ -1290,6 +1328,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.8"
|
||||
|
@ -1298,7 +1345,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
|
|||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa 1.0.4",
|
||||
"itoa 1.0.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1345,7 +1392,7 @@ dependencies = [
|
|||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa 1.0.4",
|
||||
"itoa 1.0.5",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
|
@ -1420,10 +1467,32 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.5.1"
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745"
|
||||
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||
dependencies = [
|
||||
"hermit-abi 0.2.6",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
|
@ -1442,9 +1511,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
|||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
|
||||
[[package]]
|
||||
name = "iyes_loopless"
|
||||
|
@ -1475,9 +1544,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.138"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
|
@ -1485,6 +1554,12 @@ version = "0.5.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.9"
|
||||
|
@ -1515,9 +1590,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
|
@ -1540,15 +1615,6 @@ version = "0.3.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.6.2"
|
||||
|
@ -1567,7 +1633,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1594,6 +1660,16 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||
dependencies = [
|
||||
"overload",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.0"
|
||||
|
@ -1621,9 +1697,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
|
||||
checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
@ -1683,28 +1759,28 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.14.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.2.6",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.29.0"
|
||||
version = "0.30.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
|
||||
checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.16.0"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
|
@ -1714,9 +1790,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
|||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.43"
|
||||
version = "0.10.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "020433887e44c27ff16365eaa2d380547a94544ad509aff6eb5b6e3e0b27b376"
|
||||
checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
|
@ -1746,9 +1822,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
|||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.78"
|
||||
version = "0.9.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07d5c8cb6e57b3a3612064d7b18b117912b4ce70955c2504d4b741c9e244b132"
|
||||
checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
|
@ -1757,6 +1833,12 @@ dependencies = [
|
|||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.0.0"
|
||||
|
@ -1775,9 +1857,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.5"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
|
||||
checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"cfg-if",
|
||||
|
@ -1786,7 +1868,7 @@ dependencies = [
|
|||
"redox_syscall",
|
||||
"smallvec",
|
||||
"thread-id",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1869,18 +1951,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -1917,20 +1999,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
|
||||
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.10.1"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
|
||||
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
|
@ -1949,9 +2030,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.0"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -1981,9 +2062,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.13"
|
||||
version = "0.11.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
|
||||
checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
|
@ -2043,10 +2124,24 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
name = "rustix"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
|
@ -2059,12 +2154,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.20"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
|
||||
checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.36.1",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2075,9 +2169,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
|||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.7.0"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
|
||||
checksum = "7c4437699b6d34972de58652c68b98cb5b53a4199ab126db8e20ec8ded29a721"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
|
@ -2088,9 +2182,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.6.1"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
|
||||
checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
|
@ -2104,9 +2198,9 @@ checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.149"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
@ -2123,9 +2217,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.149"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4"
|
||||
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2134,11 +2228,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.89"
|
||||
version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
|
||||
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
|
||||
dependencies = [
|
||||
"itoa 1.0.4",
|
||||
"itoa 1.0.5",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
@ -2150,7 +2244,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"itoa 1.0.4",
|
||||
"itoa 1.0.5",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
@ -2166,6 +2260,15 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
|
@ -2217,9 +2320,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.105"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2242,9 +2345,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
@ -2260,18 +2363,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2325,9 +2428,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.23.1"
|
||||
version = "1.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38a54aca0c15d014013256222ba0ebed095673f89345dd79119d912eb561b7a8"
|
||||
checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
|
@ -2340,7 +2443,7 @@ dependencies = [
|
|||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2380,9 +2483,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.10"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -2423,6 +2526,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2470,9 +2599,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||
|
||||
[[package]]
|
||||
name = "typemap_rev"
|
||||
|
@ -2488,15 +2617,15 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.8"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
|
@ -2534,6 +2663,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
|
@ -2686,19 +2821,6 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc 0.36.1",
|
||||
"windows_i686_gnu 0.36.1",
|
||||
"windows_i686_msvc 0.36.1",
|
||||
"windows_x86_64_gnu 0.36.1",
|
||||
"windows_x86_64_msvc 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
|
@ -2706,85 +2828,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc 0.42.0",
|
||||
"windows_i686_gnu 0.42.0",
|
||||
"windows_i686_msvc 0.42.0",
|
||||
"windows_x86_64_gnu 0.42.0",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc 0.42.0",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
|
|
|
@ -9,8 +9,8 @@ version = "0.5.0"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
azalea-buf = {path = "../azalea-buf", version = "^0.5.0" }
|
||||
azalea-crypto = {path = "../azalea-crypto", version = "^0.5.0" }
|
||||
azalea-buf = {path = "../azalea-buf", version = "^0.5.0"}
|
||||
azalea-crypto = {path = "../azalea-crypto", version = "^0.5.0"}
|
||||
chrono = {version = "0.4.22", default-features = false}
|
||||
log = "0.4.17"
|
||||
num-bigint = "0.4.3"
|
||||
|
@ -19,8 +19,8 @@ serde = {version = "1.0.145", features = ["derive"]}
|
|||
serde_json = "1.0.86"
|
||||
thiserror = "1.0.37"
|
||||
tokio = {version = "1.23.1", features = ["fs"]}
|
||||
uuid = "^1.1.2"
|
||||
uuid = {version = "^1.1.2", features = ["serde"]}
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.9.1"
|
||||
env_logger = "0.9.3"
|
||||
tokio = {version = "1.23.1", features = ["full"]}
|
||||
|
|
|
@ -10,6 +10,7 @@ use std::{
|
|||
time::{Instant, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AuthOpts {
|
||||
|
@ -209,8 +210,7 @@ pub struct GameOwnershipItem {
|
|||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct ProfileResponse {
|
||||
// todo: make the id a uuid
|
||||
pub id: String,
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub skins: Vec<serde_json::Value>,
|
||||
pub capes: Vec<serde_json::Value>,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use azalea_buf::McBuf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(McBuf, Debug, Clone, Default)]
|
||||
#[derive(McBuf, Debug, Clone, Default, Eq, PartialEq)]
|
||||
pub struct GameProfile {
|
||||
/// The UUID of the player.
|
||||
pub uuid: Uuid,
|
||||
|
@ -21,8 +22,100 @@ impl GameProfile {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(McBuf, Debug, Clone)]
|
||||
impl From<SerializableGameProfile> for GameProfile {
|
||||
fn from(value: SerializableGameProfile) -> Self {
|
||||
let mut properties = HashMap::new();
|
||||
for value in value.properties {
|
||||
properties.insert(
|
||||
value.name,
|
||||
ProfilePropertyValue {
|
||||
value: value.value,
|
||||
signature: value.signature,
|
||||
},
|
||||
);
|
||||
}
|
||||
Self {
|
||||
uuid: value.id,
|
||||
name: value.name,
|
||||
properties,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(McBuf, Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ProfilePropertyValue {
|
||||
pub value: String,
|
||||
pub signature: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SerializableGameProfile {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub properties: Vec<SerializableProfilePropertyValue>,
|
||||
}
|
||||
|
||||
impl From<GameProfile> for SerializableGameProfile {
|
||||
fn from(value: GameProfile) -> Self {
|
||||
let mut properties = Vec::new();
|
||||
for (key, value) in value.properties {
|
||||
properties.push(SerializableProfilePropertyValue {
|
||||
name: key,
|
||||
value: value.value,
|
||||
signature: value.signature,
|
||||
});
|
||||
}
|
||||
Self {
|
||||
id: value.uuid,
|
||||
name: value.name,
|
||||
properties,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SerializableProfilePropertyValue {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
pub signature: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_game_profile() {
|
||||
let json = r#"{
|
||||
"id": "f1a2b3c4-d5e6-f7a8-b9c0-d1e2f3a4b5c6",
|
||||
"name": "Notch",
|
||||
"properties": [
|
||||
{
|
||||
"name": "qwer",
|
||||
"value": "asdf",
|
||||
"signature": "zxcv"
|
||||
}
|
||||
]
|
||||
}"#;
|
||||
let profile =
|
||||
GameProfile::from(serde_json::from_str::<SerializableGameProfile>(json).unwrap());
|
||||
assert_eq!(
|
||||
profile,
|
||||
GameProfile {
|
||||
uuid: Uuid::parse_str("f1a2b3c4-d5e6-f7a8-b9c0-d1e2f3a4b5c6").unwrap(),
|
||||
name: "Notch".to_string(),
|
||||
properties: {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(
|
||||
"qwer".to_string(),
|
||||
ProfilePropertyValue {
|
||||
value: "asdf".to_string(),
|
||||
signature: Some("zxcv".to_string()),
|
||||
},
|
||||
);
|
||||
map
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
//! Tell Mojang you're joining a multiplayer server.
|
||||
use log::debug;
|
||||
use reqwest::StatusCode;
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::game_profile::{GameProfile, SerializableGameProfile};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SessionServerError {
|
||||
pub enum ClientSessionServerError {
|
||||
#[error("Error sending HTTP request to sessionserver: {0}")]
|
||||
HttpError(#[from] reqwest::Error),
|
||||
#[error("Multiplayer is not enabled for this account")]
|
||||
|
@ -24,6 +28,18 @@ pub enum SessionServerError {
|
|||
UnexpectedResponse { status_code: u16, body: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ServerSessionServerError {
|
||||
#[error("Error sending HTTP request to sessionserver: {0}")]
|
||||
HttpError(#[from] reqwest::Error),
|
||||
#[error("Invalid or expired session")]
|
||||
InvalidSession,
|
||||
#[error("Unexpected response from sessionserver (status code {status_code}): {body}")]
|
||||
UnexpectedResponse { status_code: u16, body: String },
|
||||
#[error("Unknown sessionserver error: {0}")]
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ForbiddenError {
|
||||
pub error: String,
|
||||
|
@ -39,7 +55,7 @@ pub async fn join(
|
|||
private_key: &[u8],
|
||||
uuid: &Uuid,
|
||||
server_id: &str,
|
||||
) -> Result<(), SessionServerError> {
|
||||
) -> Result<(), ClientSessionServerError> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let server_hash = azalea_crypto::hex_digest(&azalea_crypto::digest_data(
|
||||
|
@ -63,28 +79,82 @@ pub async fn join(
|
|||
.await?;
|
||||
|
||||
match res.status() {
|
||||
reqwest::StatusCode::NO_CONTENT => Ok(()),
|
||||
reqwest::StatusCode::FORBIDDEN => {
|
||||
StatusCode::NO_CONTENT => Ok(()),
|
||||
StatusCode::FORBIDDEN => {
|
||||
let forbidden = res.json::<ForbiddenError>().await?;
|
||||
match forbidden.error.as_str() {
|
||||
"InsufficientPrivilegesException" => Err(SessionServerError::MultiplayerDisabled),
|
||||
"UserBannedException" => Err(SessionServerError::Banned),
|
||||
"AuthenticationUnavailableException" => {
|
||||
Err(SessionServerError::AuthServersUnreachable)
|
||||
"InsufficientPrivilegesException" => {
|
||||
Err(ClientSessionServerError::MultiplayerDisabled)
|
||||
}
|
||||
"InvalidCredentialsException" => Err(SessionServerError::InvalidSession),
|
||||
"ForbiddenOperationException" => Err(SessionServerError::ForbiddenOperation),
|
||||
_ => Err(SessionServerError::Unknown(forbidden.error)),
|
||||
"UserBannedException" => Err(ClientSessionServerError::Banned),
|
||||
"AuthenticationUnavailableException" => {
|
||||
Err(ClientSessionServerError::AuthServersUnreachable)
|
||||
}
|
||||
"InvalidCredentialsException" => Err(ClientSessionServerError::InvalidSession),
|
||||
"ForbiddenOperationException" => Err(ClientSessionServerError::ForbiddenOperation),
|
||||
_ => Err(ClientSessionServerError::Unknown(forbidden.error)),
|
||||
}
|
||||
}
|
||||
status_code => {
|
||||
// log the headers
|
||||
log::debug!("Error headers: {:#?}", res.headers());
|
||||
debug!("Error headers: {:#?}", res.headers());
|
||||
let body = res.text().await?;
|
||||
Err(SessionServerError::UnexpectedResponse {
|
||||
Err(ClientSessionServerError::UnexpectedResponse {
|
||||
status_code: status_code.as_u16(),
|
||||
body,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask Mojang's servers if the player joining is authenticated.
|
||||
/// Included in the reply is the player's skin and cape.
|
||||
/// The IP field is optional and equivalent to enabling
|
||||
/// 'prevent-proxy-connections' in server.properties
|
||||
pub async fn serverside_auth(
|
||||
username: &str,
|
||||
public_key: &[u8],
|
||||
private_key: &[u8; 16],
|
||||
ip: Option<&str>,
|
||||
) -> Result<GameProfile, ServerSessionServerError> {
|
||||
let hash = azalea_crypto::hex_digest(&azalea_crypto::digest_data(
|
||||
"".as_bytes(),
|
||||
public_key,
|
||||
private_key,
|
||||
));
|
||||
|
||||
let url = reqwest::Url::parse_with_params(
|
||||
"https://sessionserver.mojang.com/session/minecraft/hasJoined",
|
||||
if let Some(ip) = ip {
|
||||
vec![("username", username), ("serverId", &hash), ("ip", ip)]
|
||||
} else {
|
||||
vec![("username", username), ("serverId", &hash)]
|
||||
},
|
||||
)
|
||||
.expect("URL should always be valid");
|
||||
|
||||
let res = reqwest::get(url).await?;
|
||||
|
||||
match res.status() {
|
||||
StatusCode::OK => {}
|
||||
StatusCode::NO_CONTENT => {
|
||||
return Err(ServerSessionServerError::InvalidSession);
|
||||
}
|
||||
StatusCode::FORBIDDEN => {
|
||||
return Err(ServerSessionServerError::Unknown(
|
||||
res.json::<ForbiddenError>().await?.error,
|
||||
))
|
||||
}
|
||||
status_code => {
|
||||
// log the headers
|
||||
debug!("Error headers: {:#?}", res.headers());
|
||||
let body = res.text().await?;
|
||||
return Err(ServerSessionServerError::UnexpectedResponse {
|
||||
status_code: status_code.as_u16(),
|
||||
body,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Ok(res.json::<SerializableGameProfile>().await?.into())
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ impl StringReader {
|
|||
}
|
||||
|
||||
pub fn is_allowed_number(c: char) -> bool {
|
||||
('0'..='9').contains(&c) || c == '.' || c == '-'
|
||||
c.is_ascii_digit() || c == '.' || c == '-'
|
||||
}
|
||||
|
||||
pub fn is_quoted_string_start(c: char) -> bool {
|
||||
|
@ -175,9 +175,9 @@ impl StringReader {
|
|||
}
|
||||
|
||||
pub fn is_allowed_in_unquoted_string(c: char) -> bool {
|
||||
('0'..='9').contains(&c)
|
||||
|| ('A'..='Z').contains(&c)
|
||||
|| ('a'..='z').contains(&c)
|
||||
c.is_ascii_digit()
|
||||
|| c.is_ascii_uppercase()
|
||||
|| c.is_ascii_lowercase()
|
||||
|| c == '_'
|
||||
|| c == '-'
|
||||
|| c == '.'
|
||||
|
|
|
@ -142,6 +142,21 @@ mod tests {
|
|||
assert_eq!(i32::var_read_from(&mut Cursor::new(&buf)).unwrap(), 7178);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_varlong() {
|
||||
let mut buf = Vec::new();
|
||||
0u64.var_write_into(&mut buf).unwrap();
|
||||
assert_eq!(buf, vec![0]);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
1u64.var_write_into(&mut buf).unwrap();
|
||||
assert_eq!(buf, vec![1]);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
9223372036854775807u64.var_write_into(&mut buf).unwrap();
|
||||
assert_eq!(buf, vec![255, 255, 255, 255, 255, 255, 255, 255, 127]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list() {
|
||||
let original_vec = vec!["a".to_string(), "bc".to_string(), "def".to_string()];
|
||||
|
|
|
@ -132,15 +132,16 @@ impl McBufVarWritable for i64 {
|
|||
fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut buffer = [0];
|
||||
let mut value = *self;
|
||||
if value == 0 {
|
||||
buf.write_all(&buffer).unwrap();
|
||||
}
|
||||
while value != 0 {
|
||||
buffer[0] = (value & 0b0111_1111) as u8;
|
||||
value = (value >> 7) & (i64::max_value() >> 6);
|
||||
if value != 0 {
|
||||
buffer[0] |= 0b1000_0000;
|
||||
}
|
||||
// this only writes a single byte, so write_all isn't necessary
|
||||
// the let _ = is so clippy doesn't complain
|
||||
let _ = buf.write(&buffer)?;
|
||||
buf.write_all(&buffer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -8,10 +8,13 @@ version = "0.5.0"
|
|||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = ["azalea-buf"]
|
||||
|
||||
[dependencies]
|
||||
azalea-buf = { path = "../azalea-buf", features = [
|
||||
"serde_json",
|
||||
], version = "^0.5.0" }
|
||||
], version = "^0.5.0", optional = true }
|
||||
azalea-language = { path = "../azalea-language", version = "^0.5.0" }
|
||||
log = "0.4.17"
|
||||
once_cell = "1.16.0"
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::{
|
|||
text_component::TextComponent,
|
||||
translatable_component::{StringOrComponent, TranslatableComponent},
|
||||
};
|
||||
#[cfg(feature = "azalea-buf")]
|
||||
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
|
||||
use log::debug;
|
||||
use once_cell::sync::Lazy;
|
||||
|
@ -275,6 +276,7 @@ impl<'de> Deserialize<'de> for FormattedText {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "azalea-buf")]
|
||||
impl McBufReadable for FormattedText {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let string = String::read_from(buf)?;
|
||||
|
@ -285,6 +287,7 @@ impl McBufReadable for FormattedText {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "azalea-buf")]
|
||||
impl McBufWritable for FormattedText {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let json = serde_json::to_string(self).unwrap();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::{collections::HashMap, fmt};
|
||||
|
||||
#[cfg(feature = "azalea-buf")]
|
||||
use azalea_buf::McBuf;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{ser::SerializeStruct, Serialize, Serializer};
|
||||
|
@ -86,7 +87,8 @@ impl Ansi {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, McBuf)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
#[cfg_attr(feature = "azalea-buf", derive(McBuf))]
|
||||
pub enum ChatFormatting {
|
||||
Black,
|
||||
DarkBlue,
|
||||
|
|
|
@ -31,7 +31,7 @@ pub struct Account {
|
|||
/// This is an `Arc<Mutex>` so it can be modified by [`Self::refresh`].
|
||||
pub access_token: Option<Arc<Mutex<String>>>,
|
||||
/// Only required for online-mode accounts.
|
||||
pub uuid: Option<uuid::Uuid>,
|
||||
pub uuid: Option<Uuid>,
|
||||
|
||||
/// The parameters (i.e. email) that were passed for creating this
|
||||
/// [`Account`]. This is used to for automatic reauthentication when we get
|
||||
|
@ -85,7 +85,7 @@ impl Account {
|
|||
Ok(Self {
|
||||
username: auth_result.profile.name,
|
||||
access_token: Some(Arc::new(Mutex::new(auth_result.access_token))),
|
||||
uuid: Some(Uuid::parse_str(&auth_result.profile.id).expect("Invalid UUID")),
|
||||
uuid: Some(auth_result.profile.id),
|
||||
auth_opts: AuthOpts::Microsoft {
|
||||
email: email.to_string(),
|
||||
},
|
||||
|
|
|
@ -75,7 +75,8 @@ impl ChatPacket {
|
|||
}
|
||||
|
||||
/// Get the UUID of the sender of the message. If it's not a
|
||||
/// player-sent chat message, this will be None.
|
||||
/// player-sent chat message, this will be None (this is sometimes the case
|
||||
/// when a server uses a plugin to modify chat messages).
|
||||
pub fn uuid(&self) -> Option<Uuid> {
|
||||
match self {
|
||||
ChatPacket::System(_) => None,
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
Account, PlayerInfo, StartSprintEvent, StartWalkEvent,
|
||||
};
|
||||
|
||||
use azalea_auth::{game_profile::GameProfile, sessionserver::SessionServerError};
|
||||
use azalea_auth::{game_profile::GameProfile, sessionserver::ClientSessionServerError};
|
||||
use azalea_chat::FormattedText;
|
||||
use azalea_physics::PhysicsPlugin;
|
||||
use azalea_protocol::{
|
||||
|
@ -85,7 +85,7 @@ pub enum JoinError {
|
|||
#[error("{0}")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("{0}")]
|
||||
SessionServer(#[from] azalea_auth::sessionserver::SessionServerError),
|
||||
SessionServer(#[from] azalea_auth::sessionserver::ClientSessionServerError),
|
||||
#[error("The given address could not be parsed into a ServerAddress")]
|
||||
InvalidAddress,
|
||||
#[error("Couldn't refresh access token: {0}")]
|
||||
|
@ -286,8 +286,8 @@ impl Client {
|
|||
}
|
||||
if matches!(
|
||||
e,
|
||||
SessionServerError::InvalidSession
|
||||
| SessionServerError::ForbiddenOperation
|
||||
ClientSessionServerError::InvalidSession
|
||||
| ClientSessionServerError::ForbiddenOperation
|
||||
) {
|
||||
// uh oh, we got an invalid session and have
|
||||
// to reauthenticate now
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::io::{Cursor, Read, Write};
|
||||
use std::io::{Cursor, Write};
|
||||
|
||||
use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable};
|
||||
|
||||
|
@ -25,9 +25,7 @@ impl BitSet {
|
|||
fn check_range(&self, from_index: usize, to_index: usize) {
|
||||
assert!(
|
||||
from_index <= to_index,
|
||||
"fromIndex: {} > toIndex: {}",
|
||||
from_index,
|
||||
to_index
|
||||
"fromIndex: {from_index} > toIndex: {to_index}",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -108,13 +106,6 @@ impl BitSet {
|
|||
pub fn set(&mut self, bit_index: usize) {
|
||||
self.data[bit_index / 64] |= 1u64 << (bit_index % 64);
|
||||
}
|
||||
|
||||
/// Read a BitSet with a known length.
|
||||
pub fn read_fixed(buf: &mut Cursor<&[u8]>, length: usize) -> Result<Self, BufReadError> {
|
||||
let mut data = vec![0; length.div_ceil(8)];
|
||||
buf.read_exact(&mut data)?;
|
||||
Ok(BitSet::from(data))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u64>> for BitSet {
|
||||
|
|
|
@ -41,3 +41,9 @@ uuid = "1.1.2"
|
|||
connecting = []
|
||||
default = ["packets"]
|
||||
packets = ["connecting", "dep:async-compression", "dep:azalea-core"]
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "^1.0.65"
|
||||
tracing = "^0.1.36"
|
||||
tracing-subscriber = "^0.3.15"
|
||||
once_cell = "1.17.0"
|
225
azalea-protocol/examples/handshake_proxy.rs
Normal file
225
azalea-protocol/examples/handshake_proxy.rs
Normal file
|
@ -0,0 +1,225 @@
|
|||
//! A "simple" server that gets login information and proxies connections.
|
||||
//! After login all connections are encrypted and Azalea cannot read them.
|
||||
|
||||
use azalea_protocol::{
|
||||
connect::Connection,
|
||||
packets::{
|
||||
handshake::{
|
||||
client_intention_packet::ClientIntentionPacket, ClientboundHandshakePacket,
|
||||
ServerboundHandshakePacket,
|
||||
},
|
||||
login::{serverbound_hello_packet::ServerboundHelloPacket, ServerboundLoginPacket},
|
||||
status::{
|
||||
clientbound_pong_response_packet::ClientboundPongResponsePacket,
|
||||
clientbound_status_response_packet::{
|
||||
ClientboundStatusResponsePacket, Players, Version,
|
||||
},
|
||||
ServerboundStatusPacket,
|
||||
},
|
||||
ConnectionProtocol, PROTOCOL_VERSION,
|
||||
},
|
||||
read::ReadPacketError,
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use log::{error, info, warn};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::error::Error;
|
||||
use tokio::{
|
||||
io::{self, AsyncWriteExt},
|
||||
net::{TcpListener, TcpStream},
|
||||
};
|
||||
use tracing::Level;
|
||||
|
||||
const LISTEN_ADDR: &str = "127.0.0.1:25566";
|
||||
const PROXY_ADDR: &str = "127.0.0.1:25565";
|
||||
|
||||
const PROXY_DESC: &str = "An Azalea Minecraft Proxy";
|
||||
|
||||
// String must be formatted like "data:image/png;base64,<data>"
|
||||
static PROXY_FAVICON: Lazy<Option<String>> = Lazy::new(|| None);
|
||||
|
||||
static PROXY_VERSION: Lazy<Version> = Lazy::new(|| Version {
|
||||
name: "1.19.3".to_string(),
|
||||
protocol: PROTOCOL_VERSION as i32,
|
||||
});
|
||||
|
||||
const PROXY_PLAYERS: Players = Players {
|
||||
max: 1,
|
||||
online: 0,
|
||||
sample: Vec::new(),
|
||||
};
|
||||
|
||||
const PROXY_PREVIEWS_CHAT: Option<bool> = Some(false);
|
||||
const PROXY_SECURE_CHAT: Option<bool> = Some(false);
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt().with_max_level(Level::INFO).init();
|
||||
|
||||
// Bind to an address and port
|
||||
let listener = TcpListener::bind(LISTEN_ADDR).await?;
|
||||
loop {
|
||||
// When a connection is made, pass it off to another thread
|
||||
let (stream, _) = listener.accept().await?;
|
||||
tokio::spawn(handle_connection(stream));
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_connection(stream: TcpStream) -> anyhow::Result<()> {
|
||||
stream.set_nodelay(true)?;
|
||||
let ip = stream.peer_addr()?;
|
||||
let mut conn: Connection<ServerboundHandshakePacket, ClientboundHandshakePacket> =
|
||||
Connection::wrap(stream);
|
||||
|
||||
// The first packet sent from a client is the intent packet.
|
||||
// This specifies whether the client is pinging
|
||||
// the server or is going to join the game.
|
||||
let intent = match conn.read().await {
|
||||
Ok(packet) => match packet {
|
||||
ServerboundHandshakePacket::ClientIntention(packet) => {
|
||||
info!(
|
||||
"New connection: {0}, Version {1}, {2:?}",
|
||||
ip.ip(),
|
||||
packet.protocol_version,
|
||||
packet.intention
|
||||
);
|
||||
packet
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let e = e.into();
|
||||
warn!("Error during intent: {e}");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
match intent.intention {
|
||||
// If the client is pinging the proxy,
|
||||
// reply with the information below.
|
||||
ConnectionProtocol::Status => {
|
||||
let mut conn = conn.status();
|
||||
loop {
|
||||
match conn.read().await {
|
||||
Ok(p) => match p {
|
||||
ServerboundStatusPacket::StatusRequest(_) => {
|
||||
conn.write(
|
||||
ClientboundStatusResponsePacket {
|
||||
description: PROXY_DESC.into(),
|
||||
favicon: PROXY_FAVICON.clone(),
|
||||
players: PROXY_PLAYERS.clone(),
|
||||
version: PROXY_VERSION.clone(),
|
||||
previews_chat: PROXY_PREVIEWS_CHAT,
|
||||
enforces_secure_chat: PROXY_SECURE_CHAT,
|
||||
}
|
||||
.get(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ServerboundStatusPacket::PingRequest(p) => {
|
||||
conn.write(ClientboundPongResponsePacket { time: p.time }.get())
|
||||
.await?;
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(e) => match *e {
|
||||
ReadPacketError::ConnectionClosed => {
|
||||
break;
|
||||
}
|
||||
e => {
|
||||
warn!("Error during status: {e}");
|
||||
return Err(e.into());
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the client intends to join the proxy,
|
||||
// wait for them to send the `Hello` packet to
|
||||
// log their username and uuid, then forward the
|
||||
// connection along to the proxy target.
|
||||
ConnectionProtocol::Login => {
|
||||
let mut conn = conn.login();
|
||||
loop {
|
||||
match conn.read().await {
|
||||
Ok(p) => {
|
||||
if let ServerboundLoginPacket::Hello(hello) = p {
|
||||
info!(
|
||||
"Player \'{0}\' from {1} logging in with uuid: {2}",
|
||||
hello.name,
|
||||
ip.ip(),
|
||||
if let Some(id) = hello.profile_id {
|
||||
id.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
);
|
||||
|
||||
tokio::spawn(transfer(conn.unwrap()?, intent, hello).map(|r| {
|
||||
if let Err(e) = r {
|
||||
error!("Failed to proxy: {e}");
|
||||
}
|
||||
}));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => match *e {
|
||||
ReadPacketError::ConnectionClosed => {
|
||||
break;
|
||||
}
|
||||
e => {
|
||||
warn!("Error during login: {e}");
|
||||
return Err(e.into());
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
warn!("Client provided weird intent: {:?}", intent.intention);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn transfer(
|
||||
mut inbound: TcpStream,
|
||||
intent: ClientIntentionPacket,
|
||||
hello: ServerboundHelloPacket,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let outbound = TcpStream::connect(PROXY_ADDR).await?;
|
||||
let name = hello.name.clone();
|
||||
outbound.set_nodelay(true)?;
|
||||
|
||||
// Repeat the intent and hello packet
|
||||
// recieved earlier to the proxy target
|
||||
let mut outbound_conn: Connection<ClientboundHandshakePacket, ServerboundHandshakePacket> =
|
||||
Connection::wrap(outbound);
|
||||
outbound_conn.write(intent.get()).await?;
|
||||
|
||||
let mut outbound_conn = outbound_conn.login();
|
||||
outbound_conn.write(hello.get()).await?;
|
||||
|
||||
let mut outbound = outbound_conn.unwrap()?;
|
||||
|
||||
// Split the incoming and outgoing connections in
|
||||
// halves and handle each pair on separate threads.
|
||||
let (mut ri, mut wi) = inbound.split();
|
||||
let (mut ro, mut wo) = outbound.split();
|
||||
|
||||
let client_to_server = async {
|
||||
io::copy(&mut ri, &mut wo).await?;
|
||||
wo.shutdown().await
|
||||
};
|
||||
|
||||
let server_to_client = async {
|
||||
io::copy(&mut ro, &mut wi).await?;
|
||||
wi.shutdown().await
|
||||
};
|
||||
|
||||
tokio::try_join!(client_to_server, server_to_client)?;
|
||||
info!("Player \'{name}\' left the game");
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -8,7 +8,8 @@ use crate::packets::status::{ClientboundStatusPacket, ServerboundStatusPacket};
|
|||
use crate::packets::ProtocolPacket;
|
||||
use crate::read::{read_packet, ReadPacketError};
|
||||
use crate::write::write_packet;
|
||||
use azalea_auth::sessionserver::SessionServerError;
|
||||
use azalea_auth::game_profile::GameProfile;
|
||||
use azalea_auth::sessionserver::{ClientSessionServerError, ServerSessionServerError};
|
||||
use azalea_crypto::{Aes128CfbDec, Aes128CfbEnc};
|
||||
use bytes::BytesMut;
|
||||
use log::{error, info};
|
||||
|
@ -17,7 +18,7 @@ use std::marker::PhantomData;
|
|||
use std::net::SocketAddr;
|
||||
use thiserror::Error;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
|
||||
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf, ReuniteError};
|
||||
use tokio::net::TcpStream;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -304,7 +305,7 @@ impl Connection<ClientboundLoginPacket, ServerboundLoginPacket> {
|
|||
/// let e = azalea_crypto::encrypt(&p.public_key, &p.nonce).unwrap();
|
||||
/// conn.authenticate(
|
||||
/// &access_token,
|
||||
/// &Uuid::parse_str(&profile.id).expect("Invalid UUID"),
|
||||
/// &profile.id,
|
||||
/// e.secret_key,
|
||||
/// &p
|
||||
/// ).await?;
|
||||
|
@ -327,7 +328,7 @@ impl Connection<ClientboundLoginPacket, ServerboundLoginPacket> {
|
|||
uuid: &Uuid,
|
||||
private_key: [u8; 16],
|
||||
packet: &ClientboundHelloPacket,
|
||||
) -> Result<(), SessionServerError> {
|
||||
) -> Result<(), ClientSessionServerError> {
|
||||
azalea_auth::sessionserver::join(
|
||||
access_token,
|
||||
&packet.public_key,
|
||||
|
@ -339,6 +340,63 @@ impl Connection<ClientboundLoginPacket, ServerboundLoginPacket> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Connection<ServerboundHandshakePacket, ClientboundHandshakePacket> {
|
||||
/// Change our state from handshake to login. This is the state that is used
|
||||
/// for logging in.
|
||||
pub fn login(self) -> Connection<ServerboundLoginPacket, ClientboundLoginPacket> {
|
||||
Connection::from(self)
|
||||
}
|
||||
|
||||
/// Change our state from handshake to status. This is the state that is
|
||||
/// used for pinging the server.
|
||||
pub fn status(self) -> Connection<ServerboundStatusPacket, ClientboundStatusPacket> {
|
||||
Connection::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Connection<ServerboundLoginPacket, ClientboundLoginPacket> {
|
||||
/// Set our compression threshold, i.e. the maximum size that a packet is
|
||||
/// allowed to be without getting compressed. If you set it to less than 0
|
||||
/// then compression gets disabled.
|
||||
pub fn set_compression_threshold(&mut self, threshold: i32) {
|
||||
// if you pass a threshold of less than 0, compression is disabled
|
||||
if threshold >= 0 {
|
||||
self.reader.compression_threshold = Some(threshold as u32);
|
||||
self.writer.compression_threshold = Some(threshold as u32);
|
||||
} else {
|
||||
self.reader.compression_threshold = None;
|
||||
self.writer.compression_threshold = None;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the encryption key that is used to encrypt and decrypt packets. It's
|
||||
/// the same for both reading and writing.
|
||||
pub fn set_encryption_key(&mut self, key: [u8; 16]) {
|
||||
let (enc_cipher, dec_cipher) = azalea_crypto::create_cipher(&key);
|
||||
self.reader.dec_cipher = Some(dec_cipher);
|
||||
self.writer.enc_cipher = Some(enc_cipher);
|
||||
}
|
||||
|
||||
/// Change our state from login to game. This is the state that's used when
|
||||
/// the client is actually in the game.
|
||||
pub fn game(self) -> Connection<ServerboundGamePacket, ClientboundGamePacket> {
|
||||
Connection::from(self)
|
||||
}
|
||||
|
||||
/// Verify connecting clients have authenticated with Minecraft's servers.
|
||||
/// This must happen after the client sends a `ServerboundLoginPacket::Key`
|
||||
/// packet.
|
||||
pub async fn authenticate(
|
||||
&self,
|
||||
username: &str,
|
||||
public_key: &[u8],
|
||||
private_key: &[u8; 16],
|
||||
ip: Option<&str>,
|
||||
) -> Result<GameProfile, ServerSessionServerError> {
|
||||
azalea_auth::sessionserver::serverside_auth(username, public_key, private_key, ip).await
|
||||
}
|
||||
}
|
||||
|
||||
// rust doesn't let us implement From because allegedly it conflicts with
|
||||
// `core`'s "impl<T> From<T> for T" so we do this instead
|
||||
impl<R1, W1> Connection<R1, W1>
|
||||
|
@ -390,4 +448,9 @@ where
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from a `Connection` into a `TcpStream`. Useful for servers.
|
||||
pub fn unwrap(self) -> Result<TcpStream, ReuniteError> {
|
||||
self.reader.read_stream.reunite(self.writer.write_stream)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use azalea_buf::McBuf;
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
|
||||
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
|
||||
#[derive(Clone, Debug, ClientboundGamePacket, McBuf)]
|
||||
pub struct ClientboundInitializeBorderPacket {
|
||||
pub new_center_x: f64,
|
||||
pub new_center_z: f64,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufWritable};
|
||||
use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable};
|
||||
use azalea_core::ParticleData;
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
use std::io::{Cursor, Write};
|
||||
|
@ -51,7 +51,18 @@ impl McBufReadable for ClientboundLevelParticlesPacket {
|
|||
}
|
||||
|
||||
impl McBufWritable for ClientboundLevelParticlesPacket {
|
||||
fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
todo!();
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
self.particle_id.var_write_into(buf)?;
|
||||
self.override_limiter.write_into(buf)?;
|
||||
self.x.write_into(buf)?;
|
||||
self.y.write_into(buf)?;
|
||||
self.z.write_into(buf)?;
|
||||
self.x_dist.write_into(buf)?;
|
||||
self.y_dist.write_into(buf)?;
|
||||
self.z_dist.write_into(buf)?;
|
||||
self.max_speed.write_into(buf)?;
|
||||
self.count.write_into(buf)?;
|
||||
self.data.write_without_id(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use azalea_buf::{
|
|||
BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
|
||||
};
|
||||
use azalea_chat::FormattedText;
|
||||
use azalea_core::{BitSet, GameType};
|
||||
use azalea_core::{FixedBitSet, GameType};
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
|
@ -151,7 +151,7 @@ impl McBufWritable for ClientboundPlayerInfoUpdatePacket {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ActionEnumSet {
|
||||
pub add_player: bool,
|
||||
pub initialize_chat: bool,
|
||||
|
@ -163,7 +163,7 @@ pub struct ActionEnumSet {
|
|||
|
||||
impl McBufReadable for ActionEnumSet {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let set = BitSet::read_fixed(buf, 6)?;
|
||||
let set = FixedBitSet::<6>::read_from(buf)?;
|
||||
Ok(ActionEnumSet {
|
||||
add_player: set.index(0),
|
||||
initialize_chat: set.index(1),
|
||||
|
@ -177,7 +177,7 @@ impl McBufReadable for ActionEnumSet {
|
|||
|
||||
impl McBufWritable for ActionEnumSet {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let mut set = BitSet::new(6);
|
||||
let mut set = FixedBitSet::<6>::new();
|
||||
if self.add_player {
|
||||
set.set(0);
|
||||
}
|
||||
|
@ -196,6 +196,29 @@ impl McBufWritable for ActionEnumSet {
|
|||
if self.update_display_name {
|
||||
set.set(5);
|
||||
}
|
||||
set.write_into(buf)
|
||||
set.write_into(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_action_enum_set() {
|
||||
let data = ActionEnumSet {
|
||||
add_player: true,
|
||||
initialize_chat: false,
|
||||
update_game_mode: true,
|
||||
update_listed: false,
|
||||
update_latency: true,
|
||||
update_display_name: false,
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
data.write_into(&mut buf).unwrap();
|
||||
let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
|
||||
let read_data = ActionEnumSet::read_from(&mut data_cursor).unwrap();
|
||||
assert_eq!(read_data, data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ impl McBufReadable for TagMap {
|
|||
|
||||
impl McBufWritable for TagMap {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
(self.len() as u32).write_into(buf)?;
|
||||
(self.len() as u32).var_write_into(buf)?;
|
||||
for (k, v) in &self.0 {
|
||||
k.write_into(buf)?;
|
||||
v.write_into(buf)?;
|
||||
|
|
|
@ -23,7 +23,7 @@ impl McBufWritable for ServerboundPlayerAbilitiesPacket {
|
|||
if self.is_flying {
|
||||
byte |= 2;
|
||||
}
|
||||
byte.write_into(buf)?;
|
||||
u8::write_into(&byte, buf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ impl McBufWritable for ServerboundPlayerInputPacket {
|
|||
fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> {
|
||||
self.xxa.write_into(buf)?;
|
||||
self.zza.write_into(buf)?;
|
||||
let mut byte = 0;
|
||||
let mut byte = 0u8;
|
||||
if self.is_jumping {
|
||||
byte |= 1;
|
||||
}
|
||||
|
|
|
@ -91,8 +91,7 @@ impl fmt::Display for BitStorageError {
|
|||
match self {
|
||||
BitStorageError::InvalidLength { got, expected } => write!(
|
||||
f,
|
||||
"Invalid length given for storage, got: {}, but expected: {}",
|
||||
got, expected
|
||||
"Invalid length given for storage, got: {got}, but expected: {expected}",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,4 @@ tokio = "^1.23.1"
|
|||
uuid = "1.2.2"
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "^1.0.65"
|
||||
env_logger = "^0.9.1"
|
||||
tokio = "^1.23.1"
|
||||
env_logger = "^0.10.0"
|
||||
|
|
|
@ -101,32 +101,24 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
|
|||
|profile: &&GameProfileComponent| profile.name == sender,
|
||||
);
|
||||
if let Some(entity) = entity {
|
||||
match m.content().as_str() {
|
||||
"goto" => {
|
||||
let entity_pos = bot.entity_component::<Position>(entity);
|
||||
let target_pos: BlockPos = entity_pos.into();
|
||||
bot.goto(BlockPosGoal::from(target_pos));
|
||||
}
|
||||
"look" => {
|
||||
let entity_pos = bot.entity_component::<Position>(entity);
|
||||
let target_pos: BlockPos = entity_pos.into();
|
||||
println!("target_pos: {target_pos:?}");
|
||||
bot.look_at(target_pos.center());
|
||||
}
|
||||
"jump" => {
|
||||
bot.set_jumping(true);
|
||||
}
|
||||
"walk" => {
|
||||
bot.walk(WalkDirection::Forward);
|
||||
}
|
||||
"stop" => {
|
||||
bot.set_jumping(false);
|
||||
bot.walk(WalkDirection::None);
|
||||
}
|
||||
"lag" => {
|
||||
std::thread::sleep(Duration::from_millis(1000));
|
||||
}
|
||||
_ => {}
|
||||
if m.content() == "goto" {
|
||||
let entity_pos = bot.entity_component::<Position>(entity);
|
||||
let target_pos: BlockPos = entity_pos.into();
|
||||
bot.goto(BlockPosGoal::from(target_pos));
|
||||
} else if m.content() == "look" {
|
||||
let entity_pos = bot.entity_component::<Position>(entity);
|
||||
let target_pos: BlockPos = entity_pos.into();
|
||||
println!("target_pos: {target_pos:?}");
|
||||
bot.look_at(target_pos.center());
|
||||
} else if m.content() == "jump" {
|
||||
bot.set_jumping(true);
|
||||
} else if m.content() == "walk" {
|
||||
bot.walk(WalkDirection::Forward);
|
||||
} else if m.content() == "stop" {
|
||||
bot.set_jumping(false);
|
||||
bot.walk(WalkDirection::None);
|
||||
} else if m.content() == "lag" {
|
||||
std::thread::sleep(Duration::from_millis(1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue