├── .github └── workflows │ ├── audit.yml │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── discord ├── Cargo.toml └── src │ └── main.rs ├── mcping ├── Cargo.toml ├── README.md ├── examples │ └── cli.rs └── src │ ├── bedrock.rs │ ├── java.rs │ ├── lib.rs │ └── tokio │ ├── bedrock.rs │ ├── java.rs │ └── mod.rs └── screenshot.png /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | - cron: '0 0 * * 0' 4 | push: 5 | paths: 6 | - '**/Cargo.toml' 7 | - '**/Cargo.lock' 8 | branches: 9 | - master 10 | pull_request: 11 | paths: 12 | - '**/Cargo.toml' 13 | - '**/Cargo.lock' 14 | 15 | name: Rust Security audit 16 | 17 | jobs: 18 | audit: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v2 22 | - uses: actions-rs/audit-check@v1 23 | with: 24 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: CI 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | profile: minimal 14 | toolchain: stable 15 | - uses: actions-rs/cargo@v1 16 | with: 17 | command: check 18 | args: --all-features 19 | 20 | test: 21 | name: Test 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions-rs/toolchain@v1 26 | with: 27 | profile: minimal 28 | toolchain: stable 29 | - uses: actions-rs/cargo@v1 30 | with: 31 | command: test 32 | args: --all-features 33 | 34 | fmt: 35 | name: Rustfmt 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: actions-rs/toolchain@v1 40 | with: 41 | profile: minimal 42 | toolchain: stable 43 | components: rustfmt 44 | - uses: actions-rs/cargo@v1 45 | with: 46 | command: fmt 47 | args: --all -- --check 48 | 49 | clippy: 50 | name: Clippy 51 | runs-on: ubuntu-latest 52 | steps: 53 | - uses: actions/checkout@v2 54 | - uses: actions-rs/toolchain@v1 55 | with: 56 | profile: minimal 57 | toolchain: stable 58 | components: clippy 59 | - uses: actions-rs/cargo@v1 60 | with: 61 | command: clippy 62 | args: --all-features -- -D warnings 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | config.toml 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "adler" 5 | version = "0.2.3" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" 8 | 9 | [[package]] 10 | name = "adler32" 11 | version = "1.2.0" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" 14 | 15 | [[package]] 16 | name = "ansi_colours" 17 | version = "1.0.2" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "52cb663b84aea8670b4a40368360e29485c11b03d14ff6283261aeccd69d5ce1" 20 | dependencies = [ 21 | "cc", 22 | ] 23 | 24 | [[package]] 25 | name = "anyhow" 26 | version = "1.0.38" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" 29 | 30 | [[package]] 31 | name = "argh" 32 | version = "0.1.4" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "91792f088f87cdc7a2cfb1d617fa5ea18d7f1dc22ef0e1b5f82f3157cdc522be" 35 | dependencies = [ 36 | "argh_derive", 37 | "argh_shared", 38 | ] 39 | 40 | [[package]] 41 | name = "argh_derive" 42 | version = "0.1.4" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "c4eb0c0c120ad477412dc95a4ce31e38f2113e46bd13511253f79196ca68b067" 45 | dependencies = [ 46 | "argh_shared", 47 | "heck", 48 | "proc-macro2", 49 | "quote", 50 | "syn", 51 | ] 52 | 53 | [[package]] 54 | name = "argh_shared" 55 | version = "0.1.4" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "781f336cc9826dbaddb9754cb5db61e64cab4f69668bd19dcc4a0394a86f4cb1" 58 | 59 | [[package]] 60 | name = "async-trait" 61 | version = "0.1.48" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf" 64 | dependencies = [ 65 | "proc-macro2", 66 | "quote", 67 | "syn", 68 | ] 69 | 70 | [[package]] 71 | name = "atty" 72 | version = "0.2.14" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 75 | dependencies = [ 76 | "hermit-abi", 77 | "libc", 78 | "winapi 0.3.9", 79 | ] 80 | 81 | [[package]] 82 | name = "autocfg" 83 | version = "1.0.1" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 86 | 87 | [[package]] 88 | name = "base64" 89 | version = "0.11.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" 92 | 93 | [[package]] 94 | name = "base64" 95 | version = "0.12.3" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 98 | 99 | [[package]] 100 | name = "base64" 101 | version = "0.13.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 104 | 105 | [[package]] 106 | name = "bitflags" 107 | version = "1.2.1" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 110 | 111 | [[package]] 112 | name = "block-buffer" 113 | version = "0.9.0" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 116 | dependencies = [ 117 | "generic-array", 118 | ] 119 | 120 | [[package]] 121 | name = "bumpalo" 122 | version = "3.6.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" 125 | 126 | [[package]] 127 | name = "bytemuck" 128 | version = "1.5.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "5a4bad0c5981acc24bc09e532f35160f952e35422603f0563cd7a73c2c2e65a0" 131 | 132 | [[package]] 133 | name = "byteorder" 134 | version = "1.4.2" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" 137 | 138 | [[package]] 139 | name = "bytes" 140 | version = "0.5.6" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" 143 | 144 | [[package]] 145 | name = "bytes" 146 | version = "1.0.1" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" 149 | 150 | [[package]] 151 | name = "cc" 152 | version = "1.0.66" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" 155 | 156 | [[package]] 157 | name = "cfg-if" 158 | version = "0.1.10" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 161 | 162 | [[package]] 163 | name = "cfg-if" 164 | version = "1.0.0" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 167 | 168 | [[package]] 169 | name = "chrono" 170 | version = "0.4.19" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 173 | dependencies = [ 174 | "libc", 175 | "num-integer", 176 | "num-traits", 177 | "serde", 178 | "time", 179 | "winapi 0.3.9", 180 | ] 181 | 182 | [[package]] 183 | name = "color_quant" 184 | version = "1.1.0" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" 187 | 188 | [[package]] 189 | name = "colored" 190 | version = "2.0.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" 193 | dependencies = [ 194 | "atty", 195 | "lazy_static", 196 | "winapi 0.3.9", 197 | ] 198 | 199 | [[package]] 200 | name = "command_attr" 201 | version = "0.2.0" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "c27d6155f93d880b6379d93ddc9b2417b3b69b715360c5f25525e4576338a381" 204 | dependencies = [ 205 | "proc-macro2", 206 | "quote", 207 | "syn", 208 | ] 209 | 210 | [[package]] 211 | name = "console" 212 | version = "0.14.1" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" 215 | dependencies = [ 216 | "encode_unicode", 217 | "lazy_static", 218 | "libc", 219 | "terminal_size", 220 | "winapi 0.3.9", 221 | ] 222 | 223 | [[package]] 224 | name = "const_fn" 225 | version = "0.4.5" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" 228 | 229 | [[package]] 230 | name = "cpuid-bool" 231 | version = "0.1.2" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" 234 | 235 | [[package]] 236 | name = "crc32fast" 237 | version = "1.2.1" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 240 | dependencies = [ 241 | "cfg-if 1.0.0", 242 | ] 243 | 244 | [[package]] 245 | name = "crossbeam-channel" 246 | version = "0.5.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" 249 | dependencies = [ 250 | "cfg-if 1.0.0", 251 | "crossbeam-utils", 252 | ] 253 | 254 | [[package]] 255 | name = "crossbeam-deque" 256 | version = "0.8.0" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" 259 | dependencies = [ 260 | "cfg-if 1.0.0", 261 | "crossbeam-epoch", 262 | "crossbeam-utils", 263 | ] 264 | 265 | [[package]] 266 | name = "crossbeam-epoch" 267 | version = "0.9.1" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" 270 | dependencies = [ 271 | "cfg-if 1.0.0", 272 | "const_fn", 273 | "crossbeam-utils", 274 | "lazy_static", 275 | "memoffset", 276 | "scopeguard", 277 | ] 278 | 279 | [[package]] 280 | name = "crossbeam-utils" 281 | version = "0.8.1" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" 284 | dependencies = [ 285 | "autocfg", 286 | "cfg-if 1.0.0", 287 | "lazy_static", 288 | ] 289 | 290 | [[package]] 291 | name = "crossterm" 292 | version = "0.19.0" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c" 295 | dependencies = [ 296 | "bitflags", 297 | "crossterm_winapi", 298 | "lazy_static", 299 | "libc", 300 | "mio 0.7.7", 301 | "parking_lot", 302 | "signal-hook", 303 | "winapi 0.3.9", 304 | ] 305 | 306 | [[package]] 307 | name = "crossterm_winapi" 308 | version = "0.7.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9" 311 | dependencies = [ 312 | "winapi 0.3.9", 313 | ] 314 | 315 | [[package]] 316 | name = "data-encoding" 317 | version = "2.3.2" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" 320 | 321 | [[package]] 322 | name = "deflate" 323 | version = "0.8.6" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" 326 | dependencies = [ 327 | "adler32", 328 | "byteorder", 329 | ] 330 | 331 | [[package]] 332 | name = "digest" 333 | version = "0.9.0" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 336 | dependencies = [ 337 | "generic-array", 338 | ] 339 | 340 | [[package]] 341 | name = "either" 342 | version = "1.6.1" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 345 | 346 | [[package]] 347 | name = "encode_unicode" 348 | version = "0.3.6" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 351 | 352 | [[package]] 353 | name = "encoding_rs" 354 | version = "0.8.28" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" 357 | dependencies = [ 358 | "cfg-if 1.0.0", 359 | ] 360 | 361 | [[package]] 362 | name = "enum-as-inner" 363 | version = "0.3.3" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" 366 | dependencies = [ 367 | "heck", 368 | "proc-macro2", 369 | "quote", 370 | "syn", 371 | ] 372 | 373 | [[package]] 374 | name = "flate2" 375 | version = "1.0.20" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" 378 | dependencies = [ 379 | "cfg-if 1.0.0", 380 | "crc32fast", 381 | "libc", 382 | "miniz_oxide 0.4.3", 383 | ] 384 | 385 | [[package]] 386 | name = "fnv" 387 | version = "1.0.7" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 390 | 391 | [[package]] 392 | name = "form_urlencoded" 393 | version = "1.0.0" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" 396 | dependencies = [ 397 | "matches", 398 | "percent-encoding", 399 | ] 400 | 401 | [[package]] 402 | name = "fuchsia-zircon" 403 | version = "0.3.3" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 406 | dependencies = [ 407 | "bitflags", 408 | "fuchsia-zircon-sys", 409 | ] 410 | 411 | [[package]] 412 | name = "fuchsia-zircon-sys" 413 | version = "0.3.3" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 416 | 417 | [[package]] 418 | name = "futures-channel" 419 | version = "0.3.13" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" 422 | dependencies = [ 423 | "futures-core", 424 | ] 425 | 426 | [[package]] 427 | name = "futures-core" 428 | version = "0.3.13" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" 431 | 432 | [[package]] 433 | name = "futures-io" 434 | version = "0.3.13" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" 437 | 438 | [[package]] 439 | name = "futures-macro" 440 | version = "0.3.13" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" 443 | dependencies = [ 444 | "proc-macro-hack", 445 | "proc-macro2", 446 | "quote", 447 | "syn", 448 | ] 449 | 450 | [[package]] 451 | name = "futures-sink" 452 | version = "0.3.13" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" 455 | 456 | [[package]] 457 | name = "futures-task" 458 | version = "0.3.13" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" 461 | 462 | [[package]] 463 | name = "futures-util" 464 | version = "0.3.13" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" 467 | dependencies = [ 468 | "futures-core", 469 | "futures-io", 470 | "futures-macro", 471 | "futures-task", 472 | "memchr", 473 | "pin-project-lite 0.2.4", 474 | "pin-utils", 475 | "proc-macro-hack", 476 | "proc-macro-nested", 477 | "slab", 478 | ] 479 | 480 | [[package]] 481 | name = "generic-array" 482 | version = "0.14.4" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 485 | dependencies = [ 486 | "typenum", 487 | "version_check", 488 | ] 489 | 490 | [[package]] 491 | name = "getrandom" 492 | version = "0.1.16" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 495 | dependencies = [ 496 | "cfg-if 1.0.0", 497 | "libc", 498 | "wasi 0.9.0+wasi-snapshot-preview1", 499 | ] 500 | 501 | [[package]] 502 | name = "getrandom" 503 | version = "0.2.2" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" 506 | dependencies = [ 507 | "cfg-if 1.0.0", 508 | "libc", 509 | "wasi 0.10.2+wasi-snapshot-preview1", 510 | ] 511 | 512 | [[package]] 513 | name = "gif" 514 | version = "0.11.1" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "02efba560f227847cb41463a7395c514d127d4f74fff12ef0137fff1b84b96c4" 517 | dependencies = [ 518 | "color_quant", 519 | "weezl", 520 | ] 521 | 522 | [[package]] 523 | name = "h2" 524 | version = "0.2.7" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" 527 | dependencies = [ 528 | "bytes 0.5.6", 529 | "fnv", 530 | "futures-core", 531 | "futures-sink", 532 | "futures-util", 533 | "http", 534 | "indexmap", 535 | "slab", 536 | "tokio 0.2.25", 537 | "tokio-util", 538 | "tracing", 539 | "tracing-futures", 540 | ] 541 | 542 | [[package]] 543 | name = "hashbrown" 544 | version = "0.9.1" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 547 | 548 | [[package]] 549 | name = "heck" 550 | version = "0.3.2" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" 553 | dependencies = [ 554 | "unicode-segmentation", 555 | ] 556 | 557 | [[package]] 558 | name = "hermit-abi" 559 | version = "0.1.18" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 562 | dependencies = [ 563 | "libc", 564 | ] 565 | 566 | [[package]] 567 | name = "hostname" 568 | version = "0.3.1" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" 571 | dependencies = [ 572 | "libc", 573 | "match_cfg", 574 | "winapi 0.3.9", 575 | ] 576 | 577 | [[package]] 578 | name = "http" 579 | version = "0.2.3" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" 582 | dependencies = [ 583 | "bytes 1.0.1", 584 | "fnv", 585 | "itoa", 586 | ] 587 | 588 | [[package]] 589 | name = "http-body" 590 | version = "0.3.1" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" 593 | dependencies = [ 594 | "bytes 0.5.6", 595 | "http", 596 | ] 597 | 598 | [[package]] 599 | name = "httparse" 600 | version = "1.3.5" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" 603 | 604 | [[package]] 605 | name = "httpdate" 606 | version = "0.3.2" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" 609 | 610 | [[package]] 611 | name = "hyper" 612 | version = "0.13.10" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "8a6f157065790a3ed2f88679250419b5cdd96e714a0d65f7797fd337186e96bb" 615 | dependencies = [ 616 | "bytes 0.5.6", 617 | "futures-channel", 618 | "futures-core", 619 | "futures-util", 620 | "h2", 621 | "http", 622 | "http-body", 623 | "httparse", 624 | "httpdate", 625 | "itoa", 626 | "pin-project", 627 | "socket2", 628 | "tokio 0.2.25", 629 | "tower-service", 630 | "tracing", 631 | "want", 632 | ] 633 | 634 | [[package]] 635 | name = "hyper-rustls" 636 | version = "0.21.0" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "37743cc83e8ee85eacfce90f2f4102030d9ff0a95244098d781e9bee4a90abb6" 639 | dependencies = [ 640 | "bytes 0.5.6", 641 | "futures-util", 642 | "hyper", 643 | "log", 644 | "rustls 0.18.1", 645 | "tokio 0.2.25", 646 | "tokio-rustls", 647 | "webpki", 648 | ] 649 | 650 | [[package]] 651 | name = "idna" 652 | version = "0.2.1" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094" 655 | dependencies = [ 656 | "matches", 657 | "unicode-bidi", 658 | "unicode-normalization", 659 | ] 660 | 661 | [[package]] 662 | name = "image" 663 | version = "0.23.13" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "293f07a1875fa7e9c5897b51aa68b2d8ed8271b87e1a44cb64b9c3d98aabbc0d" 666 | dependencies = [ 667 | "bytemuck", 668 | "byteorder", 669 | "color_quant", 670 | "gif", 671 | "jpeg-decoder", 672 | "num-iter", 673 | "num-rational", 674 | "num-traits", 675 | "png", 676 | "scoped_threadpool", 677 | "tiff", 678 | ] 679 | 680 | [[package]] 681 | name = "indexmap" 682 | version = "1.6.1" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" 685 | dependencies = [ 686 | "autocfg", 687 | "hashbrown", 688 | ] 689 | 690 | [[package]] 691 | name = "input_buffer" 692 | version = "0.3.1" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" 695 | dependencies = [ 696 | "bytes 0.5.6", 697 | ] 698 | 699 | [[package]] 700 | name = "instant" 701 | version = "0.1.9" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" 704 | dependencies = [ 705 | "cfg-if 1.0.0", 706 | ] 707 | 708 | [[package]] 709 | name = "iovec" 710 | version = "0.1.4" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 713 | dependencies = [ 714 | "libc", 715 | ] 716 | 717 | [[package]] 718 | name = "ipconfig" 719 | version = "0.2.2" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" 722 | dependencies = [ 723 | "socket2", 724 | "widestring", 725 | "winapi 0.3.9", 726 | "winreg 0.6.2", 727 | ] 728 | 729 | [[package]] 730 | name = "ipnet" 731 | version = "2.3.0" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" 734 | 735 | [[package]] 736 | name = "itertools" 737 | version = "0.10.0" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" 740 | dependencies = [ 741 | "either", 742 | ] 743 | 744 | [[package]] 745 | name = "itoa" 746 | version = "0.4.7" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 749 | 750 | [[package]] 751 | name = "jpeg-decoder" 752 | version = "0.1.22" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" 755 | dependencies = [ 756 | "rayon", 757 | ] 758 | 759 | [[package]] 760 | name = "js-sys" 761 | version = "0.3.47" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" 764 | dependencies = [ 765 | "wasm-bindgen", 766 | ] 767 | 768 | [[package]] 769 | name = "kernel32-sys" 770 | version = "0.2.2" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 773 | dependencies = [ 774 | "winapi 0.2.8", 775 | "winapi-build", 776 | ] 777 | 778 | [[package]] 779 | name = "lazy_static" 780 | version = "1.4.0" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 783 | 784 | [[package]] 785 | name = "libc" 786 | version = "0.2.86" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" 789 | 790 | [[package]] 791 | name = "linked-hash-map" 792 | version = "0.5.4" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" 795 | 796 | [[package]] 797 | name = "lock_api" 798 | version = "0.4.2" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" 801 | dependencies = [ 802 | "scopeguard", 803 | ] 804 | 805 | [[package]] 806 | name = "log" 807 | version = "0.4.14" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 810 | dependencies = [ 811 | "cfg-if 1.0.0", 812 | ] 813 | 814 | [[package]] 815 | name = "lru-cache" 816 | version = "0.1.2" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" 819 | dependencies = [ 820 | "linked-hash-map", 821 | ] 822 | 823 | [[package]] 824 | name = "match_cfg" 825 | version = "0.1.0" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" 828 | 829 | [[package]] 830 | name = "matches" 831 | version = "0.1.8" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 834 | 835 | [[package]] 836 | name = "mc-legacy-formatting" 837 | version = "0.3.1" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "4f3670aa273f8809a0b44ffb6c9f982809b3bff471dd77ed1389c084e4c69a05" 840 | dependencies = [ 841 | "bitflags", 842 | "colored", 843 | ] 844 | 845 | [[package]] 846 | name = "mcping" 847 | version = "0.2.0" 848 | dependencies = [ 849 | "argh", 850 | "async-trait", 851 | "base64 0.13.0", 852 | "byteorder", 853 | "image", 854 | "mc-legacy-formatting", 855 | "rand 0.8.3", 856 | "serde", 857 | "serde_json", 858 | "thiserror", 859 | "tokio 1.4.0", 860 | "trust-dns-resolver", 861 | "viuer", 862 | ] 863 | 864 | [[package]] 865 | name = "mcping-discord" 866 | version = "0.1.0" 867 | dependencies = [ 868 | "anyhow", 869 | "base64 0.13.0", 870 | "itertools", 871 | "mcping", 872 | "serde", 873 | "serenity", 874 | "toml", 875 | ] 876 | 877 | [[package]] 878 | name = "memchr" 879 | version = "2.3.4" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 882 | 883 | [[package]] 884 | name = "memoffset" 885 | version = "0.6.1" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" 888 | dependencies = [ 889 | "autocfg", 890 | ] 891 | 892 | [[package]] 893 | name = "mime" 894 | version = "0.3.16" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 897 | 898 | [[package]] 899 | name = "mime_guess" 900 | version = "2.0.3" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 903 | dependencies = [ 904 | "mime", 905 | "unicase", 906 | ] 907 | 908 | [[package]] 909 | name = "miniz_oxide" 910 | version = "0.3.7" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" 913 | dependencies = [ 914 | "adler32", 915 | ] 916 | 917 | [[package]] 918 | name = "miniz_oxide" 919 | version = "0.4.3" 920 | source = "registry+https://github.com/rust-lang/crates.io-index" 921 | checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" 922 | dependencies = [ 923 | "adler", 924 | "autocfg", 925 | ] 926 | 927 | [[package]] 928 | name = "mio" 929 | version = "0.6.23" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" 932 | dependencies = [ 933 | "cfg-if 0.1.10", 934 | "fuchsia-zircon", 935 | "fuchsia-zircon-sys", 936 | "iovec", 937 | "kernel32-sys", 938 | "libc", 939 | "log", 940 | "miow 0.2.2", 941 | "net2", 942 | "slab", 943 | "winapi 0.2.8", 944 | ] 945 | 946 | [[package]] 947 | name = "mio" 948 | version = "0.7.7" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" 951 | dependencies = [ 952 | "libc", 953 | "log", 954 | "miow 0.3.6", 955 | "ntapi", 956 | "winapi 0.3.9", 957 | ] 958 | 959 | [[package]] 960 | name = "miow" 961 | version = "0.2.2" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" 964 | dependencies = [ 965 | "kernel32-sys", 966 | "net2", 967 | "winapi 0.2.8", 968 | "ws2_32-sys", 969 | ] 970 | 971 | [[package]] 972 | name = "miow" 973 | version = "0.3.6" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" 976 | dependencies = [ 977 | "socket2", 978 | "winapi 0.3.9", 979 | ] 980 | 981 | [[package]] 982 | name = "net2" 983 | version = "0.2.37" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" 986 | dependencies = [ 987 | "cfg-if 0.1.10", 988 | "libc", 989 | "winapi 0.3.9", 990 | ] 991 | 992 | [[package]] 993 | name = "ntapi" 994 | version = "0.3.6" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 997 | dependencies = [ 998 | "winapi 0.3.9", 999 | ] 1000 | 1001 | [[package]] 1002 | name = "num-integer" 1003 | version = "0.1.44" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 1006 | dependencies = [ 1007 | "autocfg", 1008 | "num-traits", 1009 | ] 1010 | 1011 | [[package]] 1012 | name = "num-iter" 1013 | version = "0.1.42" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" 1016 | dependencies = [ 1017 | "autocfg", 1018 | "num-integer", 1019 | "num-traits", 1020 | ] 1021 | 1022 | [[package]] 1023 | name = "num-rational" 1024 | version = "0.3.2" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" 1027 | dependencies = [ 1028 | "autocfg", 1029 | "num-integer", 1030 | "num-traits", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "num-traits" 1035 | version = "0.2.14" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 1038 | dependencies = [ 1039 | "autocfg", 1040 | ] 1041 | 1042 | [[package]] 1043 | name = "num_cpus" 1044 | version = "1.13.0" 1045 | source = "registry+https://github.com/rust-lang/crates.io-index" 1046 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 1047 | dependencies = [ 1048 | "hermit-abi", 1049 | "libc", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "once_cell" 1054 | version = "1.5.2" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" 1057 | 1058 | [[package]] 1059 | name = "opaque-debug" 1060 | version = "0.3.0" 1061 | source = "registry+https://github.com/rust-lang/crates.io-index" 1062 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 1063 | 1064 | [[package]] 1065 | name = "parking_lot" 1066 | version = "0.11.1" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" 1069 | dependencies = [ 1070 | "instant", 1071 | "lock_api", 1072 | "parking_lot_core", 1073 | ] 1074 | 1075 | [[package]] 1076 | name = "parking_lot_core" 1077 | version = "0.8.3" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" 1080 | dependencies = [ 1081 | "cfg-if 1.0.0", 1082 | "instant", 1083 | "libc", 1084 | "redox_syscall", 1085 | "smallvec", 1086 | "winapi 0.3.9", 1087 | ] 1088 | 1089 | [[package]] 1090 | name = "percent-encoding" 1091 | version = "2.1.0" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 1094 | 1095 | [[package]] 1096 | name = "pin-project" 1097 | version = "1.0.5" 1098 | source = "registry+https://github.com/rust-lang/crates.io-index" 1099 | checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" 1100 | dependencies = [ 1101 | "pin-project-internal", 1102 | ] 1103 | 1104 | [[package]] 1105 | name = "pin-project-internal" 1106 | version = "1.0.5" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" 1109 | dependencies = [ 1110 | "proc-macro2", 1111 | "quote", 1112 | "syn", 1113 | ] 1114 | 1115 | [[package]] 1116 | name = "pin-project-lite" 1117 | version = "0.1.11" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" 1120 | 1121 | [[package]] 1122 | name = "pin-project-lite" 1123 | version = "0.2.4" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" 1126 | 1127 | [[package]] 1128 | name = "pin-utils" 1129 | version = "0.1.0" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1132 | 1133 | [[package]] 1134 | name = "png" 1135 | version = "0.16.8" 1136 | source = "registry+https://github.com/rust-lang/crates.io-index" 1137 | checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" 1138 | dependencies = [ 1139 | "bitflags", 1140 | "crc32fast", 1141 | "deflate", 1142 | "miniz_oxide 0.3.7", 1143 | ] 1144 | 1145 | [[package]] 1146 | name = "ppv-lite86" 1147 | version = "0.2.10" 1148 | source = "registry+https://github.com/rust-lang/crates.io-index" 1149 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 1150 | 1151 | [[package]] 1152 | name = "proc-macro-hack" 1153 | version = "0.5.19" 1154 | source = "registry+https://github.com/rust-lang/crates.io-index" 1155 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 1156 | 1157 | [[package]] 1158 | name = "proc-macro-nested" 1159 | version = "0.1.7" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" 1162 | 1163 | [[package]] 1164 | name = "proc-macro2" 1165 | version = "1.0.24" 1166 | source = "registry+https://github.com/rust-lang/crates.io-index" 1167 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 1168 | dependencies = [ 1169 | "unicode-xid", 1170 | ] 1171 | 1172 | [[package]] 1173 | name = "quick-error" 1174 | version = "1.2.3" 1175 | source = "registry+https://github.com/rust-lang/crates.io-index" 1176 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 1177 | 1178 | [[package]] 1179 | name = "quote" 1180 | version = "1.0.9" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 1183 | dependencies = [ 1184 | "proc-macro2", 1185 | ] 1186 | 1187 | [[package]] 1188 | name = "rand" 1189 | version = "0.7.3" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1192 | dependencies = [ 1193 | "getrandom 0.1.16", 1194 | "libc", 1195 | "rand_chacha 0.2.2", 1196 | "rand_core 0.5.1", 1197 | "rand_hc 0.2.0", 1198 | ] 1199 | 1200 | [[package]] 1201 | name = "rand" 1202 | version = "0.8.3" 1203 | source = "registry+https://github.com/rust-lang/crates.io-index" 1204 | checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" 1205 | dependencies = [ 1206 | "libc", 1207 | "rand_chacha 0.3.0", 1208 | "rand_core 0.6.2", 1209 | "rand_hc 0.3.0", 1210 | ] 1211 | 1212 | [[package]] 1213 | name = "rand_chacha" 1214 | version = "0.2.2" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1217 | dependencies = [ 1218 | "ppv-lite86", 1219 | "rand_core 0.5.1", 1220 | ] 1221 | 1222 | [[package]] 1223 | name = "rand_chacha" 1224 | version = "0.3.0" 1225 | source = "registry+https://github.com/rust-lang/crates.io-index" 1226 | checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" 1227 | dependencies = [ 1228 | "ppv-lite86", 1229 | "rand_core 0.6.2", 1230 | ] 1231 | 1232 | [[package]] 1233 | name = "rand_core" 1234 | version = "0.5.1" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1237 | dependencies = [ 1238 | "getrandom 0.1.16", 1239 | ] 1240 | 1241 | [[package]] 1242 | name = "rand_core" 1243 | version = "0.6.2" 1244 | source = "registry+https://github.com/rust-lang/crates.io-index" 1245 | checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" 1246 | dependencies = [ 1247 | "getrandom 0.2.2", 1248 | ] 1249 | 1250 | [[package]] 1251 | name = "rand_hc" 1252 | version = "0.2.0" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1255 | dependencies = [ 1256 | "rand_core 0.5.1", 1257 | ] 1258 | 1259 | [[package]] 1260 | name = "rand_hc" 1261 | version = "0.3.0" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" 1264 | dependencies = [ 1265 | "rand_core 0.6.2", 1266 | ] 1267 | 1268 | [[package]] 1269 | name = "rayon" 1270 | version = "1.5.0" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" 1273 | dependencies = [ 1274 | "autocfg", 1275 | "crossbeam-deque", 1276 | "either", 1277 | "rayon-core", 1278 | ] 1279 | 1280 | [[package]] 1281 | name = "rayon-core" 1282 | version = "1.9.0" 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" 1284 | checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" 1285 | dependencies = [ 1286 | "crossbeam-channel", 1287 | "crossbeam-deque", 1288 | "crossbeam-utils", 1289 | "lazy_static", 1290 | "num_cpus", 1291 | ] 1292 | 1293 | [[package]] 1294 | name = "redox_syscall" 1295 | version = "0.2.5" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" 1298 | dependencies = [ 1299 | "bitflags", 1300 | ] 1301 | 1302 | [[package]] 1303 | name = "remove_dir_all" 1304 | version = "0.5.3" 1305 | source = "registry+https://github.com/rust-lang/crates.io-index" 1306 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1307 | dependencies = [ 1308 | "winapi 0.3.9", 1309 | ] 1310 | 1311 | [[package]] 1312 | name = "reqwest" 1313 | version = "0.10.10" 1314 | source = "registry+https://github.com/rust-lang/crates.io-index" 1315 | checksum = "0718f81a8e14c4dbb3b34cf23dc6aaf9ab8a0dfec160c534b3dbca1aaa21f47c" 1316 | dependencies = [ 1317 | "base64 0.13.0", 1318 | "bytes 0.5.6", 1319 | "encoding_rs", 1320 | "futures-core", 1321 | "futures-util", 1322 | "http", 1323 | "http-body", 1324 | "hyper", 1325 | "hyper-rustls", 1326 | "ipnet", 1327 | "js-sys", 1328 | "lazy_static", 1329 | "log", 1330 | "mime", 1331 | "mime_guess", 1332 | "percent-encoding", 1333 | "pin-project-lite 0.2.4", 1334 | "rustls 0.18.1", 1335 | "serde", 1336 | "serde_json", 1337 | "serde_urlencoded", 1338 | "tokio 0.2.25", 1339 | "tokio-rustls", 1340 | "url", 1341 | "wasm-bindgen", 1342 | "wasm-bindgen-futures", 1343 | "web-sys", 1344 | "webpki-roots 0.20.0", 1345 | "winreg 0.7.0", 1346 | ] 1347 | 1348 | [[package]] 1349 | name = "resolv-conf" 1350 | version = "0.7.0" 1351 | source = "registry+https://github.com/rust-lang/crates.io-index" 1352 | checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" 1353 | dependencies = [ 1354 | "hostname", 1355 | "quick-error", 1356 | ] 1357 | 1358 | [[package]] 1359 | name = "ring" 1360 | version = "0.16.20" 1361 | source = "registry+https://github.com/rust-lang/crates.io-index" 1362 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 1363 | dependencies = [ 1364 | "cc", 1365 | "libc", 1366 | "once_cell", 1367 | "spin", 1368 | "untrusted", 1369 | "web-sys", 1370 | "winapi 0.3.9", 1371 | ] 1372 | 1373 | [[package]] 1374 | name = "rustls" 1375 | version = "0.17.0" 1376 | source = "registry+https://github.com/rust-lang/crates.io-index" 1377 | checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" 1378 | dependencies = [ 1379 | "base64 0.11.0", 1380 | "log", 1381 | "ring", 1382 | "sct", 1383 | "webpki", 1384 | ] 1385 | 1386 | [[package]] 1387 | name = "rustls" 1388 | version = "0.18.1" 1389 | source = "registry+https://github.com/rust-lang/crates.io-index" 1390 | checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" 1391 | dependencies = [ 1392 | "base64 0.12.3", 1393 | "log", 1394 | "ring", 1395 | "sct", 1396 | "webpki", 1397 | ] 1398 | 1399 | [[package]] 1400 | name = "ryu" 1401 | version = "1.0.5" 1402 | source = "registry+https://github.com/rust-lang/crates.io-index" 1403 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1404 | 1405 | [[package]] 1406 | name = "scoped_threadpool" 1407 | version = "0.1.9" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" 1410 | 1411 | [[package]] 1412 | name = "scopeguard" 1413 | version = "1.1.0" 1414 | source = "registry+https://github.com/rust-lang/crates.io-index" 1415 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1416 | 1417 | [[package]] 1418 | name = "sct" 1419 | version = "0.6.0" 1420 | source = "registry+https://github.com/rust-lang/crates.io-index" 1421 | checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" 1422 | dependencies = [ 1423 | "ring", 1424 | "untrusted", 1425 | ] 1426 | 1427 | [[package]] 1428 | name = "serde" 1429 | version = "1.0.123" 1430 | source = "registry+https://github.com/rust-lang/crates.io-index" 1431 | checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" 1432 | dependencies = [ 1433 | "serde_derive", 1434 | ] 1435 | 1436 | [[package]] 1437 | name = "serde_derive" 1438 | version = "1.0.123" 1439 | source = "registry+https://github.com/rust-lang/crates.io-index" 1440 | checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" 1441 | dependencies = [ 1442 | "proc-macro2", 1443 | "quote", 1444 | "syn", 1445 | ] 1446 | 1447 | [[package]] 1448 | name = "serde_json" 1449 | version = "1.0.62" 1450 | source = "registry+https://github.com/rust-lang/crates.io-index" 1451 | checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" 1452 | dependencies = [ 1453 | "itoa", 1454 | "ryu", 1455 | "serde", 1456 | ] 1457 | 1458 | [[package]] 1459 | name = "serde_urlencoded" 1460 | version = "0.7.0" 1461 | source = "registry+https://github.com/rust-lang/crates.io-index" 1462 | checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" 1463 | dependencies = [ 1464 | "form_urlencoded", 1465 | "itoa", 1466 | "ryu", 1467 | "serde", 1468 | ] 1469 | 1470 | [[package]] 1471 | name = "serenity" 1472 | version = "0.8.9" 1473 | source = "registry+https://github.com/rust-lang/crates.io-index" 1474 | checksum = "e25ca90a57c2183579d2e7d680df2cb0363b599fc275d2cc3e78e30e9c873dc2" 1475 | dependencies = [ 1476 | "base64 0.12.3", 1477 | "bitflags", 1478 | "chrono", 1479 | "command_attr", 1480 | "flate2", 1481 | "log", 1482 | "parking_lot", 1483 | "reqwest", 1484 | "rustls 0.17.0", 1485 | "serde", 1486 | "serde_json", 1487 | "static_assertions", 1488 | "threadpool", 1489 | "tungstenite", 1490 | "typemap", 1491 | "url", 1492 | "uwl", 1493 | "webpki", 1494 | "webpki-roots 0.19.0", 1495 | ] 1496 | 1497 | [[package]] 1498 | name = "sha-1" 1499 | version = "0.9.4" 1500 | source = "registry+https://github.com/rust-lang/crates.io-index" 1501 | checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f" 1502 | dependencies = [ 1503 | "block-buffer", 1504 | "cfg-if 1.0.0", 1505 | "cpuid-bool", 1506 | "digest", 1507 | "opaque-debug", 1508 | ] 1509 | 1510 | [[package]] 1511 | name = "signal-hook" 1512 | version = "0.1.17" 1513 | source = "registry+https://github.com/rust-lang/crates.io-index" 1514 | checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" 1515 | dependencies = [ 1516 | "libc", 1517 | "mio 0.7.7", 1518 | "signal-hook-registry", 1519 | ] 1520 | 1521 | [[package]] 1522 | name = "signal-hook-registry" 1523 | version = "1.3.0" 1524 | source = "registry+https://github.com/rust-lang/crates.io-index" 1525 | checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" 1526 | dependencies = [ 1527 | "libc", 1528 | ] 1529 | 1530 | [[package]] 1531 | name = "slab" 1532 | version = "0.4.2" 1533 | source = "registry+https://github.com/rust-lang/crates.io-index" 1534 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 1535 | 1536 | [[package]] 1537 | name = "smallvec" 1538 | version = "1.6.1" 1539 | source = "registry+https://github.com/rust-lang/crates.io-index" 1540 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 1541 | 1542 | [[package]] 1543 | name = "socket2" 1544 | version = "0.3.19" 1545 | source = "registry+https://github.com/rust-lang/crates.io-index" 1546 | checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" 1547 | dependencies = [ 1548 | "cfg-if 1.0.0", 1549 | "libc", 1550 | "winapi 0.3.9", 1551 | ] 1552 | 1553 | [[package]] 1554 | name = "spin" 1555 | version = "0.5.2" 1556 | source = "registry+https://github.com/rust-lang/crates.io-index" 1557 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1558 | 1559 | [[package]] 1560 | name = "static_assertions" 1561 | version = "1.1.0" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1564 | 1565 | [[package]] 1566 | name = "syn" 1567 | version = "1.0.67" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "6498a9efc342871f91cc2d0d694c674368b4ceb40f62b65a7a08c3792935e702" 1570 | dependencies = [ 1571 | "proc-macro2", 1572 | "quote", 1573 | "unicode-xid", 1574 | ] 1575 | 1576 | [[package]] 1577 | name = "tempfile" 1578 | version = "3.2.0" 1579 | source = "registry+https://github.com/rust-lang/crates.io-index" 1580 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 1581 | dependencies = [ 1582 | "cfg-if 1.0.0", 1583 | "libc", 1584 | "rand 0.8.3", 1585 | "redox_syscall", 1586 | "remove_dir_all", 1587 | "winapi 0.3.9", 1588 | ] 1589 | 1590 | [[package]] 1591 | name = "termcolor" 1592 | version = "1.1.2" 1593 | source = "registry+https://github.com/rust-lang/crates.io-index" 1594 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 1595 | dependencies = [ 1596 | "winapi-util", 1597 | ] 1598 | 1599 | [[package]] 1600 | name = "terminal_size" 1601 | version = "0.1.16" 1602 | source = "registry+https://github.com/rust-lang/crates.io-index" 1603 | checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406" 1604 | dependencies = [ 1605 | "libc", 1606 | "winapi 0.3.9", 1607 | ] 1608 | 1609 | [[package]] 1610 | name = "thiserror" 1611 | version = "1.0.23" 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" 1613 | checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" 1614 | dependencies = [ 1615 | "thiserror-impl", 1616 | ] 1617 | 1618 | [[package]] 1619 | name = "thiserror-impl" 1620 | version = "1.0.23" 1621 | source = "registry+https://github.com/rust-lang/crates.io-index" 1622 | checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" 1623 | dependencies = [ 1624 | "proc-macro2", 1625 | "quote", 1626 | "syn", 1627 | ] 1628 | 1629 | [[package]] 1630 | name = "threadpool" 1631 | version = "1.8.1" 1632 | source = "registry+https://github.com/rust-lang/crates.io-index" 1633 | checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" 1634 | dependencies = [ 1635 | "num_cpus", 1636 | ] 1637 | 1638 | [[package]] 1639 | name = "tiff" 1640 | version = "0.6.1" 1641 | source = "registry+https://github.com/rust-lang/crates.io-index" 1642 | checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" 1643 | dependencies = [ 1644 | "jpeg-decoder", 1645 | "miniz_oxide 0.4.3", 1646 | "weezl", 1647 | ] 1648 | 1649 | [[package]] 1650 | name = "time" 1651 | version = "0.1.43" 1652 | source = "registry+https://github.com/rust-lang/crates.io-index" 1653 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 1654 | dependencies = [ 1655 | "libc", 1656 | "winapi 0.3.9", 1657 | ] 1658 | 1659 | [[package]] 1660 | name = "tinyvec" 1661 | version = "1.1.1" 1662 | source = "registry+https://github.com/rust-lang/crates.io-index" 1663 | checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" 1664 | dependencies = [ 1665 | "tinyvec_macros", 1666 | ] 1667 | 1668 | [[package]] 1669 | name = "tinyvec_macros" 1670 | version = "0.1.0" 1671 | source = "registry+https://github.com/rust-lang/crates.io-index" 1672 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1673 | 1674 | [[package]] 1675 | name = "tokio" 1676 | version = "0.2.25" 1677 | source = "registry+https://github.com/rust-lang/crates.io-index" 1678 | checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" 1679 | dependencies = [ 1680 | "bytes 0.5.6", 1681 | "fnv", 1682 | "futures-core", 1683 | "iovec", 1684 | "lazy_static", 1685 | "memchr", 1686 | "mio 0.6.23", 1687 | "num_cpus", 1688 | "pin-project-lite 0.1.11", 1689 | "slab", 1690 | ] 1691 | 1692 | [[package]] 1693 | name = "tokio" 1694 | version = "1.4.0" 1695 | source = "registry+https://github.com/rust-lang/crates.io-index" 1696 | checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722" 1697 | dependencies = [ 1698 | "autocfg", 1699 | "bytes 1.0.1", 1700 | "libc", 1701 | "memchr", 1702 | "mio 0.7.7", 1703 | "num_cpus", 1704 | "pin-project-lite 0.2.4", 1705 | "tokio-macros", 1706 | ] 1707 | 1708 | [[package]] 1709 | name = "tokio-macros" 1710 | version = "1.1.0" 1711 | source = "registry+https://github.com/rust-lang/crates.io-index" 1712 | checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" 1713 | dependencies = [ 1714 | "proc-macro2", 1715 | "quote", 1716 | "syn", 1717 | ] 1718 | 1719 | [[package]] 1720 | name = "tokio-rustls" 1721 | version = "0.14.1" 1722 | source = "registry+https://github.com/rust-lang/crates.io-index" 1723 | checksum = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a" 1724 | dependencies = [ 1725 | "futures-core", 1726 | "rustls 0.18.1", 1727 | "tokio 0.2.25", 1728 | "webpki", 1729 | ] 1730 | 1731 | [[package]] 1732 | name = "tokio-util" 1733 | version = "0.3.1" 1734 | source = "registry+https://github.com/rust-lang/crates.io-index" 1735 | checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" 1736 | dependencies = [ 1737 | "bytes 0.5.6", 1738 | "futures-core", 1739 | "futures-sink", 1740 | "log", 1741 | "pin-project-lite 0.1.11", 1742 | "tokio 0.2.25", 1743 | ] 1744 | 1745 | [[package]] 1746 | name = "toml" 1747 | version = "0.5.8" 1748 | source = "registry+https://github.com/rust-lang/crates.io-index" 1749 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 1750 | dependencies = [ 1751 | "serde", 1752 | ] 1753 | 1754 | [[package]] 1755 | name = "tower-service" 1756 | version = "0.3.1" 1757 | source = "registry+https://github.com/rust-lang/crates.io-index" 1758 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 1759 | 1760 | [[package]] 1761 | name = "tracing" 1762 | version = "0.1.23" 1763 | source = "registry+https://github.com/rust-lang/crates.io-index" 1764 | checksum = "f7d40a22fd029e33300d8d89a5cc8ffce18bb7c587662f54629e94c9de5487f3" 1765 | dependencies = [ 1766 | "cfg-if 1.0.0", 1767 | "log", 1768 | "pin-project-lite 0.2.4", 1769 | "tracing-core", 1770 | ] 1771 | 1772 | [[package]] 1773 | name = "tracing-core" 1774 | version = "0.1.17" 1775 | source = "registry+https://github.com/rust-lang/crates.io-index" 1776 | checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" 1777 | dependencies = [ 1778 | "lazy_static", 1779 | ] 1780 | 1781 | [[package]] 1782 | name = "tracing-futures" 1783 | version = "0.2.5" 1784 | source = "registry+https://github.com/rust-lang/crates.io-index" 1785 | checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" 1786 | dependencies = [ 1787 | "pin-project", 1788 | "tracing", 1789 | ] 1790 | 1791 | [[package]] 1792 | name = "traitobject" 1793 | version = "0.1.0" 1794 | source = "registry+https://github.com/rust-lang/crates.io-index" 1795 | checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" 1796 | 1797 | [[package]] 1798 | name = "trust-dns-proto" 1799 | version = "0.20.0" 1800 | source = "registry+https://github.com/rust-lang/crates.io-index" 1801 | checksum = "98a0381b2864c2978db7f8e17c7b23cca5a3a5f99241076e13002261a8ecbabd" 1802 | dependencies = [ 1803 | "async-trait", 1804 | "cfg-if 1.0.0", 1805 | "data-encoding", 1806 | "enum-as-inner", 1807 | "futures-channel", 1808 | "futures-io", 1809 | "futures-util", 1810 | "idna", 1811 | "ipnet", 1812 | "lazy_static", 1813 | "log", 1814 | "rand 0.8.3", 1815 | "smallvec", 1816 | "thiserror", 1817 | "tokio 1.4.0", 1818 | "url", 1819 | ] 1820 | 1821 | [[package]] 1822 | name = "trust-dns-resolver" 1823 | version = "0.20.0" 1824 | source = "registry+https://github.com/rust-lang/crates.io-index" 1825 | checksum = "3072d18c10bd621cb00507d59cfab5517862285c353160366e37fbf4c74856e4" 1826 | dependencies = [ 1827 | "cfg-if 1.0.0", 1828 | "futures-util", 1829 | "ipconfig", 1830 | "lazy_static", 1831 | "log", 1832 | "lru-cache", 1833 | "parking_lot", 1834 | "resolv-conf", 1835 | "smallvec", 1836 | "thiserror", 1837 | "tokio 1.4.0", 1838 | "trust-dns-proto", 1839 | ] 1840 | 1841 | [[package]] 1842 | name = "try-lock" 1843 | version = "0.2.3" 1844 | source = "registry+https://github.com/rust-lang/crates.io-index" 1845 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1846 | 1847 | [[package]] 1848 | name = "tungstenite" 1849 | version = "0.11.1" 1850 | source = "registry+https://github.com/rust-lang/crates.io-index" 1851 | checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23" 1852 | dependencies = [ 1853 | "base64 0.12.3", 1854 | "byteorder", 1855 | "bytes 0.5.6", 1856 | "http", 1857 | "httparse", 1858 | "input_buffer", 1859 | "log", 1860 | "rand 0.7.3", 1861 | "sha-1", 1862 | "url", 1863 | "utf-8", 1864 | ] 1865 | 1866 | [[package]] 1867 | name = "typemap" 1868 | version = "0.3.3" 1869 | source = "registry+https://github.com/rust-lang/crates.io-index" 1870 | checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" 1871 | dependencies = [ 1872 | "unsafe-any", 1873 | ] 1874 | 1875 | [[package]] 1876 | name = "typenum" 1877 | version = "1.12.0" 1878 | source = "registry+https://github.com/rust-lang/crates.io-index" 1879 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 1880 | 1881 | [[package]] 1882 | name = "unicase" 1883 | version = "2.6.0" 1884 | source = "registry+https://github.com/rust-lang/crates.io-index" 1885 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1886 | dependencies = [ 1887 | "version_check", 1888 | ] 1889 | 1890 | [[package]] 1891 | name = "unicode-bidi" 1892 | version = "0.3.4" 1893 | source = "registry+https://github.com/rust-lang/crates.io-index" 1894 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1895 | dependencies = [ 1896 | "matches", 1897 | ] 1898 | 1899 | [[package]] 1900 | name = "unicode-normalization" 1901 | version = "0.1.17" 1902 | source = "registry+https://github.com/rust-lang/crates.io-index" 1903 | checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" 1904 | dependencies = [ 1905 | "tinyvec", 1906 | ] 1907 | 1908 | [[package]] 1909 | name = "unicode-segmentation" 1910 | version = "1.7.1" 1911 | source = "registry+https://github.com/rust-lang/crates.io-index" 1912 | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" 1913 | 1914 | [[package]] 1915 | name = "unicode-xid" 1916 | version = "0.2.1" 1917 | source = "registry+https://github.com/rust-lang/crates.io-index" 1918 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1919 | 1920 | [[package]] 1921 | name = "unsafe-any" 1922 | version = "0.4.2" 1923 | source = "registry+https://github.com/rust-lang/crates.io-index" 1924 | checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" 1925 | dependencies = [ 1926 | "traitobject", 1927 | ] 1928 | 1929 | [[package]] 1930 | name = "untrusted" 1931 | version = "0.7.1" 1932 | source = "registry+https://github.com/rust-lang/crates.io-index" 1933 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 1934 | 1935 | [[package]] 1936 | name = "url" 1937 | version = "2.2.0" 1938 | source = "registry+https://github.com/rust-lang/crates.io-index" 1939 | checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" 1940 | dependencies = [ 1941 | "form_urlencoded", 1942 | "idna", 1943 | "matches", 1944 | "percent-encoding", 1945 | ] 1946 | 1947 | [[package]] 1948 | name = "utf-8" 1949 | version = "0.7.5" 1950 | source = "registry+https://github.com/rust-lang/crates.io-index" 1951 | checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" 1952 | 1953 | [[package]] 1954 | name = "uwl" 1955 | version = "0.6.0" 1956 | source = "registry+https://github.com/rust-lang/crates.io-index" 1957 | checksum = "f4bf03e0ca70d626ecc4ba6b0763b934b6f2976e8c744088bb3c1d646fbb1ad0" 1958 | 1959 | [[package]] 1960 | name = "version_check" 1961 | version = "0.9.2" 1962 | source = "registry+https://github.com/rust-lang/crates.io-index" 1963 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 1964 | 1965 | [[package]] 1966 | name = "viuer" 1967 | version = "0.4.0" 1968 | source = "registry+https://github.com/rust-lang/crates.io-index" 1969 | checksum = "2d96be939be45eace63eb7bb41099888cc44a47dba5b59e2a7e7d93ea8ebed0b" 1970 | dependencies = [ 1971 | "ansi_colours", 1972 | "base64 0.13.0", 1973 | "console", 1974 | "crossterm", 1975 | "image", 1976 | "lazy_static", 1977 | "tempfile", 1978 | "termcolor", 1979 | ] 1980 | 1981 | [[package]] 1982 | name = "want" 1983 | version = "0.3.0" 1984 | source = "registry+https://github.com/rust-lang/crates.io-index" 1985 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1986 | dependencies = [ 1987 | "log", 1988 | "try-lock", 1989 | ] 1990 | 1991 | [[package]] 1992 | name = "wasi" 1993 | version = "0.9.0+wasi-snapshot-preview1" 1994 | source = "registry+https://github.com/rust-lang/crates.io-index" 1995 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1996 | 1997 | [[package]] 1998 | name = "wasi" 1999 | version = "0.10.2+wasi-snapshot-preview1" 2000 | source = "registry+https://github.com/rust-lang/crates.io-index" 2001 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 2002 | 2003 | [[package]] 2004 | name = "wasm-bindgen" 2005 | version = "0.2.70" 2006 | source = "registry+https://github.com/rust-lang/crates.io-index" 2007 | checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" 2008 | dependencies = [ 2009 | "cfg-if 1.0.0", 2010 | "serde", 2011 | "serde_json", 2012 | "wasm-bindgen-macro", 2013 | ] 2014 | 2015 | [[package]] 2016 | name = "wasm-bindgen-backend" 2017 | version = "0.2.70" 2018 | source = "registry+https://github.com/rust-lang/crates.io-index" 2019 | checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" 2020 | dependencies = [ 2021 | "bumpalo", 2022 | "lazy_static", 2023 | "log", 2024 | "proc-macro2", 2025 | "quote", 2026 | "syn", 2027 | "wasm-bindgen-shared", 2028 | ] 2029 | 2030 | [[package]] 2031 | name = "wasm-bindgen-futures" 2032 | version = "0.4.20" 2033 | source = "registry+https://github.com/rust-lang/crates.io-index" 2034 | checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94" 2035 | dependencies = [ 2036 | "cfg-if 1.0.0", 2037 | "js-sys", 2038 | "wasm-bindgen", 2039 | "web-sys", 2040 | ] 2041 | 2042 | [[package]] 2043 | name = "wasm-bindgen-macro" 2044 | version = "0.2.70" 2045 | source = "registry+https://github.com/rust-lang/crates.io-index" 2046 | checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" 2047 | dependencies = [ 2048 | "quote", 2049 | "wasm-bindgen-macro-support", 2050 | ] 2051 | 2052 | [[package]] 2053 | name = "wasm-bindgen-macro-support" 2054 | version = "0.2.70" 2055 | source = "registry+https://github.com/rust-lang/crates.io-index" 2056 | checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" 2057 | dependencies = [ 2058 | "proc-macro2", 2059 | "quote", 2060 | "syn", 2061 | "wasm-bindgen-backend", 2062 | "wasm-bindgen-shared", 2063 | ] 2064 | 2065 | [[package]] 2066 | name = "wasm-bindgen-shared" 2067 | version = "0.2.70" 2068 | source = "registry+https://github.com/rust-lang/crates.io-index" 2069 | checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" 2070 | 2071 | [[package]] 2072 | name = "web-sys" 2073 | version = "0.3.47" 2074 | source = "registry+https://github.com/rust-lang/crates.io-index" 2075 | checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" 2076 | dependencies = [ 2077 | "js-sys", 2078 | "wasm-bindgen", 2079 | ] 2080 | 2081 | [[package]] 2082 | name = "webpki" 2083 | version = "0.21.4" 2084 | source = "registry+https://github.com/rust-lang/crates.io-index" 2085 | checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" 2086 | dependencies = [ 2087 | "ring", 2088 | "untrusted", 2089 | ] 2090 | 2091 | [[package]] 2092 | name = "webpki-roots" 2093 | version = "0.19.0" 2094 | source = "registry+https://github.com/rust-lang/crates.io-index" 2095 | checksum = "f8eff4b7516a57307f9349c64bf34caa34b940b66fed4b2fb3136cb7386e5739" 2096 | dependencies = [ 2097 | "webpki", 2098 | ] 2099 | 2100 | [[package]] 2101 | name = "webpki-roots" 2102 | version = "0.20.0" 2103 | source = "registry+https://github.com/rust-lang/crates.io-index" 2104 | checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" 2105 | dependencies = [ 2106 | "webpki", 2107 | ] 2108 | 2109 | [[package]] 2110 | name = "weezl" 2111 | version = "0.1.4" 2112 | source = "registry+https://github.com/rust-lang/crates.io-index" 2113 | checksum = "4a32b378380f4e9869b22f0b5177c68a5519f03b3454fde0b291455ddbae266c" 2114 | 2115 | [[package]] 2116 | name = "widestring" 2117 | version = "0.4.3" 2118 | source = "registry+https://github.com/rust-lang/crates.io-index" 2119 | checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" 2120 | 2121 | [[package]] 2122 | name = "winapi" 2123 | version = "0.2.8" 2124 | source = "registry+https://github.com/rust-lang/crates.io-index" 2125 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 2126 | 2127 | [[package]] 2128 | name = "winapi" 2129 | version = "0.3.9" 2130 | source = "registry+https://github.com/rust-lang/crates.io-index" 2131 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2132 | dependencies = [ 2133 | "winapi-i686-pc-windows-gnu", 2134 | "winapi-x86_64-pc-windows-gnu", 2135 | ] 2136 | 2137 | [[package]] 2138 | name = "winapi-build" 2139 | version = "0.1.1" 2140 | source = "registry+https://github.com/rust-lang/crates.io-index" 2141 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 2142 | 2143 | [[package]] 2144 | name = "winapi-i686-pc-windows-gnu" 2145 | version = "0.4.0" 2146 | source = "registry+https://github.com/rust-lang/crates.io-index" 2147 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2148 | 2149 | [[package]] 2150 | name = "winapi-util" 2151 | version = "0.1.5" 2152 | source = "registry+https://github.com/rust-lang/crates.io-index" 2153 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 2154 | dependencies = [ 2155 | "winapi 0.3.9", 2156 | ] 2157 | 2158 | [[package]] 2159 | name = "winapi-x86_64-pc-windows-gnu" 2160 | version = "0.4.0" 2161 | source = "registry+https://github.com/rust-lang/crates.io-index" 2162 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2163 | 2164 | [[package]] 2165 | name = "winreg" 2166 | version = "0.6.2" 2167 | source = "registry+https://github.com/rust-lang/crates.io-index" 2168 | checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" 2169 | dependencies = [ 2170 | "winapi 0.3.9", 2171 | ] 2172 | 2173 | [[package]] 2174 | name = "winreg" 2175 | version = "0.7.0" 2176 | source = "registry+https://github.com/rust-lang/crates.io-index" 2177 | checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" 2178 | dependencies = [ 2179 | "winapi 0.3.9", 2180 | ] 2181 | 2182 | [[package]] 2183 | name = "ws2_32-sys" 2184 | version = "0.2.1" 2185 | source = "registry+https://github.com/rust-lang/crates.io-index" 2186 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 2187 | dependencies = [ 2188 | "winapi 0.2.8", 2189 | "winapi-build", 2190 | ] 2191 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "discord", 4 | "mcping" 5 | ] 6 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Brandon Lucier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mcping 2 | 3 | _See [`mcping/`](mcping) for the library_ 4 | 5 | Discord bot written in Rust that pings a Java Minecraft server with a custom command and displays the status information in chat. 6 | 7 | ![screenshot](screenshot.png) 8 | 9 | ## Config 10 | 11 | Create a config file called `config.toml` in the root directory of the project with the following: 12 | 13 | ``` 14 | token = "" 15 | address = "" 16 | command = "" 17 | ``` 18 | 19 | Where 20 | - `token` is the discord bot token 21 | - `address` is the Minecraft server address 22 | - `command` is the command that will trigger the ping, for example `~ping` or `~minecraft` 23 | 24 | ## Running 25 | 26 | In order to run the project you'll need [Rust](https://www.rust-lang.org/) installed. Once you have it installed and have created the config file you can run the project with `cargo run --release`. 27 | 28 | ## Library 29 | 30 | The Discord bot is built on top of the [`mcping`](mcping) library, which can be integrated into your own application. It supports both Java and Bedrock servers and has an async implementation. 31 | 32 | #### License 33 | 34 | 35 | Licensed under either of Apache License, Version 36 | 2.0 or MIT license at your option. 37 | 38 | 39 |
40 | 41 | 42 | Unless you explicitly state otherwise, any contribution intentionally submitted 43 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 44 | be dual licensed as above, without any additional terms or conditions. 45 | 46 | -------------------------------------------------------------------------------- /discord/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mcping-discord" 3 | version = "0.1.0" 4 | license = "MIT OR Apache-2.0" 5 | authors = ["Scetch "] 6 | edition = "2018" 7 | 8 | [dependencies] 9 | anyhow = "1" 10 | base64 = "0.13" 11 | itertools = "0.10" 12 | mcping = { path = "../mcping" } 13 | serde = { version = "1", features = [ "derive" ] } 14 | serenity = "0.8" 15 | toml = "0.5" 16 | -------------------------------------------------------------------------------- /discord/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context as AnyhowContext; 2 | use itertools::Itertools; 3 | use serde::Deserialize; 4 | use serenity::{ 5 | client::{Client, Context}, 6 | http::AttachmentType, 7 | model::channel::Message, 8 | prelude::EventHandler, 9 | }; 10 | use std::{fs::File, io::prelude::*, time::Duration}; 11 | 12 | fn main() -> Result<(), anyhow::Error> { 13 | let cfg = load_config().with_context(|| "failed to load config")?; 14 | let handler = Handler::new(cfg.address, cfg.command); 15 | let mut client = 16 | Client::new(&cfg.token, handler).with_context(|| "failed to create Discord client")?; 17 | client 18 | .start() 19 | .with_context(|| "failed to start Discord client")?; 20 | 21 | Ok(()) 22 | } 23 | 24 | /// Configuration file with a discord token and server address. 25 | #[derive(Debug, Deserialize)] 26 | struct Config { 27 | token: String, 28 | address: String, 29 | command: String, 30 | } 31 | 32 | /// Loads a config file with a discord token and server address. 33 | fn load_config() -> Result { 34 | let config_file = "config.toml"; 35 | 36 | let mut file = File::open(config_file) 37 | .with_context(|| format!("failed to open file '{}'", config_file))?; 38 | let mut contents = String::new(); 39 | file.read_to_string(&mut contents) 40 | .with_context(|| format!("failed to read file '{}'", config_file))?; 41 | toml::from_str(&contents) 42 | .with_context(|| format!("failed to parse TOML loaded from file '{}'", config_file)) 43 | } 44 | 45 | struct Handler { 46 | addr: String, 47 | command: String, 48 | } 49 | 50 | impl Handler { 51 | fn new(addr: S, command: String) -> Self 52 | where 53 | S: Into, 54 | { 55 | Handler { 56 | addr: addr.into(), 57 | command, 58 | } 59 | } 60 | } 61 | 62 | impl EventHandler for Handler { 63 | fn message(&self, context: Context, msg: Message) { 64 | let cmd = msg 65 | .content 66 | .split_whitespace() 67 | .next() 68 | .filter(|&cmd| cmd == self.command); 69 | 70 | if cmd.is_none() { 71 | return; 72 | } 73 | 74 | let chan = msg.channel_id; 75 | 76 | // Retrieve our response, decode the icon, and build our sample. 77 | let status = mcping::get_status(mcping::Java { 78 | server_address: self.addr.clone(), 79 | timeout: Some(Duration::from_secs(10)), 80 | }); 81 | 82 | let res = status.map(|(ping, r)| { 83 | // The icon is a base64 encoded PNG so we must decode that first. 84 | let icon = r 85 | .favicon 86 | .map(|i| { 87 | base64::decode_config( 88 | i.trim_start_matches("data:image/png;base64,"), 89 | base64::STANDARD, 90 | ) 91 | }) 92 | .transpose() 93 | .unwrap_or(None); 94 | 95 | let sanitize = |s: &str| { 96 | s.chars().fold(String::with_capacity(s.len()), |mut s, c| { 97 | match c { 98 | '*' | '_' | '~' | '>' | '`' => { 99 | s.push('\\'); 100 | s.push(c); 101 | } 102 | _ => s.push(c), 103 | } 104 | s 105 | }) 106 | }; 107 | 108 | // Join the sample player names into a single string. 109 | let sample = r 110 | .players 111 | .sample 112 | .map(|s| s.into_iter().map(|p| sanitize(&p.name)).join(", ")) 113 | .unwrap_or_else(|| "None".to_string()); 114 | 115 | ( 116 | icon, 117 | r.description, 118 | r.players.online, 119 | r.players.max, 120 | sample, 121 | ping, 122 | ) 123 | }); 124 | 125 | // Attempt to send a message to this channel. 126 | let msg = match res { 127 | Ok((icon, desc, online, max, sample, ping)) => chan.send_message(&context.http, |m| { 128 | m.embed(|e| { 129 | e.title(desc.text()) 130 | .fields(vec![ 131 | ("Players", format!("{}/{}", online, max), true), 132 | ("Online", sample, true), 133 | ]) 134 | .footer(|f| f.text(format!("{} | {} ms", &self.addr, ping))); 135 | 136 | if icon.is_some() { 137 | e.thumbnail("attachment://icon.png"); 138 | } 139 | 140 | e 141 | }); 142 | 143 | if let Some(icon) = icon { 144 | m.add_file(AttachmentType::Bytes { 145 | data: icon.into(), 146 | filename: String::from("icon.png"), 147 | }); 148 | } 149 | 150 | m 151 | }), 152 | Err(err) => { 153 | // If there is an error we will send a message with the error content. 154 | chan.send_message(&context.http, |m| { 155 | m.embed(|e| e.title("Error").description(&err.to_string())) 156 | }) 157 | } 158 | }; 159 | 160 | // Check if there was an error sending the message. 161 | if let Err(e) = msg { 162 | println!("Error sending message: {}", e); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /mcping/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mcping" 3 | version = "0.2.0" 4 | license = "MIT OR Apache-2.0" 5 | authors = ["Scetch ", "Cldfire "] 6 | description = "Minecraft ping protocol implementations for both the Java and Bedrock editions" 7 | keywords = ["minecraft", "ping", "async", "bedrock", "status"] 8 | categories = ["api-bindings", "game-development", "network-programming"] 9 | repository = "https://github.com/Scetch/mcping" 10 | edition = "2018" 11 | readme = "README.md" 12 | 13 | [features] 14 | tokio-runtime = ["tokio", "async-trait"] 15 | 16 | [dependencies] 17 | async-trait = { version = "0.1.48", optional = true } 18 | byteorder = "1" 19 | rand = "0.8" 20 | serde = { version = "1", features = ["derive"] } 21 | serde_json = "1" 22 | thiserror = "1" 23 | tokio = { version = "1.4.0", features = ["net", "io-util"], optional = true } 24 | trust-dns-resolver = "0.20" 25 | 26 | [dev-dependencies] 27 | argh = "0.1" 28 | mc-legacy-formatting = "0.3" 29 | viuer = "0.4" 30 | base64 = "0.13" 31 | image = "0.23" 32 | tokio = { version = "1.4.0", features = ["rt", "rt-multi-thread", "macros"] } 33 | -------------------------------------------------------------------------------- /mcping/README.md: -------------------------------------------------------------------------------- 1 | # mcping 2 | 3 | [![crate documentation](https://docs.rs/mcping/badge.svg)](https://docs.rs/mcping) 4 | [![Crates.io version](https://img.shields.io/crates/v/mcping.svg)](https://crates.io/crates/mcping) 5 | [![Crates.io downloads](https://img.shields.io/crates/d/mcping.svg)](https://crates.io/crates/mcping) 6 | ![CI](https://github.com/Scetch/mcping/workflows/CI/badge.svg) 7 | [![dependency status](https://deps.rs/repo/github/Scetch/mcping/status.svg)](https://deps.rs/repo/github/Scetch/mcping) 8 | 9 | `mcping` is a Rust crate that provides Minecraft server ping protocol implementations. It can be used to ping servers and collect information such as the MOTD, max player count, online player sample, server icon, etc. 10 | 11 | The library supports both Java and Bedrock servers, and has comprehensive DNS handling (such as SRV record lookup). An async implemention on top of the tokio runtime is also provided. 12 | 13 | ## Example 14 | 15 | Ping a Java Server with no timeout: 16 | 17 | ```rust 18 | use std::time::Duration; 19 | 20 | let (latency, response) = mcping::get_status(mcping::Java { 21 | server_address: "mc.hypixel.net".into(), 22 | timeout: None, 23 | })?; 24 | ``` 25 | 26 | Ping a Bedrock server with no timeout, trying 3 times: 27 | 28 | ```rust 29 | use std::time::Duration; 30 | 31 | let (latency, response) = mcping::get_status(mcping::Bedrock { 32 | server_address: "play.nethergames.org".into(), 33 | timeout: None, 34 | tries: 3, 35 | ..Default::default() 36 | })?; 37 | ``` 38 | 39 | A more complete example can be found in the `cli` example (`examples/cli.rs`) and can be run with `cargo run --example cli`. Some example invocations: 40 | 41 | ``` 42 | cargo run --example cli -- --edition java mc.hypixel.net 43 | cargo run --example cli -- --edition bedrock play.nethergames.org 44 | ``` 45 | 46 | You can run the async version of the example with: 47 | 48 | ``` 49 | cargo run --example cli --features tokio-runtime -- --edition java mc.hypixel.net 50 | cargo run --example cli --features tokio-runtime -- --edition bedrock play.nethergames.org 51 | ``` 52 | 53 | Make sure your working directory is the `mcping` directory when doing so (you can't toggle features from the workspace root). 54 | 55 | ## License 56 | 57 | 58 | Licensed under either of Apache License, Version 59 | 2.0 or MIT license at your option. 60 | 61 | 62 |
63 | 64 | 65 | Unless you explicitly state otherwise, any contribution intentionally submitted 66 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 67 | be dual licensed as above, without any additional terms or conditions. 68 | 69 | -------------------------------------------------------------------------------- /mcping/examples/cli.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use argh::FromArgs; 4 | use mc_legacy_formatting::SpanExt; 5 | use mcping::{BedrockResponse, JavaResponse}; 6 | 7 | #[derive(FromArgs)] 8 | /// Test out pinging servers, Bedrock or Java edition. 9 | struct Args { 10 | /// the server edition to try and ping 11 | #[argh(option)] 12 | edition: Edition, 13 | 14 | /// the server address to ping 15 | #[argh(positional)] 16 | address: String, 17 | } 18 | 19 | enum Edition { 20 | Java, 21 | Bedrock, 22 | } 23 | 24 | impl std::str::FromStr for Edition { 25 | type Err = String; 26 | 27 | fn from_str(s: &str) -> Result { 28 | Ok(match s.to_lowercase().as_ref() { 29 | "java" => Self::Java, 30 | "bedrock" => Self::Bedrock, 31 | _ => return Err("invalid edition".into()), 32 | }) 33 | } 34 | } 35 | 36 | #[cfg(not(feature = "tokio-runtime"))] 37 | fn main() -> Result<(), mcping::Error> { 38 | let args: Args = argh::from_env(); 39 | 40 | match args.edition { 41 | Edition::Java => { 42 | let (latency, status) = mcping::get_status(mcping::Java { 43 | server_address: args.address, 44 | timeout: Some(Duration::from_secs(5)), 45 | })?; 46 | 47 | print_java(latency, status); 48 | } 49 | Edition::Bedrock => { 50 | let (latency, status) = mcping::get_status(mcping::Bedrock { 51 | server_address: args.address, 52 | timeout: Some(Duration::from_secs(5)), 53 | ..Default::default() 54 | })?; 55 | 56 | print_bedrock(latency, status); 57 | } 58 | } 59 | 60 | Ok(()) 61 | } 62 | 63 | #[cfg(feature = "tokio-runtime")] 64 | #[tokio::main] 65 | async fn main() -> Result<(), mcping::Error> { 66 | let args: Args = argh::from_env(); 67 | 68 | match args.edition { 69 | Edition::Java => { 70 | let (latency, status) = mcping::tokio::get_status(mcping::Java { 71 | server_address: args.address, 72 | timeout: Some(Duration::from_secs(5)), 73 | }) 74 | .await?; 75 | 76 | print_java(latency, status); 77 | } 78 | Edition::Bedrock => { 79 | let (latency, status) = mcping::tokio::get_status(mcping::Bedrock { 80 | server_address: args.address, 81 | timeout: Some(Duration::from_secs(5)), 82 | ..Default::default() 83 | }) 84 | .await?; 85 | 86 | print_bedrock(latency, status); 87 | } 88 | } 89 | 90 | Ok(()) 91 | } 92 | 93 | fn print_java(latency: u64, status: JavaResponse) { 94 | println!(); 95 | print!("version: "); 96 | status 97 | .version 98 | .name 99 | .span_iter() 100 | .map(|s| s.wrap_colored()) 101 | .for_each(|s| print!("{}", s)); 102 | 103 | println!(); 104 | println!(); 105 | 106 | println!("description:"); 107 | status 108 | .description 109 | .text() 110 | .span_iter() 111 | .map(|s| s.wrap_colored()) 112 | .for_each(|s| print!("{}", s)); 113 | 114 | println!(); 115 | println!(); 116 | println!( 117 | "players: {}/{}", 118 | &status.players.online, &status.players.max 119 | ); 120 | 121 | print!("sample: "); 122 | 123 | status 124 | .players 125 | .sample 126 | .filter(|sample| !sample.is_empty()) 127 | .map(|sample| { 128 | println!(); 129 | 130 | for player in sample { 131 | player 132 | .name 133 | .span_iter() 134 | .map(|s| s.wrap_colored()) 135 | .for_each(|s| print!("{}", s)); 136 | println!(); 137 | } 138 | }) 139 | .unwrap_or_else(|| println!("N/A")); 140 | 141 | println!("latency: {}ms", latency); 142 | println!("server icon:\n"); 143 | 144 | // The icon is a base64 encoded PNG so we must decode that first. 145 | if let Some(icon_bytes) = status 146 | .favicon 147 | .map(|i| { 148 | base64::decode_config( 149 | i.trim_start_matches("data:image/png;base64,"), 150 | base64::STANDARD, 151 | ) 152 | }) 153 | .transpose() 154 | .unwrap_or(None) 155 | { 156 | if let Ok(icon_img) = 157 | image::load_from_memory_with_format(&icon_bytes, image::ImageFormat::Png) 158 | { 159 | viuer::print( 160 | &icon_img, 161 | &viuer::Config { 162 | transparent: true, 163 | absolute_offset: false, 164 | width: Some(32), 165 | ..Default::default() 166 | }, 167 | ) 168 | .expect("image printing failed"); 169 | } 170 | } 171 | 172 | println!(); 173 | } 174 | 175 | fn print_bedrock(latency: u64, status: BedrockResponse) { 176 | println!(); 177 | println!("version: {}", &status.version_name); 178 | println!("edition: {}", &status.edition); 179 | println!("game mode: {}", status.game_mode.as_deref().unwrap_or("")); 180 | 181 | // Some fun facts about MOTDs on bedrock: 182 | // 183 | // - so far they seem to exclusively use legacy color codes 184 | // - the random style has a special impl for periods, they turn into animated 185 | // colons that warp up and down rapidly 186 | // - motd_2 is ignored? client displays "motd_1 - v{version}", where the 187 | // appended version text is considered part of motd_1 for color code processing 188 | // - motd_2 seems to mainly be used to return the server software in use (e.g. 189 | // PocketMine-MP) 190 | // - it looks like trailing whitespace might get trimmed from motd_1 (but not 191 | // color codes). Need to confirm 192 | println!(); 193 | print!("description: "); 194 | 195 | let motd = if !status.version_name.is_empty() { 196 | format!("{} - v{}", &status.motd_1, &status.version_name) 197 | } else { 198 | status.motd_1.clone() 199 | }; 200 | 201 | motd.span_iter() 202 | .map(|s| s.wrap_colored()) 203 | .for_each(|s| print!("{}", s)); 204 | 205 | println!(); 206 | println!(); 207 | println!( 208 | "players: {}/{}", 209 | &status.players_online.unwrap_or(0), 210 | &status.players_max.unwrap_or(0) 211 | ); 212 | 213 | println!("latency: {}ms", latency); 214 | 215 | println!(); 216 | } 217 | -------------------------------------------------------------------------------- /mcping/src/bedrock.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the RakNet ping/pong protocol. 2 | //! https://wiki.vg/Raknet_Protocol#Unconnected_Ping 3 | 4 | use crate::{Error, Pingable}; 5 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 6 | use std::{ 7 | io::{self, Cursor, Read}, 8 | net::{Ipv4Addr, SocketAddr, UdpSocket}, 9 | thread, 10 | time::{Duration, Instant}, 11 | }; 12 | use trust_dns_resolver::{config::*, Resolver}; 13 | 14 | /// Raknets default OFFLINE_MESSAGE_DATA_ID. 15 | /// 16 | /// See more: https://wiki.vg/Raknet_Protocol#Data_types 17 | pub(crate) const OFFLINE_MESSAGE_DATA_ID: &[u8] = &[ 18 | 0x00, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78, 19 | ]; 20 | 21 | /// The default port of a Raknet Bedrock Server. 22 | pub(crate) const DEFAULT_PORT: u16 = 19132; 23 | 24 | /// Configuration for pinging a Bedrock server. 25 | /// 26 | /// # Examples 27 | /// 28 | /// ``` 29 | /// use mcping::Bedrock; 30 | /// use std::time::Duration; 31 | /// 32 | /// let bedrock_config = Bedrock { 33 | /// server_address: "play.nethergames.org".to_string(), 34 | /// timeout: Some(Duration::from_secs(10)), 35 | /// ..Default::default() 36 | /// }; 37 | /// ``` 38 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 39 | pub struct Bedrock { 40 | /// The bedrock server address. 41 | /// 42 | /// This can be either an IP or a hostname, and both may optionally have a 43 | /// port at the end. 44 | /// 45 | /// DNS resolution will be performed on hostnames. 46 | /// 47 | /// # Examples 48 | /// 49 | /// ```text 50 | /// test.server.com 51 | /// test.server.com:19384 52 | /// 13.212.76.209 53 | /// 13.212.76.209:23193 54 | /// ``` 55 | pub server_address: String, 56 | /// The read and write timeouts for the socket. 57 | pub timeout: Option, 58 | /// The amount of times to try to send the ping packet. 59 | /// 60 | /// In case of packet loss an attempt can be made to send more than a single ping. 61 | pub tries: usize, 62 | /// The amount of time to wait in-between sending ping packets. 63 | pub wait_to_try: Option, 64 | /// The socket addresses to try binding the UDP socket to. 65 | pub socket_addresses: Vec, 66 | } 67 | 68 | impl Default for Bedrock { 69 | fn default() -> Self { 70 | Self { 71 | server_address: String::new(), 72 | timeout: None, 73 | tries: 5, 74 | wait_to_try: Some(Duration::from_millis(10)), 75 | socket_addresses: vec![ 76 | SocketAddr::from((Ipv4Addr::new(0, 0, 0, 0), 25567)), 77 | SocketAddr::from((Ipv4Addr::new(0, 0, 0, 0), 25568)), 78 | SocketAddr::from((Ipv4Addr::new(0, 0, 0, 0), 25569)), 79 | ], 80 | } 81 | } 82 | } 83 | 84 | impl Pingable for Bedrock { 85 | type Response = BedrockResponse; 86 | 87 | fn ping(self) -> Result<(u64, Self::Response), Error> { 88 | let mut connection = 89 | Connection::new(&self.server_address, &self.socket_addresses, self.timeout)?; 90 | 91 | for _ in 0..self.tries { 92 | connection.send(Packet::UnconnectedPing)?; 93 | 94 | if let Some(wait) = self.wait_to_try { 95 | thread::sleep(wait); 96 | } 97 | } 98 | 99 | let before = Instant::now(); 100 | if let Packet::UnconnectedPong { payload, .. } = connection.read()? { 101 | let latency = (Instant::now() - before).as_millis() as u64; 102 | 103 | // Attempt to extract useful information from the payload. 104 | if let Some(response) = BedrockResponse::extract(&payload) { 105 | Ok((latency, response)) 106 | } else { 107 | Err(Error::IoError(io::Error::new( 108 | io::ErrorKind::Other, 109 | "Invalid Payload", 110 | ))) 111 | } 112 | } else { 113 | Err(Error::IoError(io::Error::new( 114 | io::ErrorKind::Other, 115 | "Invalid Packet Response", 116 | ))) 117 | } 118 | } 119 | } 120 | 121 | /// Represents the edition of a bedrock server. 122 | #[derive(Clone, Debug, Eq, PartialEq, Hash)] 123 | pub enum BedrockEdition { 124 | PocketEdition, 125 | EducationEdition, 126 | /// An unknown edition string. 127 | Other(String), 128 | } 129 | 130 | impl std::fmt::Display for BedrockEdition { 131 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 132 | match self { 133 | BedrockEdition::PocketEdition => f.write_str("MCPE"), 134 | BedrockEdition::EducationEdition => f.write_str("MCEE"), 135 | BedrockEdition::Other(s) => f.write_str(s), 136 | } 137 | } 138 | } 139 | 140 | impl From for BedrockEdition { 141 | fn from(edition: String) -> Self { 142 | match edition.to_lowercase().as_ref() { 143 | "mcpe" => Self::PocketEdition, 144 | "mcee" => Self::EducationEdition, 145 | _ => Self::Other(edition), 146 | } 147 | } 148 | } 149 | 150 | /// Bedrock Server Payload Response 151 | /// 152 | /// See More: https://wiki.vg/Raknet_Protocol#Unconnected_Pong 153 | #[derive(Clone, Debug, Eq, PartialEq, Hash)] 154 | pub struct BedrockResponse { 155 | /// The server's edition. 156 | pub edition: BedrockEdition, 157 | /// The first line of the server's Message Of The Day (MOTD). 158 | /// 159 | /// In practice, this seems to be the only line that the bedrock clients 160 | /// display, and therefore the only line servers usually send. 161 | pub motd_1: String, 162 | /// The server's protocol version (ex: 390). 163 | pub protocol_version: Option, 164 | /// The name of the servers version (ex: 1.16.200). 165 | /// 166 | /// Bedrock clients display this after the first line of the MOTD, in the 167 | /// format `motd_1 - v{version_name}`. This is ommitted if no version name 168 | /// is in the response. 169 | pub version_name: String, 170 | /// The numbers of players online. 171 | pub players_online: Option, 172 | /// The maximum number of players that could be online at once. 173 | pub players_max: Option, 174 | /// The server UUID. 175 | pub server_id: Option, 176 | /// The second line of the server's MOTD. 177 | /// 178 | /// In practice, it looks like servers don't really use this. It seems to get 179 | /// used sometimes to communicate the server software being used (e.g. 180 | /// PocketMine-MP). 181 | pub motd_2: Option, 182 | /// The game mode the server defaults new users to (e.g. "Survival"). 183 | pub game_mode: Option, 184 | /// The numerical representation of `game_mode` (e.g. "1"). 185 | pub game_mode_id: Option, 186 | /// The port to connect to the server on with an IPv4 address. 187 | pub port_v4: Option, 188 | /// The port to connect to the server on with an IPv6 address. 189 | pub port_v6: Option, 190 | } 191 | 192 | impl BedrockResponse { 193 | /// Extracts information from the semicolon-separated payload. 194 | /// 195 | /// Edition (MCPE or MCEE for Education Edition) 196 | /// MOTD line 1 197 | /// Protocol Version 198 | /// Version Name 199 | /// Player Count 200 | /// Max Player Count 201 | /// Server Unique ID 202 | /// MOTD line 2 203 | /// Game mode 204 | /// Game mode (numeric) 205 | /// Port (IPv4) 206 | /// Port (IPv6) 207 | pub(crate) fn extract(payload: &str) -> Option { 208 | let mut parts = payload.split(';').map(|s| s.to_string()); 209 | 210 | Some(BedrockResponse { 211 | edition: parts.next().map(BedrockEdition::from)?, 212 | motd_1: parts.next()?, 213 | protocol_version: parts.next().map(|s| s.parse().ok())?, 214 | version_name: parts.next()?, 215 | players_online: parts.next().and_then(|s| s.parse().ok()), 216 | players_max: parts.next().and_then(|s| s.parse().ok()), 217 | server_id: parts.next().and_then(|s| s.parse().ok()), 218 | motd_2: parts.next(), 219 | game_mode: parts.next(), 220 | game_mode_id: parts.next().and_then(|s| s.parse().ok()), 221 | port_v4: parts.next().and_then(|s| s.parse().ok()), 222 | port_v6: parts.next().and_then(|s| s.parse().ok()), 223 | }) 224 | } 225 | } 226 | 227 | /// Extension to `Read` and `ReadBytesExt` that supplies simple methods to write RakNet types. 228 | trait ReadBedrockExt: Read + ReadBytesExt { 229 | /// Writes a Rust `String` in the form Raknet will respond to. 230 | /// 231 | /// See more: https://wiki.vg/Raknet_Protocol#Data_types 232 | fn read_string(&mut self) -> Result { 233 | let len = self.read_u16::()?; 234 | let mut buf = vec![0; len as usize]; 235 | self.read_exact(&mut buf)?; 236 | String::from_utf8(buf) 237 | .map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid UTF-8 String.")) 238 | } 239 | } 240 | 241 | impl ReadBedrockExt for T {} 242 | 243 | /// Represents a RakNet Unconnected Ping Protocol. 244 | #[derive(Debug)] 245 | pub(crate) enum Packet { 246 | UnconnectedPing, 247 | UnconnectedPong { 248 | time: u64, 249 | server_id: u64, 250 | payload: String, 251 | }, 252 | } 253 | 254 | /// Udp Socket Connection to a Raknet Bedrock Server. 255 | struct Connection { 256 | socket: UdpSocket, 257 | } 258 | 259 | impl Connection { 260 | fn new( 261 | address: &str, 262 | socket_addresses: &[SocketAddr], 263 | timeout: Option, 264 | ) -> Result { 265 | let mut parts = address.split(':'); 266 | 267 | let host = parts.next().ok_or(Error::InvalidAddress)?.to_string(); 268 | 269 | let port = if let Some(port) = parts.next() { 270 | port.parse::().map_err(|_| Error::InvalidAddress)? 271 | } else { 272 | DEFAULT_PORT 273 | }; 274 | 275 | // Do a hostname lookup 276 | let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap(); 277 | 278 | let ip = resolver 279 | .lookup_ip(host.as_str()) 280 | .ok() 281 | .and_then(|ips| ips.iter().next()) 282 | .ok_or(Error::DnsLookupFailed)?; 283 | 284 | let socket = UdpSocket::bind(socket_addresses)?; 285 | socket.connect((ip, port))?; 286 | socket.set_read_timeout(timeout)?; 287 | socket.set_write_timeout(timeout)?; 288 | 289 | Ok(Self { socket }) 290 | } 291 | 292 | fn send(&mut self, packet: Packet) -> Result<(), io::Error> { 293 | match packet { 294 | Packet::UnconnectedPing => { 295 | let mut buf = vec![0x01]; // Packet ID 296 | buf.write_i64::(0x00)?; // Timestamp 297 | buf.extend_from_slice(OFFLINE_MESSAGE_DATA_ID); // MAGIC 298 | buf.write_i64::(0)?; // Client GUID 299 | 300 | self.socket.send(&buf)?; 301 | } 302 | _ => { 303 | return Err(io::Error::new( 304 | io::ErrorKind::Other, 305 | "Invalid C -> S Packet", 306 | )) 307 | } 308 | } 309 | 310 | Ok(()) 311 | } 312 | 313 | fn read(&mut self) -> Result { 314 | let mut buf = vec![0; 1024]; 315 | self.socket.recv(&mut buf)?; 316 | 317 | let mut buf = Cursor::new(&buf); 318 | 319 | match buf.read_u8()? { 320 | 0x1C => { 321 | // time, server guid, MAGIC, server id 322 | let time = buf.read_u64::()?; 323 | let server_id = buf.read_u64::()?; 324 | 325 | let mut tmp = [0; 16]; 326 | buf.read_exact(&mut tmp)?; 327 | 328 | if tmp != OFFLINE_MESSAGE_DATA_ID { 329 | return Err(io::Error::new( 330 | io::ErrorKind::Other, 331 | "incorrect offline message data ID received", 332 | )); 333 | } 334 | 335 | let payload = buf.read_string()?; 336 | 337 | Ok(Packet::UnconnectedPong { 338 | time, 339 | server_id, 340 | payload, 341 | }) 342 | } 343 | _ => Err(io::Error::new( 344 | io::ErrorKind::Other, 345 | "Invalid S -> C Packet", 346 | )), 347 | } 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /mcping/src/java.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the Java Minecraft ping protocol. 2 | //! https://wiki.vg/Server_List_Ping 3 | 4 | use crate::{Error, Pingable}; 5 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 6 | use serde::Deserialize; 7 | use std::{ 8 | io::{self, Cursor, Read, Write}, 9 | net::{IpAddr, SocketAddr, TcpStream}, 10 | time::{Duration, Instant}, 11 | }; 12 | use thiserror::Error; 13 | use trust_dns_resolver::{config::*, Resolver}; 14 | 15 | /// Configuration for pinging a Java server. 16 | /// 17 | /// # Examples 18 | /// 19 | /// ``` 20 | /// use mcping::Java; 21 | /// use std::time::Duration; 22 | /// 23 | /// let bedrock_config = Java { 24 | /// server_address: "mc.hypixel.net".to_string(), 25 | /// timeout: Some(Duration::from_secs(10)), 26 | /// }; 27 | /// ``` 28 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 29 | pub struct Java { 30 | /// The java server address. 31 | /// 32 | /// This can be either an IP or a hostname, and both may optionally have a 33 | /// port at the end. 34 | /// 35 | /// DNS resolution will be performed on hostnames. 36 | /// 37 | /// # Examples 38 | /// 39 | /// ```text 40 | /// test.server.com 41 | /// test.server.com:19384 42 | /// 13.212.76.209 43 | /// 13.212.76.209:23193 44 | /// ``` 45 | pub server_address: String, 46 | /// The connection timeout if a connection cannot be made. 47 | pub timeout: Option, 48 | } 49 | 50 | impl Pingable for Java { 51 | type Response = JavaResponse; 52 | 53 | fn ping(self) -> Result<(u64, Self::Response), crate::Error> { 54 | let mut conn = Connection::new(&self.server_address, self.timeout)?; 55 | 56 | // Handshake 57 | conn.send_packet(Packet::Handshake { 58 | version: 47, 59 | host: conn.host.clone(), 60 | port: conn.port, 61 | next_state: 1, 62 | })?; 63 | 64 | // Request 65 | conn.send_packet(Packet::Request {})?; 66 | 67 | let resp = match conn.read_packet()? { 68 | Packet::Response { response } => serde_json::from_str(&response)?, 69 | _ => return Err(Error::InvalidPacket), 70 | }; 71 | 72 | // Ping Request 73 | let r = rand::random(); 74 | conn.send_packet(Packet::Ping { payload: r })?; 75 | 76 | let before = Instant::now(); 77 | let ping = match conn.read_packet()? { 78 | Packet::Pong { payload } if payload == r => { 79 | (Instant::now() - before).as_millis() as u64 80 | } 81 | _ => return Err(Error::InvalidPacket), 82 | }; 83 | 84 | Ok((ping, resp)) 85 | } 86 | } 87 | 88 | /// The server status reponse 89 | /// 90 | /// More information can be found [here](https://wiki.vg/Server_List_Ping). 91 | #[derive(Deserialize)] 92 | pub struct JavaResponse { 93 | /// The version of the server. 94 | pub version: Version, 95 | /// Information about online players 96 | pub players: Players, 97 | /// The description of the server (MOTD). 98 | pub description: Chat, 99 | /// The server icon (a Base64-encoded PNG image) 100 | pub favicon: Option, 101 | } 102 | 103 | /// Information about the server's version 104 | #[derive(Deserialize)] 105 | pub struct Version { 106 | /// The name of the version the server is running 107 | /// 108 | /// In practice this comes in a large variety of different formats. 109 | pub name: String, 110 | /// See https://wiki.vg/Protocol_version_numbers 111 | pub protocol: i64, 112 | } 113 | 114 | /// An online player of the server. 115 | #[derive(Deserialize)] 116 | pub struct Player { 117 | /// The name of the player. 118 | pub name: String, 119 | /// The player's UUID 120 | pub id: String, 121 | } 122 | 123 | /// The stats for players on the server. 124 | #[derive(Deserialize)] 125 | pub struct Players { 126 | /// The max amount of players. 127 | pub max: i64, 128 | /// The amount of players online. 129 | pub online: i64, 130 | /// A preview of which players are online 131 | /// 132 | /// In practice servers often don't send this or use it for more advertising 133 | pub sample: Option>, 134 | } 135 | 136 | /// This is a partial implemenation of a Minecraft chat component limited to just text 137 | // TODO: Finish this object. 138 | #[derive(Deserialize)] 139 | #[serde(untagged)] 140 | pub enum Chat { 141 | Text { text: String }, 142 | String(String), 143 | } 144 | 145 | impl Chat { 146 | pub fn text(&self) -> &str { 147 | match self { 148 | Chat::Text { text } => text.as_str(), 149 | Chat::String(s) => s.as_str(), 150 | } 151 | } 152 | } 153 | 154 | trait ReadJavaExt: Read + ReadBytesExt { 155 | fn read_varint(&mut self) -> io::Result { 156 | let mut res = 0i32; 157 | for i in 0..5 { 158 | let part = self.read_u8()?; 159 | res |= (part as i32 & 0x7F) << (7 * i); 160 | if part & 0x80 == 0 { 161 | return Ok(res); 162 | } 163 | } 164 | Err(io::Error::new(io::ErrorKind::Other, "VarInt too big!")) 165 | } 166 | 167 | fn read_string(&mut self) -> io::Result { 168 | let len = self.read_varint()? as usize; 169 | let mut buf = vec![0; len as usize]; 170 | self.read_exact(&mut buf)?; 171 | Ok(String::from_utf8(buf).expect("Invalid UTF-8 String.")) 172 | } 173 | } 174 | 175 | impl ReadJavaExt for T where T: Read + ReadBytesExt {} 176 | 177 | trait WriteJavaExt: Write + WriteBytesExt { 178 | fn write_varint(&mut self, mut val: i32) -> io::Result<()> { 179 | for _ in 0..5 { 180 | if val & !0x7F == 0 { 181 | self.write_u8(val as u8)?; 182 | return Ok(()); 183 | } 184 | self.write_u8((val & 0x7F | 0x80) as u8)?; 185 | val >>= 7; 186 | } 187 | Err(io::Error::new(io::ErrorKind::Other, "VarInt too big!")) 188 | } 189 | 190 | fn write_string(&mut self, s: &str) -> io::Result<()> { 191 | self.write_varint(s.len() as i32)?; 192 | self.write_all(s.as_bytes())?; 193 | Ok(()) 194 | } 195 | } 196 | 197 | impl WriteJavaExt for T where T: Write + WriteBytesExt {} 198 | 199 | #[derive(Debug, Error)] 200 | #[error("invalid packet response `{packet:?}`")] 201 | pub struct InvalidPacket { 202 | packet: Packet, 203 | } 204 | 205 | #[derive(Debug)] 206 | pub(crate) enum Packet { 207 | Handshake { 208 | version: i32, 209 | host: String, 210 | port: u16, 211 | next_state: i32, 212 | }, 213 | Response { 214 | response: String, 215 | }, 216 | Pong { 217 | payload: u64, 218 | }, 219 | Request {}, 220 | Ping { 221 | payload: u64, 222 | }, 223 | } 224 | 225 | struct Connection { 226 | stream: TcpStream, 227 | host: String, 228 | port: u16, 229 | } 230 | 231 | impl Connection { 232 | fn new(address: &str, timeout: Option) -> Result { 233 | // Split the address up into it's parts, saving the host and port for later and converting the 234 | // potential domain into an ip 235 | let mut parts = address.split(':'); 236 | 237 | let host = parts.next().ok_or(Error::InvalidAddress)?.to_string(); 238 | 239 | // If a port exists we want to try and parse it and if not we will 240 | // default to 25565 (Minecraft) 241 | let port = if let Some(port) = parts.next() { 242 | port.parse::().map_err(|_| Error::InvalidAddress)? 243 | } else { 244 | 25565 245 | }; 246 | 247 | // Attempt to lookup the ip of the server from an srv record, falling back on the ip from a host 248 | let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap(); 249 | 250 | // Determine what host to lookup by doing the following: 251 | // - Lookup the SRV record for the domain, if it exists perform a lookup of the ip from the target 252 | // and grab the port pointed at by the record. 253 | // 254 | // Note: trust_dns_resolver should do a recursive lookup for an ip but it doesn't seem to at 255 | // the moment. 256 | // 257 | // - If the above failed in any way fall back to the normal ip lookup from the host provided 258 | // and use the provided port. 259 | let lookup_ip = 260 | |host: &str| -> Option { resolver.lookup_ip(host).ok()?.into_iter().next() }; 261 | 262 | let (ip, port) = resolver 263 | .srv_lookup(format!("_minecraft._tcp.{}.", &host)) 264 | .ok() 265 | .and_then(|lookup| { 266 | let record = lookup.into_iter().next()?; 267 | let ip = lookup_ip(&record.target().to_string())?; 268 | Some((ip, record.port())) 269 | }) 270 | .or_else(|| Some((lookup_ip(&host)?, port))) 271 | .ok_or(Error::DnsLookupFailed)?; 272 | 273 | let socket_addr = SocketAddr::new(ip, port); 274 | 275 | Ok(Self { 276 | stream: if let Some(timeout) = timeout { 277 | TcpStream::connect_timeout(&socket_addr, timeout)? 278 | } else { 279 | TcpStream::connect(&socket_addr)? 280 | }, 281 | host, 282 | port, 283 | }) 284 | } 285 | 286 | fn send_packet(&mut self, p: Packet) -> Result<(), Error> { 287 | let mut buf = Vec::new(); 288 | match p { 289 | Packet::Handshake { 290 | version, 291 | host, 292 | port, 293 | next_state, 294 | } => { 295 | buf.write_varint(0x00)?; 296 | buf.write_varint(version)?; 297 | buf.write_string(&host)?; 298 | buf.write_u16::(port)?; 299 | buf.write_varint(next_state)?; 300 | } 301 | Packet::Request {} => { 302 | buf.write_varint(0x00)?; 303 | } 304 | Packet::Ping { payload } => { 305 | buf.write_varint(0x01)?; 306 | buf.write_u64::(payload)?; 307 | } 308 | _ => return Err(Error::InvalidPacket), 309 | } 310 | self.stream.write_varint(buf.len() as i32)?; 311 | self.stream.write_all(&buf)?; 312 | Ok(()) 313 | } 314 | 315 | fn read_packet(&mut self) -> Result { 316 | let len = self.stream.read_varint()?; 317 | let mut buf = vec![0; len as usize]; 318 | self.stream.read_exact(&mut buf)?; 319 | let mut c = Cursor::new(buf); 320 | 321 | Ok(match c.read_varint()? { 322 | 0x00 => Packet::Response { 323 | response: c.read_string()?, 324 | }, 325 | 0x01 => Packet::Pong { 326 | payload: c.read_u64::()?, 327 | }, 328 | _ => return Err(Error::InvalidPacket), 329 | }) 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /mcping/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! `mcping` is a Rust crate that provides Minecraft server ping protocol 2 | //! implementations. It can be used to ping servers and collect information such 3 | //! as the MOTD, max player count, online player sample, server icon, etc. 4 | //! 5 | //! The library supports both Java and Bedrock servers, and has comprehensive DNS 6 | //! handling (such as SRV record lookup). An async implemention on top of the tokio 7 | //! runtime is also provided. 8 | //! 9 | //! The main API surface is [`get_status`]. 10 | 11 | #[cfg(feature = "tokio-runtime")] 12 | pub mod tokio; 13 | 14 | mod bedrock; 15 | mod java; 16 | 17 | pub use bedrock::{Bedrock, BedrockResponse}; 18 | pub use java::{Chat, Java, JavaResponse, Player, Players, Version}; 19 | 20 | /// Errors that can occur when pinging a server. 21 | #[derive(Debug, thiserror::Error)] 22 | pub enum Error { 23 | #[error("an invalid packet configuration was sent")] 24 | InvalidPacket, 25 | #[error("an I/O error occurred: {0}")] 26 | IoError(#[from] std::io::Error), 27 | #[error("a JSON error occurred: {0}")] 28 | JsonErr(#[from] serde_json::Error), 29 | #[error("an invalid address was provided")] 30 | InvalidAddress, 31 | #[error("DNS lookup for the host provided failed")] 32 | DnsLookupFailed, 33 | } 34 | 35 | /// Represents a pingable entity. 36 | pub trait Pingable { 37 | /// The type of response that is expected in reply to the ping. 38 | type Response; 39 | 40 | /// Ping the entity, gathering the latency and response. 41 | fn ping(self) -> Result<(u64, Self::Response), Error>; 42 | } 43 | 44 | /// Retrieve the status of a given Minecraft server using a `Pingable` configuration. 45 | /// 46 | /// 47 | /// Returns `(latency_ms, response)` where response is a response type of the `Pingable` configuration. 48 | /// 49 | /// # Examples 50 | /// 51 | /// Ping a Java Server with no timeout: 52 | /// 53 | /// ```no_run 54 | /// use std::time::Duration; 55 | /// 56 | /// let (latency, response) = mcping::get_status(mcping::Java { 57 | /// server_address: "mc.hypixel.net".into(), 58 | /// timeout: None, 59 | /// })?; 60 | /// # Ok::<(), mcping::Error>(()) 61 | /// ``` 62 | /// 63 | /// Ping a Bedrock server with no timeout, trying 3 times: 64 | /// 65 | /// ```no_run 66 | /// use std::time::Duration; 67 | /// 68 | /// let (latency, response) = mcping::get_status(mcping::Bedrock { 69 | /// server_address: "play.nethergames.org".into(), 70 | /// timeout: None, 71 | /// tries: 3, 72 | /// ..Default::default() 73 | /// })?; 74 | /// # Ok::<(), mcping::Error>(()) 75 | /// ``` 76 | pub fn get_status(pingable: P) -> Result<(u64, P::Response), Error> { 77 | pingable.ping() 78 | } 79 | -------------------------------------------------------------------------------- /mcping/src/tokio/bedrock.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the RakNet ping/pong protocol. 2 | //! https://wiki.vg/Raknet_Protocol#Unconnected_Ping 3 | 4 | use async_trait::async_trait; 5 | use std::{ 6 | io::{self, Cursor}, 7 | net::SocketAddr, 8 | time::{Duration, Instant}, 9 | }; 10 | use tokio::{ 11 | io::{AsyncRead, AsyncReadExt, AsyncWriteExt}, 12 | net::UdpSocket, 13 | }; 14 | use trust_dns_resolver::{config::*, TokioAsyncResolver}; 15 | 16 | use crate::{ 17 | bedrock::{Packet, DEFAULT_PORT, OFFLINE_MESSAGE_DATA_ID}, 18 | tokio::AsyncPingable, 19 | Bedrock, BedrockResponse, Error, 20 | }; 21 | 22 | #[async_trait] 23 | impl AsyncPingable for Bedrock { 24 | type Response = BedrockResponse; 25 | 26 | async fn ping(self) -> Result<(u64, Self::Response), Error> { 27 | let mut connection = 28 | Connection::new(&self.server_address, &self.socket_addresses, self.timeout).await?; 29 | 30 | for _ in 0..self.tries { 31 | connection.send(Packet::UnconnectedPing).await?; 32 | 33 | if let Some(wait) = self.wait_to_try { 34 | tokio::time::sleep(wait).await; 35 | } 36 | } 37 | 38 | let before = Instant::now(); 39 | if let Packet::UnconnectedPong { payload, .. } = connection.read().await? { 40 | let latency = (Instant::now() - before).as_millis() as u64; 41 | 42 | // Attempt to extract useful information from the payload. 43 | if let Some(response) = BedrockResponse::extract(&payload) { 44 | Ok((latency, response)) 45 | } else { 46 | Err(Error::IoError(io::Error::new( 47 | io::ErrorKind::Other, 48 | "Invalid Payload", 49 | ))) 50 | } 51 | } else { 52 | Err(Error::IoError(io::Error::new( 53 | io::ErrorKind::Other, 54 | "Invalid Packet Response", 55 | ))) 56 | } 57 | } 58 | } 59 | 60 | /// Extension to `Read` and `ReadBytesExt` that supplies simple methods to write RakNet types. 61 | #[async_trait] 62 | trait AsyncReadBedrockExt: AsyncRead + AsyncReadExt + Unpin { 63 | /// Writes a Rust `String` in the form Raknet will respond to. 64 | /// 65 | /// See more: https://wiki.vg/Raknet_Protocol#Data_types 66 | async fn read_string(&mut self) -> Result { 67 | let len = self.read_u16().await?; 68 | let mut buf = vec![0; len as usize]; 69 | self.read_exact(&mut buf).await?; 70 | String::from_utf8(buf) 71 | .map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid UTF-8 String.")) 72 | } 73 | } 74 | 75 | impl AsyncReadBedrockExt for T {} 76 | 77 | /// Udp Socket Connection to a Raknet Bedrock Server. 78 | struct Connection { 79 | socket: UdpSocket, 80 | } 81 | 82 | impl Connection { 83 | async fn new( 84 | address: &str, 85 | socket_addresses: &[SocketAddr], 86 | timeout: Option, 87 | ) -> Result { 88 | let mut parts = address.split(':'); 89 | 90 | let host = parts.next().ok_or(Error::InvalidAddress)?.to_string(); 91 | 92 | let port = if let Some(port) = parts.next() { 93 | port.parse::().map_err(|_| Error::InvalidAddress)? 94 | } else { 95 | DEFAULT_PORT 96 | }; 97 | 98 | // Do a hostname lookup 99 | let resolver = 100 | TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()).unwrap(); 101 | 102 | let ip = resolver 103 | .lookup_ip(host.as_str()) 104 | .await 105 | .ok() 106 | .and_then(|ips| ips.iter().next()) 107 | .ok_or(Error::DnsLookupFailed)?; 108 | 109 | let socket = UdpSocket::bind(socket_addresses).await?; 110 | socket.connect((ip, port)).await?; 111 | 112 | let socket = socket.into_std()?; 113 | 114 | socket.set_read_timeout(timeout)?; 115 | socket.set_write_timeout(timeout)?; 116 | 117 | Ok(Self { 118 | socket: UdpSocket::from_std(socket)?, 119 | }) 120 | } 121 | 122 | async fn send(&mut self, packet: Packet) -> Result<(), io::Error> { 123 | match packet { 124 | Packet::UnconnectedPing => { 125 | let mut buf = vec![0x01]; // Packet ID 126 | buf.write_i64(0x00).await?; // Timestamp 127 | buf.extend_from_slice(OFFLINE_MESSAGE_DATA_ID); // MAGIC 128 | buf.write_i64(0).await?; // Client GUID 129 | 130 | self.socket.send(&buf).await?; 131 | } 132 | _ => { 133 | return Err(io::Error::new( 134 | io::ErrorKind::Other, 135 | "Invalid C -> S Packet", 136 | )) 137 | } 138 | } 139 | 140 | Ok(()) 141 | } 142 | 143 | async fn read(&mut self) -> Result { 144 | let mut buf = vec![0; 1024]; 145 | self.socket.recv(&mut buf).await?; 146 | 147 | let mut buf = Cursor::new(&buf); 148 | 149 | match buf.read_u8().await? { 150 | 0x1C => { 151 | // time, server guid, MAGIC, server id 152 | let time = buf.read_u64().await?; 153 | let server_id = buf.read_u64().await?; 154 | 155 | let mut tmp = [0; 16]; 156 | buf.read_exact(&mut tmp).await?; 157 | 158 | if tmp != OFFLINE_MESSAGE_DATA_ID { 159 | return Err(io::Error::new( 160 | io::ErrorKind::Other, 161 | "incorrect offline message data ID received", 162 | )); 163 | } 164 | 165 | let payload = buf.read_string().await?; 166 | 167 | Ok(Packet::UnconnectedPong { 168 | time, 169 | server_id, 170 | payload, 171 | }) 172 | } 173 | _ => Err(io::Error::new( 174 | io::ErrorKind::Other, 175 | "Invalid S -> C Packet", 176 | )), 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /mcping/src/tokio/java.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the Java Minecraft ping protocol. 2 | //! https://wiki.vg/Server_List_Ping 3 | 4 | use async_trait::async_trait; 5 | use std::{ 6 | io::{self, Cursor}, 7 | net::{IpAddr, SocketAddr}, 8 | time::{Duration, Instant}, 9 | }; 10 | use tokio::{ 11 | io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, 12 | net::TcpStream, 13 | }; 14 | use trust_dns_resolver::{config::*, TokioAsyncResolver}; 15 | 16 | use crate::{java::Packet, tokio::AsyncPingable, Error, Java, JavaResponse}; 17 | 18 | #[async_trait] 19 | impl AsyncPingable for Java { 20 | type Response = JavaResponse; 21 | 22 | async fn ping(self) -> Result<(u64, Self::Response), crate::Error> { 23 | let mut conn = Connection::new(&self.server_address, self.timeout).await?; 24 | 25 | // Handshake 26 | conn.send_packet(Packet::Handshake { 27 | version: 47, 28 | host: conn.host.clone(), 29 | port: conn.port, 30 | next_state: 1, 31 | }) 32 | .await?; 33 | 34 | // Request 35 | conn.send_packet(Packet::Request {}).await?; 36 | 37 | let resp = match conn.read_packet().await? { 38 | Packet::Response { response } => serde_json::from_str(&response)?, 39 | _ => return Err(Error::InvalidPacket), 40 | }; 41 | 42 | // Ping Request 43 | let r = rand::random(); 44 | conn.send_packet(Packet::Ping { payload: r }).await?; 45 | 46 | let before = Instant::now(); 47 | let ping = match conn.read_packet().await? { 48 | Packet::Pong { payload } if payload == r => { 49 | (Instant::now() - before).as_millis() as u64 50 | } 51 | _ => return Err(Error::InvalidPacket), 52 | }; 53 | 54 | Ok((ping, resp)) 55 | } 56 | } 57 | 58 | #[async_trait] 59 | trait AsyncReadJavaExt: AsyncRead + AsyncReadExt + Unpin { 60 | async fn read_varint(&mut self) -> io::Result { 61 | let mut res = 0i32; 62 | for i in 0..5u8 { 63 | let part = self.read_u8().await?; 64 | res |= (part as i32 & 0x7F) << (7 * i); 65 | if part & 0x80 == 0 { 66 | return Ok(res); 67 | } 68 | } 69 | Err(io::Error::new(io::ErrorKind::Other, "VarInt too big!")) 70 | } 71 | 72 | async fn read_string(&mut self) -> io::Result { 73 | let len = self.read_varint().await? as usize; 74 | let mut buf = vec![0; len as usize]; 75 | self.read_exact(&mut buf).await?; 76 | Ok(String::from_utf8(buf).expect("Invalid UTF-8 String.")) 77 | } 78 | } 79 | 80 | impl AsyncReadJavaExt for T where T: AsyncRead + AsyncReadExt + Unpin {} 81 | 82 | #[async_trait] 83 | trait AsyncWriteJavaExt: AsyncWrite + AsyncWriteExt + Unpin { 84 | async fn write_varint(&mut self, mut val: i32) -> io::Result<()> { 85 | for _ in 0..5 { 86 | if val & !0x7F == 0 { 87 | self.write_u8(val as u8).await?; 88 | return Ok(()); 89 | } 90 | self.write_u8((val & 0x7F | 0x80) as u8).await?; 91 | val >>= 7; 92 | } 93 | Err(io::Error::new(io::ErrorKind::Other, "VarInt too big!")) 94 | } 95 | 96 | async fn write_string(&mut self, s: &str) -> io::Result<()> { 97 | self.write_varint(s.len() as i32).await?; 98 | self.write_all(s.as_bytes()).await?; 99 | Ok(()) 100 | } 101 | } 102 | 103 | impl AsyncWriteJavaExt for T where T: AsyncWrite + AsyncWriteExt + Unpin {} 104 | 105 | struct Connection { 106 | stream: TcpStream, 107 | host: String, 108 | port: u16, 109 | } 110 | 111 | impl Connection { 112 | async fn new(address: &str, timeout: Option) -> Result { 113 | // Split the address up into it's parts, saving the host and port for later and converting the 114 | // potential domain into an ip 115 | let mut parts = address.split(':'); 116 | 117 | let host = parts.next().ok_or(Error::InvalidAddress)?.to_string(); 118 | 119 | // If a port exists we want to try and parse it and if not we will 120 | // default to 25565 (Minecraft) 121 | let port = if let Some(port) = parts.next() { 122 | port.parse::().map_err(|_| Error::InvalidAddress)? 123 | } else { 124 | 25565 125 | }; 126 | 127 | // Attempt to lookup the ip of the server from an srv record, falling back on the ip from a host 128 | let resolver = 129 | TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()).unwrap(); 130 | 131 | // Determine what host to lookup by doing the following: 132 | // - Lookup the SRV record for the domain, if it exists perform a lookup of the ip from the target 133 | // and grab the port pointed at by the record. 134 | // 135 | // Note: trust_dns_resolver should do a recursive lookup for an ip but it doesn't seem to at 136 | // the moment. 137 | // 138 | // - If the above failed in any way fall back to the normal ip lookup from the host provided 139 | // and use the provided port. 140 | 141 | let srv_lookup = resolver 142 | .srv_lookup(format!("_minecraft._tcp.{}.", &host)) 143 | .await 144 | .ok(); 145 | let ip: IpAddr = match srv_lookup { 146 | Some(lookup) => match lookup.into_iter().next() { 147 | Some(record) => resolver 148 | .lookup_ip(record.target().to_string()) 149 | .await 150 | .ok() 151 | .and_then(|lookup_ip| lookup_ip.into_iter().next()), 152 | None => None, 153 | }, 154 | None => resolver 155 | .lookup_ip(host.clone()) 156 | .await 157 | .ok() 158 | .and_then(|lookup_ip| lookup_ip.into_iter().next()), 159 | } 160 | .ok_or(Error::DnsLookupFailed)?; 161 | 162 | let socket_addr = SocketAddr::new(ip, port); 163 | 164 | let stream = TcpStream::connect(&socket_addr).await?.into_std()?; 165 | 166 | stream.set_read_timeout(timeout)?; 167 | stream.set_write_timeout(timeout)?; 168 | 169 | Ok(Self { 170 | stream: TcpStream::from_std(stream)?, 171 | host, 172 | port, 173 | }) 174 | } 175 | 176 | async fn send_packet(&mut self, p: Packet) -> Result<(), Error> { 177 | let mut buf = Vec::new(); 178 | match p { 179 | Packet::Handshake { 180 | version, 181 | host, 182 | port, 183 | next_state, 184 | } => { 185 | buf.write_varint(0x00).await?; 186 | buf.write_varint(version).await?; 187 | buf.write_string(&host).await?; 188 | buf.write_u16(port).await?; 189 | buf.write_varint(next_state).await?; 190 | } 191 | Packet::Request {} => { 192 | buf.write_varint(0x00).await?; 193 | } 194 | Packet::Ping { payload } => { 195 | buf.write_varint(0x01).await?; 196 | buf.write_u64(payload).await?; 197 | } 198 | _ => return Err(Error::InvalidPacket), 199 | } 200 | self.stream.write_varint(buf.len() as i32).await?; 201 | self.stream.write_all(&buf).await?; 202 | Ok(()) 203 | } 204 | 205 | async fn read_packet(&mut self) -> Result { 206 | let len = self.stream.read_varint().await?; 207 | let mut buf = vec![0; len as usize]; 208 | self.stream.read_exact(&mut buf).await?; 209 | let mut c = Cursor::new(buf); 210 | 211 | Ok(match c.read_varint().await? { 212 | 0x00 => Packet::Response { 213 | response: c.read_string().await?, 214 | }, 215 | 0x01 => Packet::Pong { 216 | payload: c.read_u64().await?, 217 | }, 218 | _ => return Err(Error::InvalidPacket), 219 | }) 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /mcping/src/tokio/mod.rs: -------------------------------------------------------------------------------- 1 | mod bedrock; 2 | mod java; 3 | 4 | use async_trait::async_trait; 5 | 6 | use crate::Error; 7 | 8 | /// Represents a pingable entity. 9 | #[async_trait] 10 | pub trait AsyncPingable { 11 | /// The type of response that is expected in reply to the ping. 12 | type Response; 13 | 14 | /// Ping the entity, gathering the latency and response. 15 | async fn ping(self) -> Result<(u64, Self::Response), Error>; 16 | } 17 | 18 | /// Retrieve the status of a given Minecraft server using a `AsyncPingable` configuration. 19 | /// 20 | /// 21 | /// Returns `(latency_ms, response)` where response is a response type of the `Pingable` configuration. 22 | /// 23 | /// # Examples 24 | /// 25 | /// Ping a Java Server with no timeout: 26 | /// 27 | /// ```no_run 28 | /// # async { 29 | /// use std::time::Duration; 30 | /// 31 | /// let (latency, response) = mcping::tokio::get_status(mcping::Java { 32 | /// server_address: "mc.hypixel.net".into(), 33 | /// timeout: None, 34 | /// }).await?; 35 | /// # Ok::<(), mcping::Error>(()) 36 | /// # }; 37 | /// ``` 38 | /// 39 | /// Ping a Bedrock server with no timeout, trying 3 times: 40 | /// 41 | /// ```no_run 42 | /// # async { 43 | /// use std::time::Duration; 44 | /// 45 | /// let (latency, response) = mcping::tokio::get_status(mcping::Bedrock { 46 | /// server_address: "play.nethergames.org".into(), 47 | /// timeout: None, 48 | /// tries: 3, 49 | /// ..Default::default() 50 | /// }).await?; 51 | /// # Ok::<(), mcping::Error>(()) 52 | /// # }; 53 | /// ``` 54 | pub async fn get_status(pingable: P) -> Result<(u64, P::Response), Error> { 55 | pingable.ping().await 56 | } 57 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scetch/mcping/7e74d2220a61557b5514768edb7da42d2758e143/screenshot.png --------------------------------------------------------------------------------