From: Geoffrey Allott Date: Wed, 3 Feb 2021 21:41:32 +0000 (+0000) Subject: initial commit X-Git-Url: https://git.pointlesshacks.com/?a=commitdiff_plain;h=9ab085b73575fa7ac0477e00b7a9db1578738341;p=pokerwave.git initial commit --- 9ab085b73575fa7ac0477e00b7a9db1578738341 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a6cc0e8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1979 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher", +] + +[[package]] +name = "aes-gcm" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher", + "opaque-debug", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher", + "opaque-debug", +] + +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "async-channel" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59740d83946db6a5af71ae25ddf9562c2b176b2ca42cf99a455f09f4a220d6b9" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-dup" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7427a12b8dc09291528cfb1da2447059adb4a257388c2acd6497a79d55cf6f7c" +dependencies = [ + "futures-io", + "simple-mutex", +] + +[[package]] +name = "async-executor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb877970c7b440ead138f6321a3b5395d6061183af779340b65e20c0fede9146" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "vec-arena", +] + +[[package]] +name = "async-global-executor" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-mutex", + "blocking", + "futures-lite", + "num_cpus", + "once_cell", +] + +[[package]] +name = "async-h1" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c68a75f812ff0f299e142c06dd0c34e3295a594d935e61eeb6c77041d1d4dc" +dependencies = [ + "async-channel", + "async-dup", + "async-std", + "byte-pool", + "futures-core", + "http-types", + "httparse", + "lazy_static", + "log", + "pin-project", +] + +[[package]] +name = "async-io" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9315f8f07556761c3e48fec2e6b276004acf426e6dc068b2c2251854d65ee0fd" +dependencies = [ + "concurrent-queue", + "fastrand", + "futures-lite", + "libc", + "log", + "nb-connect", + "once_cell", + "parking", + "polling", + "vec-arena", + "waker-fn", + "winapi", +] + +[[package]] +name = "async-lock" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1996609732bde4a9988bc42125f55f2af5f3c36370e27c778d5191a4a1b63bfb" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-process" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8cea09c1fb10a317d1b5af8024eeba256d6554763e85ecd90ff8df31c7bbda" +dependencies = [ + "async-io", + "blocking", + "cfg-if 0.1.10", + "event-listener", + "futures-lite", + "once_cell", + "signal-hook 0.1.17", + "winapi", +] + +[[package]] +name = "async-sse" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53bba003996b8fd22245cd0c59b869ba764188ed435392cf2796d03b805ade10" +dependencies = [ + "async-channel", + "async-std", + "http-types", + "log", + "memchr", + "pin-project-lite 0.1.11", +] + +[[package]] +name = "async-std" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341" +dependencies = [ + "async-attributes", + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils 0.8.1", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "num_cpus", + "once_cell", + "pin-project-lite 0.2.4", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" + +[[package]] +name = "async-tls" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f23d769dbf1838d5df5156e7b1ad404f4c463d1ac2c6aeb6cd943630f8a8400" +dependencies = [ + "futures-core", + "futures-io", + "rustls", + "webpki", + "webpki-roots", +] + +[[package]] +name = "async-trait" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-tungstenite" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39eca8dd578b18e557361e50ca767df55c5e62f690a5e53868c3c7a8123145b7" +dependencies = [ + "futures-io", + "futures-util", + "log", + "pin-project", + "tungstenite", +] + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + +[[package]] +name = "bumpalo" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" + +[[package]] +name = "byte-pool" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38e98299d518ec351ca016363e0cbfc77059dcd08dfa9700d15e405536097a" +dependencies = [ + "crossbeam-queue", + "stable_deref_trait", +] + +[[package]] +name = "byteorder" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cache-padded" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" + +[[package]] +name = "cc" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + +[[package]] +name = "combine" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4369b5e4c0cddf64ad8981c0111e7df4f7078f4d6ba98fb31f2e17c4c57b7e" +dependencies = [ + "bytes 1.0.1", + "futures-util", + "memchr", + "pin-project-lite 0.2.4", + "tokio", +] + +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "const_fn" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" + +[[package]] +name = "cookie" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" +dependencies = [ + "aes-gcm", + "base64 0.12.3", + "hkdf", + "hmac", + "percent-encoding", + "rand 0.7.3", + "sha2", + "time", + "version_check", +] + +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + +[[package]] +name = "cpuid-bool" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" + +[[package]] +name = "crossbeam-queue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +dependencies = [ + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "lazy_static", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-mac" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctor" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10bcb9d7dcbf7002aaffbb53eac22906b64cdcc127971dcc387d8eb7c95d5560" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ctr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +dependencies = [ + "cipher", +] + +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + +[[package]] +name = "dtoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" + +[[package]] +name = "env_logger" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "event-listener" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" + +[[package]] +name = "fastrand" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" + +[[package]] +name = "futures-executor" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" + +[[package]] +name = "futures-lite" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite 0.2.4", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" + +[[package]] +name = "futures-task" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite 0.2.4", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gloo-timers" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "hkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac", + "digest", +] + +[[package]] +name = "http" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" +dependencies = [ + "bytes 1.0.1", + "fnv", + "itoa", +] + +[[package]] +name = "http-client" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010092b71b94ee49293995625ce7a607778b8b4099c8088fa84fd66bd3e0f21c" +dependencies = [ + "async-trait", + "http-types", + "log", +] + +[[package]] +name = "http-types" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32613ebb139d1d430ef5783676f84abfa06fc5f2b4b5a25220cdeeff7e16ef5c" +dependencies = [ + "anyhow", + "async-channel", + "async-std", + "base64 0.13.0", + "cookie", + "futures-lite", + "infer", + "pin-project-lite 0.2.4", + "rand 0.7.3", + "serde", + "serde_json", + "serde_qs", + "serde_urlencoded", + "url", +] + +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "infer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" + +[[package]] +name = "input_buffer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" +dependencies = [ + "bytes 0.5.6", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "js-sys" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", + "value-bag", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "nb-connect" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8123a81538e457d44b933a02faf885d3fe8408806b23fa700e8f01c6c3a98998" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b70b68509f17aa2857863b6fa00bf21fc93674c7a8893de2f469f6aa7ca2f2" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa25a6393f22ce819b0f50e0be89287292fda8d425be38ee0ca14c4931d9e71" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" + +[[package]] +name = "pin-project-lite" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pokerwave" +version = "0.1.0" +dependencies = [ + "async-std", + "env_logger", + "futures", + "log", + "rand 0.8.3", + "redis", + "serde", + "serde_derive", + "serde_json", + "signal-hook 0.3.4", + "signal-hook-async-std", + "tide", + "tide-rustls", + "tide-websockets", +] + +[[package]] +name = "polling" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "log", + "wepoll-sys", + "winapi", +] + +[[package]] +name = "polyval" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +dependencies = [ + "cpuid-bool 0.2.0", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.1", + "rand_hc 0.3.0", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.1", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +dependencies = [ + "getrandom 0.2.2", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.1", +] + +[[package]] +name = "redis" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a6ddfecac9391fed21cce10e83c65fa4abafd77df05c98b1c647c65374ce9b3" +dependencies = [ + "async-std", + "async-trait", + "bytes 1.0.1", + "combine", + "dtoa", + "futures-util", + "itoa", + "percent-encoding", + "pin-project-lite 0.2.4", + "sha1", + "tokio", + "tokio-util", + "url", +] + +[[package]] +name = "regex" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" + +[[package]] +name = "ring" +version = "0.16.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "route-recognizer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56770675ebc04927ded3e60633437841581c285dc6236109ea25fbf3beb7b59e" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" +dependencies = [ + "base64 0.13.0", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_qs" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5af82de3c6549b001bec34961ff2d6a54339a87bab37ce901b693401f27de6cb" +dependencies = [ + "data-encoding", + "percent-encoding", + "serde", + "thiserror", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c" +dependencies = [ + "block-buffer", + "cfg-if 1.0.0", + "cpuid-bool 0.1.2", + "digest", + "opaque-debug", +] + +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + +[[package]] +name = "sha2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" +dependencies = [ + "block-buffer", + "cfg-if 1.0.0", + "cpuid-bool 0.1.2", + "digest", + "opaque-debug", +] + +[[package]] +name = "signal-hook" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780f5e3fe0c66f67197236097d89de1e86216f1f6fdeaf47c442f854ab46c240" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-async-std" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90526e74631c69a79b38212e3d4fda4b00de9d6be56b3cead133bf67ad371af1" +dependencies = [ + "async-io", + "futures-lite", + "libc", + "signal-hook 0.3.4", +] + +[[package]] +name = "signal-hook-registry" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +dependencies = [ + "libc", +] + +[[package]] +name = "simple-mutex" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38aabbeafa6f6dead8cebf246fe9fae1f9215c8d29b3a69f93bd62a9e4a3dcd6" +dependencies = [ + "event-listener", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "standback" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66a8cff4fa24853fdf6b51f75c6d7f8206d7c75cab4e467bcd7f25c2b1febe0" +dependencies = [ + "version_check", +] + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + +[[package]] +name = "subtle" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" + +[[package]] +name = "sval" +version = "1.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45f6ee7c7b87caf59549e9fe45d6a69c75c8019e79e212a835c5da0e92f0ba08" + +[[package]] +name = "syn" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8208a331e1cb318dd5bd76951d2b8fc48ca38a69f5f4e4af1b6a9f8c6236915" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tide" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c459573f0dd2cc734b539047f57489ea875af8ee950860ded20cf93a79a1dee0" +dependencies = [ + "async-h1", + "async-sse", + "async-std", + "async-trait", + "futures-util", + "http-client", + "http-types", + "kv-log-macro", + "log", + "pin-project-lite 0.2.4", + "route-recognizer", + "serde", + "serde_json", +] + +[[package]] +name = "tide-rustls" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf8521961215f0ebbb27e10c9d610ff2a64f853dc3707c03748350c6dbe022" +dependencies = [ + "async-dup", + "async-h1", + "async-std", + "async-tls", + "rustls", + "tide", +] + +[[package]] +name = "tide-websockets" +version = "0.1.0" +source = "git+https://github.com/http-rs/tide-websockets?branch=main#c7f5fc6fd81bb592d6f55f09bddc62cc7a60bdeb" +dependencies = [ + "async-dup", + "async-std", + "async-tungstenite", + "base64 0.13.0", + "futures-util", + "pin-project", + "serde", + "serde_json", + "sha-1", + "tide", +] + +[[package]] +name = "time" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1195b046942c221454c2539395f85413b33383a067449d78aab2b7b052a142f7" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb", + "time-macros", + "version_check", + "winapi", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6714d663090b6b0acb0fa85841c6d66233d150cdb2602c8f9b8abb03370beb3f" +dependencies = [ + "autocfg", + "bytes 1.0.1", + "memchr", + "pin-project-lite 0.2.4", +] + +[[package]] +name = "tokio-util" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebb7cb2f00c5ae8df755b252306272cd1790d39728363936e01827e11f0b017b" +dependencies = [ + "bytes 1.0.1", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.2.4", + "tokio", +] + +[[package]] +name = "tungstenite" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23" +dependencies = [ + "base64 0.12.3", + "byteorder", + "bytes 0.5.6", + "http", + "httparse", + "input_buffer", + "log", + "rand 0.7.3", + "sha-1", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "universal-hash" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" + +[[package]] +name = "value-bag" +version = "1.0.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b676010e055c99033117c2343b33a40a30b91fecd6c49055ac9cd2d6c305ab1" +dependencies = [ + "ctor", + "sval", +] + +[[package]] +name = "vec-arena" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" + +[[package]] +name = "web-sys" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" +dependencies = [ + "webpki", +] + +[[package]] +name = "wepoll-sys" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff" +dependencies = [ + "cc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..521a4f8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "pokerwave" +version = "0.1.0" +authors = ["Geoffrey Allott "] +edition = "2018" + +[dependencies] +async-std = { version = "1", features = ["attributes"] } +env_logger = "0.8" +log = "0.4" +futures = "0.3" +rand = "0.8" +redis = { version = "0.19", features = ["async-std-comp"] } +serde = "1" +serde_derive = "1" +serde_json = "1" +signal-hook = "0.3" +signal-hook-async-std = "0.2" +tide = { version = "0.16.0", default-features = false, features = ["h1-server"] } +tide-rustls = "0.2" +tide-websockets = { git = "https://github.com/http-rs/tide-websockets", branch = "main" } diff --git a/cert/cert.pem b/cert/cert.pem new file mode 100644 index 0000000..78c58ae --- /dev/null +++ b/cert/cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEKTCCApGgAwIBAgIRANSpSYVi2aOvByhI0RgGwRAwDQYJKoZIhvcNAQELBQAw +VzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMRYwFAYDVQQLDA1nZW9m +ZkBHZW9mZlBDMR0wGwYDVQQDDBRta2NlcnQgZ2VvZmZAR2VvZmZQQzAeFw0yMTAx +MzEyMzIxMDhaFw0yMzA1MDEyMjIxMDhaMEExJzAlBgNVBAoTHm1rY2VydCBkZXZl +bG9wbWVudCBjZXJ0aWZpY2F0ZTEWMBQGA1UECwwNZ2VvZmZAR2VvZmZQQzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzdzX/aPttLL+WGa5E1T3d6jmGQ +TLElkRUZtGbYbR0PmVj2uBbIPhwe9n7R4QcY6MroGZiem5/tO+/bAvz4x6dDHFww +//wCzsBZaO+KgqC5839JRyRsHyoLVZfcv/IbJDh52Nubs7ursiDDCo2Vtv+Y0lV4 +iMKxzy882g13Jkfhv7Of+kWlABA/LS+CEjKbbCKI9W7zIl5RvdNXN0dPNws6p5gN +G96ZIhHWQ0Ah+RYw5bcOzu5AkSlFpUfKBaSyXi0wlCjdglFe4hwCEHU0SwM8qzgX +WXcp6U+QfDmWg1vb9Vfd7tUUzk6LjhIqR72slMCtmm2AhXkk0nTTnViFLUUCAwEA +AaOBhTCBgjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYD +VR0TAQH/BAIwADAfBgNVHSMEGDAWgBQle3YBraPadyDsV2cbZUG6rxEYEDAsBgNV +HREEJTAjgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZI +hvcNAQELBQADggGBAJ+mFs0I2/TDv+6Ni19++I2D3qUGcb3nM7RXUOHMrzcVcQHq +eE+MoNLtyVjZZFqBzZox+RlCCWbHAfqFYblpL27crLXcttDyXIdeQGjuGwE4NGe9 +xi1rlvt0A9IPfrli6SKdH24vijorryXF+GOToBXM7dS8bg1fra5g7uiQgoFhToQp +TH6eEO8y9X3qgCtY1EcWkGIchxfM0qPhwP+zqe7ZTU8XdCQZqBL53DTVj5HmvOd5 +rGB/zJ1C4X6QJ+WivM6228bSm6vyxSj4zx2RM1Fs4LfPBEzd14vyxXJrbIjDk6Be +9V8lY3taYhw0GCslGLtUua8rBrvec+XpMrViJ361pMNvG7BhIseg4BET0DA9Er2/ +gZ67hI9gAfHHs8M2SeZUPJdMHMU3FMtpfA3fPBiHKn1gx0/CRx1Yt47VBobVHth9 +con+wkUOne/kXPDLzWiUyOAdjREN84dM/nwmTcKjPJbzoAUzAF/Zs8ZWUH60Tn7T +eaU0yb10XMxl40b05w== +-----END CERTIFICATE----- diff --git a/cert/key.pem b/cert/key.pem new file mode 100644 index 0000000..ea649d7 --- /dev/null +++ b/cert/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDM3c1/2j7bSy/l +hmuRNU93eo5hkEyxJZEVGbRm2G0dD5lY9rgWyD4cHvZ+0eEHGOjK6BmYnpuf7Tvv +2wL8+MenQxxcMP/8As7AWWjvioKgufN/SUckbB8qC1WX3L/yGyQ4edjbm7O7q7Ig +wwqNlbb/mNJVeIjCsc8vPNoNdyZH4b+zn/pFpQAQPy0vghIym2wiiPVu8yJeUb3T +VzdHTzcLOqeYDRvemSIR1kNAIfkWMOW3Ds7uQJEpRaVHygWksl4tMJQo3YJRXuIc +AhB1NEsDPKs4F1l3KelPkHw5loNb2/VX3e7VFM5Oi44SKke9rJTArZptgIV5JNJ0 +051YhS1FAgMBAAECggEANlXluTFDVppce73pLbw79W518P56uMlD75q/tD9WwUxv +2Z6mOLSndIHp0r0Lt/wtUhO5Jjd7AzHCod0ur7W0DnLIRNNiMY71EyMjp9TOzJ5F +LM9cpRZgNaCH9R1Bc48rTidTEvv68Z2aQwI3/FFnFeeMccrkSu8qCldTZhrh9lI5 +6of9yedK5Vv6tcbEvW4MRSWS9E9QuqWogDKklAYJggX3sEsDHt6cEeqkUxGIayHm +kUrYUEFAj+pLktGALSLO7ByrfPMtnZVLMfu366y5vYIUbhf79sWdMGsGI+/g/2bm +0NGTBpnWnLwpFQKbhYe09CKsmXqmsdhch6/yK+N9CQKBgQDWF7yGPjJqBepn79jq +Q0IcvoRCt6JO0729jTm57Gu1NZSiM71dmylOy6JMxmRMcYnmEfk+xmm/3ANifo70 +iX1zw8gyrDKYHp0t0kr5BfaMph1W2OYgP1YzV97KlN0+FiZmaLjq9FQUsS+Tr/aR +5E0h3HQXCIuM/go3+j6OrINxMwKBgQD097uf1NOhjEbs3snYIuoPZStfPXuUruu7 +nVinpMi9IMxCRecf/9b+tranidNrmFXLIxrIV0x7NwHRt+ykFMVWHi8JTVKKCyAR +rGDnyLfgNHngozqeedBpjxBqWKstTk/Qn+chggKonDqE3gngFi5PORAO5QMbWWQX +C+jzC3pXpwKBgH3U76pbLhhgp7g1IWH27Amm9GeeNNht3SxmsBIV4exctKJtFJTK +2ImGaDsFs/e2F93QnJUH2ym374lZZz5U/RYocSdPTGFaRPmkiwo4NgPOspnT29YC +Q/DjaX/Z5PtT23f/fwghWLXcumDsYFi5PZh0UQskq5jPy0PBYjlLLeo/AoGBAN+z +BqRLTsM8fVw7iqkUIRfT9Epxs9Ov9NcMNfRJV4LWW5C9kvU2xVcu9ReDlgywPNBX +C1Md3Vq7fa7MvY2M5jPhNmTRNmmXBT7+YjPnqHpWkWGgzZwAc2Ch44Sp2g9ybxJ9 +oyyHM/RTTmEc/nmi58Eyw8ZGPZMRC4S8PIsN1TgLAoGAdc8i9zGBp+PwjTIeLZry +byP9GQV6dsBcCFswuGICqO98SdWPVzdTQj7j/9Jnv48FKVEli/KR27POrbIQ+EZY +20IRywZLe+QB+dAs0SapLRJjq7jABoGgF12ol6R04as2ZgIBBz/cdndhFghjEO4d +Unvj9zDM6L67LknR+PyaD2c= +-----END PRIVATE KEY----- diff --git a/site/404.html b/site/404.html new file mode 100644 index 0000000..8268b39 --- /dev/null +++ b/site/404.html @@ -0,0 +1,9 @@ + + + + + + +

