├── .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 | 
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 | [](https://docs.rs/mcping)
4 | [](https://crates.io/crates/mcping)
5 | [](https://crates.io/crates/mcping)
6 | 
7 | [](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
--------------------------------------------------------------------------------