404 Not Found

+ + diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..da7e8f7 --- /dev/null +++ b/site/index.html @@ -0,0 +1,10 @@ + + + + + + + +

Pokerwave

+ + diff --git a/site/main.js b/site/main.js new file mode 100644 index 0000000..a99a9ff --- /dev/null +++ b/site/main.js @@ -0,0 +1,35 @@ +"use strict"; + +var socket; + +function socket_open() { + let proto = window.location.protocol === "https:" ? "wss:" : "ws:"; + let uri = proto + "//" + window.location.host + "/api"; + socket = new WebSocket(uri); + socket.onopen = socket_onopen; + socket.onmessage = socket_onmessage; +} + +function socket_onopen() { + console.log("WebSocket connected"); + socket_send({type: "Login", username: "geoff"}); +} + +function socket_send(object) { + console.log(">>", object); + socket.send(JSON.stringify(object)); +} + +function socket_onmessage(msg) { + var message = JSON.parse(msg.data); + console.log("<<", message); + switch (message.type) { + case "LoginAuthChallenge": + socket_send({type: "LoginAuthResponse", signature: "hunter2"}); + break; + } +} + +window.onload = function() { + socket_open(); +}; diff --git a/src/api.rs b/src/api.rs new file mode 100644 index 0000000..17cb91d --- /dev/null +++ b/src/api.rs @@ -0,0 +1,57 @@ +use crate::auth::Auth; +use crate::game::{Action, Game, GameSettings, GameSummary, UserAction}; + +#[derive(Debug, Clone, Deserialize)] +enum Scope { + Global, + Game { id: u32 }, + Hand { id: u32 }, + Player { username: String }, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(tag = "type")] +pub enum ClientMessage { + CreateUser { username: String, auth: Auth, nickname: String }, + Login { username: String }, + LoginAuthResponse { signature: String }, + ChangeAuth { auth: Auth }, + ChangeNickname { nickname: String }, + Logout, + CreateGame { settings: GameSettings }, + GetGameList { tags: Vec }, + JoinGame { id: u32 }, + TakeAction { action: Action }, + SendMessage { scope: Scope, message: String }, + LeaveGame, + GetHandHistory { scope: Scope }, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "type")] +pub enum ServerMessage { + CreateUserSuccess, + CreateUserFailure { reason: String }, + LoginAuthChallenge { challenge: String }, + LoginSuccess, + LoginFailure { reason: String }, + ChangeAuthSuccess, + ChangeAuthFailure { reason: String }, + ChangeNicknameSuccess, + ChangeNicknameFailure { reason: String }, + LogoutSuccess, + CreateGameSuccess { id: u32 }, + CreateGameFailure { reason: String }, + GameList { games: Vec }, + GameListFailure { reason: String }, + JoinGameSuccess { game: Game }, + JoinGameFailure { reason: String }, + NewAction { action: UserAction }, + TakeActionSuccess, + TakeActionFailure { reason: String }, + NewMessage { username: String, message: String }, + LeaveGameSuccess, + LeaveGameFailure { reason: String }, + HandHistory { games: Vec }, + ProtocolError { reason: String }, +} diff --git a/src/auth.rs b/src/auth.rs new file mode 100644 index 0000000..c25f8d2 --- /dev/null +++ b/src/auth.rs @@ -0,0 +1,15 @@ +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "method")] +pub enum Auth { + NoLogin, + Plain { password: String }, +} + +impl Auth { + pub fn verify(&self, challenge: &str, signature: &str) -> bool { + match self { + Auth::NoLogin => false, + Auth::Plain{password} => signature == password, + } + } +} diff --git a/src/card.rs b/src/card.rs new file mode 100644 index 0000000..81fe743 --- /dev/null +++ b/src/card.rs @@ -0,0 +1,185 @@ +#![allow(non_upper_case_globals)] + +use std::fmt::{self, Display, Formatter}; + +use self::Rank::*; +use self::Suit::*; + +#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Serialize, Deserialize)] +pub enum Rank { + Two = 2, + Three = 3, + Four = 4, + Five = 5, + Six = 6, + Seven = 7, + Eight = 8, + Nine = 9, + Ten = 10, + Jack = 11, + Queen = 12, + King = 13, + Ace = 14, +} + +impl Display for Rank { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + Two => "2", + Three => "3", + Four => "4", + Five => "5", + Six => "6", + Seven => "7", + Eight => "8", + Nine => "9", + Ten => "T", + Jack => "J", + Queen => "Q", + King => "K", + Ace => "A", + }) + } +} + +#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Serialize, Deserialize)] +pub enum Suit { + Clubs, + Diamonds, + Hearts, + Spades, +} + +impl Display for Suit { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + Clubs => "♣", + Diamonds => "♢", + Hearts => "♡", + Spades => "♠", + }) + } +} + +#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Serialize, Deserialize)] +pub struct Card { + pub rank: Rank, + pub suit: Suit, +} + +impl Display for Card { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}{}", self.rank, self.suit) + } +} + +pub const TwoOfClubs: Card = Card{rank: Two, suit: Clubs}; +pub const ThreeOfClubs: Card = Card{rank: Three, suit: Clubs}; +pub const FourOfClubs: Card = Card{rank: Four, suit: Clubs}; +pub const FiveOfClubs: Card = Card{rank: Five, suit: Clubs}; +pub const SixOfClubs: Card = Card{rank: Six, suit: Clubs}; +pub const SevenOfClubs: Card = Card{rank: Seven, suit: Clubs}; +pub const EightOfClubs: Card = Card{rank: Eight, suit: Clubs}; +pub const NineOfClubs: Card = Card{rank: Nine, suit: Clubs}; +pub const TenOfClubs: Card = Card{rank: Ten, suit: Clubs}; +pub const JackOfClubs: Card = Card{rank: Jack, suit: Clubs}; +pub const QueenOfClubs: Card = Card{rank: Queen, suit: Clubs}; +pub const KingOfClubs: Card = Card{rank: King, suit: Clubs}; +pub const AceOfClubs: Card = Card{rank: Ace, suit: Clubs}; + +pub const TwoOfDiamonds: Card = Card{rank: Two, suit: Diamonds}; +pub const ThreeOfDiamonds: Card = Card{rank: Three, suit: Diamonds}; +pub const FourOfDiamonds: Card = Card{rank: Four, suit: Diamonds}; +pub const FiveOfDiamonds: Card = Card{rank: Five, suit: Diamonds}; +pub const SixOfDiamonds: Card = Card{rank: Six, suit: Diamonds}; +pub const SevenOfDiamonds: Card = Card{rank: Seven, suit: Diamonds}; +pub const EightOfDiamonds: Card = Card{rank: Eight, suit: Diamonds}; +pub const NineOfDiamonds: Card = Card{rank: Nine, suit: Diamonds}; +pub const TenOfDiamonds: Card = Card{rank: Ten, suit: Diamonds}; +pub const JackOfDiamonds: Card = Card{rank: Jack, suit: Diamonds}; +pub const QueenOfDiamonds: Card = Card{rank: Queen, suit: Diamonds}; +pub const KingOfDiamonds: Card = Card{rank: King, suit: Diamonds}; +pub const AceOfDiamonds: Card = Card{rank: Ace, suit: Diamonds}; + +pub const TwoOfHearts: Card = Card{rank: Two, suit: Hearts}; +pub const ThreeOfHearts: Card = Card{rank: Three, suit: Hearts}; +pub const FourOfHearts: Card = Card{rank: Four, suit: Hearts}; +pub const FiveOfHearts: Card = Card{rank: Five, suit: Hearts}; +pub const SixOfHearts: Card = Card{rank: Six, suit: Hearts}; +pub const SevenOfHearts: Card = Card{rank: Seven, suit: Hearts}; +pub const EightOfHearts: Card = Card{rank: Eight, suit: Hearts}; +pub const NineOfHearts: Card = Card{rank: Nine, suit: Hearts}; +pub const TenOfHearts: Card = Card{rank: Ten, suit: Hearts}; +pub const JackOfHearts: Card = Card{rank: Jack, suit: Hearts}; +pub const QueenOfHearts: Card = Card{rank: Queen, suit: Hearts}; +pub const KingOfHearts: Card = Card{rank: King, suit: Hearts}; +pub const AceOfHearts: Card = Card{rank: Ace, suit: Hearts}; + +pub const TwoOfSpades: Card = Card{rank: Two, suit: Spades}; +pub const ThreeOfSpades: Card = Card{rank: Three, suit: Spades}; +pub const FourOfSpades: Card = Card{rank: Four, suit: Spades}; +pub const FiveOfSpades: Card = Card{rank: Five, suit: Spades}; +pub const SixOfSpades: Card = Card{rank: Six, suit: Spades}; +pub const SevenOfSpades: Card = Card{rank: Seven, suit: Spades}; +pub const EightOfSpades: Card = Card{rank: Eight, suit: Spades}; +pub const NineOfSpades: Card = Card{rank: Nine, suit: Spades}; +pub const TenOfSpades: Card = Card{rank: Ten, suit: Spades}; +pub const JackOfSpades: Card = Card{rank: Jack, suit: Spades}; +pub const QueenOfSpades: Card = Card{rank: Queen, suit: Spades}; +pub const KingOfSpades: Card = Card{rank: King, suit: Spades}; +pub const AceOfSpades: Card = Card{rank: Ace, suit: Spades}; + +pub const FIFTY_TWO_CARD_DECK: [Card; 52] = [ + AceOfSpades, + AceOfHearts, + AceOfDiamonds, + AceOfClubs, + KingOfSpades, + KingOfHearts, + KingOfDiamonds, + KingOfClubs, + QueenOfSpades, + QueenOfHearts, + QueenOfDiamonds, + QueenOfClubs, + JackOfSpades, + JackOfHearts, + JackOfDiamonds, + JackOfClubs, + TenOfSpades, + TenOfHearts, + TenOfDiamonds, + TenOfClubs, + NineOfSpades, + NineOfHearts, + NineOfDiamonds, + NineOfClubs, + EightOfSpades, + EightOfHearts, + EightOfDiamonds, + EightOfClubs, + SevenOfSpades, + SevenOfHearts, + SevenOfDiamonds, + SevenOfClubs, + SixOfSpades, + SixOfHearts, + SixOfDiamonds, + SixOfClubs, + FiveOfSpades, + FiveOfHearts, + FiveOfDiamonds, + FiveOfClubs, + FourOfSpades, + FourOfHearts, + FourOfDiamonds, + FourOfClubs, + ThreeOfSpades, + ThreeOfHearts, + ThreeOfDiamonds, + ThreeOfClubs, + TwoOfSpades, + TwoOfHearts, + TwoOfDiamonds, + TwoOfClubs, +]; diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..f2006a1 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,151 @@ +use std::sync::{Arc, RwLock}; + +use futures::stream::{Stream, StreamExt, empty, iter, once}; + +use crate::api::{ClientMessage, ServerMessage}; +use crate::game::{Game, UserAction}; +use crate::server::{ActionStatus, ServerState}; + +pub struct ConnectionState { + server: ServerState, + client: ClientState, +} + +#[derive(Debug, Clone)] +pub enum ClientState { + Connected, + LoginAuthIssued { username: String, challenge: String }, + LoggedIn { + username: String, + state: LoggedInState, + }, +} + +pub enum ClientUpdate { + GameList, + Game, + Users, +} + +#[derive(Debug, Clone)] +pub enum LoggedInState { + InLobby, + InGame { game: Game }, +} + +impl ConnectionState { + pub fn new(server: ServerState) -> Self { + Self { + server, + client: ClientState::Connected, + } + } + + pub async fn retrieve_updates(&mut self, update: ClientUpdate) -> impl Stream { + match update { + ClientUpdate::GameList => empty().boxed(), // TODO + ClientUpdate::Game => match &mut self.client { + ClientState::LoggedIn{ref username, state: LoggedInState::InGame{ref mut game}} => { + let from = game.actions_len(); + match self.server.update_game_state(game).await { + Ok(()) => iter(game.update_view_for(username, from)).map(|action| ServerMessage::NewAction{action}).boxed(), + Err(err) => once(async move { ServerMessage::ProtocolError{reason: err.to_string()} }).boxed(), + } + } + _ => empty().boxed(), + } + ClientUpdate::Users => empty().boxed(), // TODO + } + } + + pub async fn apply_message(&mut self, message: ClientMessage) -> ServerMessage { + match (&mut self.client, message) { + (_, ClientMessage::CreateUser{username, auth, nickname}) => { + match self.server.create_user(&username, auth, &nickname).await { + Ok(()) => ServerMessage::CreateUserSuccess, + Err(_) => ServerMessage::CreateUserFailure{reason: "User already exists".to_string()}, + } + } + (ClientState::Connected, ClientMessage::Login{username}) => { + let challenge = format!("{:032x}{:032x}", rand::random::(), rand::random::()); + self.client = ClientState::LoginAuthIssued{username, challenge: challenge.clone()}; + ServerMessage::LoginAuthChallenge{challenge} + } + (ClientState::LoginAuthIssued{username, challenge}, ClientMessage::LoginAuthResponse{signature}) => { + if self.server.verify(&username, &challenge, &signature).await { + self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::InLobby}; + ServerMessage::LoginSuccess + } else { + self.client = ClientState::Connected; + ServerMessage::LoginFailure{reason: "Invalid username or password".to_string()} + } + } + (ClientState::LoggedIn{username, ..}, ClientMessage::GetGameList{tags}) => { + match self.server.get_game_list().await { + Ok(games) => ServerMessage::GameList{games}, + Err(err) => ServerMessage::GameListFailure{reason: err.to_string()}, + } + } + (ClientState::LoggedIn{username, ..}, ClientMessage::CreateGame{settings}) => { + match self.server.create_game(settings).await { + Ok(id) => ServerMessage::CreateGameSuccess{id}, + Err(err) => ServerMessage::CreateGameFailure{reason: err.to_string()}, + } + } + (ClientState::LoggedIn{username, state: LoggedInState::InLobby}, ClientMessage::JoinGame{id}) => { + match self.server.get_game(id).await { + Ok(game) => { + let game_view = game.view_for(&username); + self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::InGame{game}}; + ServerMessage::JoinGameSuccess{game: game_view} + } + Err(err) => ServerMessage::JoinGameFailure{reason: err.to_string()}, + } + } + (ClientState::LoggedIn{ref username, state: LoggedInState::InGame{ref mut game}}, ClientMessage::TakeAction{action}) => { + let action = UserAction{username: username.clone(), action}; + loop { + let len = game.actions_len(); + if let Err(err) = game.verify(&action) { + return ServerMessage::TakeActionFailure{reason: err.to_string()}; + } + match self.server.take_action(game.id(), len, &action).await { + Ok(ActionStatus::Committed) => match game.take_action(action) { + Ok(()) => return ServerMessage::TakeActionSuccess, + Err(err) => return ServerMessage::TakeActionFailure{reason: err.to_string()}, + } + Ok(ActionStatus::Interrupted) => { + if let Err(err) = self.server.update_game_state(game).await { + return ServerMessage::TakeActionFailure{reason: err.to_string()}; + } + } + Err(err) => return ServerMessage::TakeActionFailure{reason: err.to_string()}, + } + } + } + (ClientState::LoggedIn{username, state: LoggedInState::InGame{..}}, ClientMessage::LeaveGame) => { + self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::InLobby}; + ServerMessage::LeaveGameSuccess + } + (ClientState::LoggedIn{username, ..}, ClientMessage::Logout) => { + self.client = ClientState::Connected; + ServerMessage::LogoutSuccess + } + (ClientState::LoggedIn{username, ..}, ClientMessage::ChangeAuth{auth}) => { + match self.server.set_user_auth(&username, auth).await { + Ok(()) => ServerMessage::ChangeAuthSuccess, + Err(err) => ServerMessage::ChangeAuthFailure{reason: err.to_string()}, + } + } + (ClientState::LoggedIn{username, ..}, ClientMessage::ChangeNickname{nickname}) => { + match self.server.set_user_nickname(&username, &nickname).await { + Ok(()) => ServerMessage::ChangeNicknameSuccess, + Err(err) => ServerMessage::ChangeNicknameFailure{reason: err.to_string()}, + } + } + (_, _) => { + ServerMessage::ProtocolError{reason: "Protocol error".to_string() } + } + } + } +} diff --git a/src/game.rs b/src/game.rs new file mode 100644 index 0000000..ce72da5 --- /dev/null +++ b/src/game.rs @@ -0,0 +1,151 @@ +use std::fmt::Debug; + +use crate::card::Card; +use crate::gamestate::GameState; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "format")] +pub enum GameSettings { + Chatroom {max_users: u32}, + TexasHoldEm { + // TODO + }, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "action")] +pub enum Action { + Join { seat: u32, chips: u64 }, + AddOn { chips: u64 }, + NextToAct, + CommunityCard { card: Card }, + ReceiveCard { card: Option }, + RevealCard { card: Card }, + Fold, + Bet { chips: u64 }, + WinPot { chips: u64 }, + Message { message: String }, + Leave, +} + +impl Action { + pub fn anonymise(&self) -> Self { + match self { + Action::ReceiveCard{..} => Action::ReceiveCard{card: None}, + action => action.clone(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UserAction { + pub username: String, + pub action: Action, +} + +impl UserAction { + pub fn view_for(&self, username: &str) -> Self { + Self { + username: self.username.clone(), + action: if username == self.username { self.action.clone() } else { self.action.anonymise() }, + } + } +} + +#[derive(Debug, Clone, Serialize)] +pub enum ActionError { + NotAuthorised, + AlreadyJoined, + NoSeatAvailable, + OutOfTurn, + InvalidActionForGameType, +} + +use std::fmt::{Display, Formatter}; + +impl Display for ActionError { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + match self { + ActionError::NotAuthorised => f.write_str("NotAuthorised"), + ActionError::AlreadyJoined => f.write_str("AlreadyJoined"), + ActionError::NoSeatAvailable => f.write_str("NoSeatAvailable"), + ActionError::OutOfTurn => f.write_str("OutOfTurn"), + ActionError::InvalidActionForGameType => f.write_str("InvalidActionForGameType"), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GameSummary { + id: u32, + settings: GameSettings, +} + +impl GameSummary { + pub fn new(id: u32, settings: GameSettings) -> Self { + Self{id, settings} + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Game { + summary: GameSummary, + state: GameState, +} + +impl Game { + pub fn new(id: u32, settings: GameSettings) -> Self { + Self { + summary: GameSummary{id, settings}, + state: GameState::new(), + } + } + + pub fn id(&self) -> u32 { + self.summary.id + } + + pub fn actions_len(&self) -> usize { + self.state.actions_len() + } + + pub fn view_for(&self, username: &str) -> Game { + Game { + summary: self.summary.clone(), + state: self.state.view_for(username), + } + } + + pub fn update_view_for(&self, username: &str, from: usize) -> Vec { + self.state.update_view_for(username, from) + } + + pub fn verify(&self, UserAction{ref username, ref action}: &UserAction) -> Result<(), ActionError> { + debug!("Verifying action: UserAction {{ username: {:?}, action: {:?} }}", username, action); + match self.summary.settings { + GameSettings::Chatroom{max_users} => match action { + Action::Join{seat: 0, chips: 0} if self.state.user_has_joined(username) => Err(ActionError::AlreadyJoined), + Action::Join{seat: 0, chips: 0} if self.state.num_players() + 1 > max_users => Err(ActionError::NoSeatAvailable), + Action::Join{seat: 0, chips: 0} => Ok(()), + Action::Message{ref message} if self.state.user_has_joined(username) => Ok(()), + Action::Message{..} => Err(ActionError::NotAuthorised), + Action::Leave if self.state.user_has_joined(username) => Ok(()), + Action::Leave => Err(ActionError::NotAuthorised), + _ => Err(ActionError::InvalidActionForGameType), + }, + GameSettings::TexasHoldEm{..} => { + // TODO + Err(ActionError::NotAuthorised) + } + } + } + + pub fn take_action(&mut self, user_action: UserAction) -> Result<(), ActionError> { + self.verify(&user_action)?; + debug!("Taking action: {:?}", user_action); + debug!("State before: {:?}", self.state); + self.state.take_action(user_action); + debug!("State after: {:?}", self.state); + Ok(()) + } +} diff --git a/src/gamestate.rs b/src/gamestate.rs new file mode 100644 index 0000000..c1d1350 --- /dev/null +++ b/src/gamestate.rs @@ -0,0 +1,79 @@ +use crate::game::{Action, UserAction}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GameState { + actions: Vec, +} + +impl GameState { + pub fn new() -> Self { + Self { + actions: Vec::new(), + } + } + + pub fn actions_len(&self) -> usize { + self.actions.len() + } + + pub fn view_for(&self, username: &str) -> Self { + Self { + actions: self.actions.iter().map(|action| action.view_for(username)).collect(), + } + } + + pub fn update_view_for(&self, username: &str, from: usize) -> Vec { + self.actions.iter().skip(from).map(|action| action.view_for(username)).collect() + } + + pub fn user_has_joined(&self, user: &str) -> bool { + let mut logged_in = false; + for action in &self.actions { + match action { + UserAction{ref username, action: Action::Join{..}} if user == username => logged_in = true, + UserAction{ref username, action: Action::Leave} if user == username => logged_in = false, + _ => continue, + } + } + logged_in + } + + pub fn num_players(&self) -> u32 { + let mut num_players = 0; + for action in &self.actions { + match action.action { + Action::Join{..} => num_players += 1, + Action::Leave => num_players -= 1, + _ => continue, + } + } + num_players + } + + pub fn take_action(&mut self, action: UserAction) { + self.actions.push(action); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn user_has_joined() { + let mut state = GameState::new(); + state.take_action(UserAction{username: "user".to_string(), action: Action::Join{seat: 0, chips: 10000}}); + assert!(state.user_has_joined("user")); + } + + #[test] + fn num_players() { + let mut state = GameState::new(); + state.take_action(UserAction{username: "user".to_string(), action: Action::Join{seat: 0, chips: 10000}}); + assert_eq!(1, state.num_players()); + state.take_action(UserAction{username: "user2".to_string(), action: Action::Join{seat: 1, chips: 10000}}); + assert_eq!(2, state.num_players()); + state.take_action(UserAction{username: "user".to_string(), action: Action::Leave}); + assert_eq!(1, state.num_players()); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..25386be --- /dev/null +++ b/src/main.rs @@ -0,0 +1,105 @@ +#[macro_use] extern crate log; +#[macro_use] extern crate serde_derive; + +use std::pin::Pin; + +use async_std::prelude::*; +use futures::{future::select, future::Either, pin_mut, Stream, StreamExt, stream}; +use signal_hook::consts::signal::*; +use signal_hook_async_std::Signals; +use tide::{Body, Request, Error, Response, StatusCode}; +use tide::utils::After; +use tide_rustls::TlsListener; +use tide_websockets::{Message, WebSocket, WebSocketConnection}; + +mod api; +mod auth; +mod card; +mod client; +mod game; +mod gamestate; +mod server; + +use crate::api::ServerMessage; +use crate::server::ServerState; +use crate::client::{ClientUpdate, ConnectionState}; + +pub async fn handle_websocket_request(request: Request, mut stream: WebSocketConnection) -> Result<(), Error> { + let mut client = ConnectionState::new(request.state().clone()); + let update_stream: Pin + Send>>; + update_stream = stream::empty().boxed(); // TODO + let mut combined = stream::select(stream.clone().map(Either::Left), update_stream.map(Either::Right)); + while let Some(message) = combined.next().await { + let message: Either, ClientUpdate> = message; + match message { + Either::Left(Ok(Message::Text(input))) => { + let response = match serde_json::from_str(&input) { + Ok(message) => client.apply_message(message).await, + Err(err) => ServerMessage::ProtocolError{reason: err.to_string()}, + }; + stream.send_json(&response).await?; + } + Either::Right(update) => { + let mut responses = client.retrieve_updates(update).await; + while let Some(response) = responses.next().await { + stream.send_json(&response).await? + } + } + Either::Left(Ok(_)) => { + error!("Websocket received non-text"); + break; + } + Either::Left(Err(err)) => { + error!("Websocket error: {}", err); + break; + } + } + } + Ok(()) +} + +async fn handle_signals(mut signals: Signals) -> Result<(), std::io::Error> { + signals.next().await; + info!("Shutting down..."); + Ok(()) +} + +async fn serve_404(response: Response) -> Result { + match response.status() { + StatusCode::NotFound => + Ok(Response::builder(404) + .body(Body::from_file("site/404.html").await?) + .content_type("text/html") + .build()), + _ => Ok(response) + } +} + +#[async_std::main] +async fn main() -> Result<(), Error> { + env_logger::init(); + + let mut app = tide::with_state(ServerState::new("redis://localhost/").await?); + + app.at("/").serve_dir("site/")?; + app.at("/").serve_file("site/index.html")?; + app.at("/api").get(WebSocket::new(handle_websocket_request)); + app.with(After(serve_404)); + + let signals = Signals::new(&[SIGINT])?; + let signal_handler = handle_signals(signals); + + let listener = TlsListener::build() + .addrs("localhost:4433") + .cert("cert/cert.pem") + .key("cert/key.pem"); + + let app = app.listen(listener); + pin_mut!(signal_handler, app); + + select(app, signal_handler).await.factor_first().0?; + + info!("Pokerwave shut down gracefully."); + + Ok(()) +} diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..54d5f03 --- /dev/null +++ b/src/server.rs @@ -0,0 +1,170 @@ +use futures::Stream; +use redis::{AsyncCommands, Client, ErrorKind, FromRedisValue, IntoConnectionInfo, Msg, RedisError, RedisResult, RedisWrite, ToRedisArgs, Value, aio::MultiplexedConnection}; +use serde::{Serialize, Deserialize}; + +use crate::auth::Auth; +use crate::game::{Game, GameSettings, GameSummary, UserAction}; + +#[derive(Clone)] +pub struct ServerState { + client: Client, + redis: MultiplexedConnection, +} + +fn user_key(username: &str) -> String { + format!("user:{}", username) +} + +fn game_key(id: u32) -> String { + format!("game:{}", id) +} + +const TAKE_ACTION_LUA_SCRIPT: &'static str = r#" + local len = redis.call('llen', KEYS[1]) + local expected = tonumber(ARGV[1]) + if (len == expected) then + return redis.call('rpush', KEYS[1], ARGV[2]) + elseif (len > expected) then + return 0 + else + local error = string.format('Inconsistent game state - database reports %d actions but expected at least %d', len, expected) + return redis.error_reply(error) + end +"#; + +impl ServerState { + pub async fn new(params: T) -> RedisResult { + let client = Client::open(params)?; + let redis = client.get_multiplexed_async_std_connection().await?; + Ok(Self{client, redis}) + } + + pub async fn create_user(&mut self, username: &str, auth: Auth, nickname: &str) -> RedisResult<()> { + let key = user_key(username); + if self.redis.hset_nx::<_, _, _, i32>(&key, "auth", AsJson(auth)).await? == 0 { + return Err(RedisError::from((ErrorKind::ResponseError, "User already exists"))); + } + self.redis.hset(&key, "nickname", username).await + } + + pub async fn set_user_auth(&mut self, username: &str, auth: Auth) -> RedisResult<()> { + let key = user_key(username); + self.redis.hset(key, "auth", AsJson(auth)).await + } + + async fn get_user_auth(&mut self, username: &str) -> Option { + let key = user_key(username); + self.redis.hget(key, "auth").await.ok().map(AsJson::get) + } + + pub async fn set_user_nickname(&mut self, username: &str, nickname: &str) -> RedisResult<()> { + let key = user_key(username); + self.redis.hset(key, "nickname", nickname).await + } + + pub async fn verify(&mut self, username: &str, challenge: &str, signature: &str) -> bool { + match self.get_user_auth(username).await { + None => false, + Some(auth) => auth.verify(challenge, signature), + } + } + + pub async fn create_game(&mut self, settings: GameSettings) -> RedisResult { + self.redis.rpush("games", AsJson(settings)).await.map(|i: u32| i - 1) + } + + pub async fn get_game_list(&mut self) -> RedisResult> { + const GAME_LIST_BLOCK_SIZE: isize = 1024; + let mut ret = Vec::new(); + for i in (0..).step_by(GAME_LIST_BLOCK_SIZE as usize) { + let games: Vec> = self.redis.lrange("games", i, i + GAME_LIST_BLOCK_SIZE).await?; + if games.is_empty() { break; } + for (j, AsJson(settings)) in games.into_iter().enumerate() { + ret.push(GameSummary::new(i as u32 + j as u32, settings)); + } + } + Ok(ret) + } + + pub async fn update_game_state(&mut self, game: &mut Game) -> RedisResult<()> { + const GAME_ACTION_BLOCK_SIZE: isize = 1024; + let key = game_key(game.id()); + for i in (game.actions_len() as isize..).step_by(GAME_ACTION_BLOCK_SIZE as usize) { + let actions: Vec> = self.redis.lrange(&key, i, i + GAME_ACTION_BLOCK_SIZE).await?; + if actions.is_empty() { break; } + for AsJson(action) in actions { + game.take_action(action).map_err(|err| RedisError::from((ErrorKind::ResponseError, "Invalid action")))?; + } + } + Ok(()) + } + + pub async fn get_game(&mut self, id: u32) -> RedisResult { + let settings = self.redis.lindex("games", id as isize).await.map(AsJson::get)?; + let mut game = Game::new(id, settings); + self.update_game_state(&mut game).await?; + Ok(game) + } + + pub async fn take_action(&mut self, id: u32, len: usize, action: &UserAction) -> RedisResult { + let key = game_key(id); + debug!("redis: executing: EVAL {:?} 1 {:?} {} {:?}", TAKE_ACTION_LUA_SCRIPT, key, len, serde_json::to_string(&action).unwrap()); + redis::cmd("EVAL").arg(TAKE_ACTION_LUA_SCRIPT).arg(1).arg(key).arg(len).arg(AsJson(action)).query_async(&mut self.redis).await + } +} + +pub enum ActionStatus { + Committed, + Interrupted, +} + +impl FromRedisValue for ActionStatus { + fn from_redis_value(value: &Value) -> RedisResult { + match value { + Value::Int(0) => Ok(ActionStatus::Interrupted), + Value::Int(_) => Ok(ActionStatus::Committed), + _ => Err(RedisError::from((ErrorKind::TypeError, "ActionStatus may only be converted from an integer"))) + } + } +} + +struct AsJson(T); + +impl AsJson { + pub fn get(self) -> T { + self.0 + } +} + +impl FromRedisValue for AsJson + where T: for<'a> Deserialize<'a> +{ + fn from_redis_value(value: &Value) -> RedisResult { + match value { + Value::Data(ref bytes) => serde_json::from_slice(bytes).map(AsJson) + .map_err(|err| RedisError::from(( + ErrorKind::TypeError, + "Failed to parse as JSON", + err.to_string() + ))), + _ => Err(RedisError::from((ErrorKind::TypeError, "Value was not Value::Data"))), + } + } +} + +impl ToRedisArgs for AsJson + where T: Serialize +{ + fn write_redis_args(&self, out: &mut W) + where W: ?Sized + RedisWrite + { + match serde_json::to_vec(&self.0) { + Ok(bytes) => out.write_arg(&bytes), + Err(_) => return, + } + } + + fn is_single_arg(&self) -> bool { + true + } +}