diff --git a/Cargo.lock b/Cargo.lock index 292bbda..f7ea7a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -26,6 +37,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -118,6 +141,12 @@ dependencies = [ "password-hash", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "async-stream" version = "0.3.6" @@ -151,6 +180,15 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits 0.2.19", +] + [[package]] name = "atomic" version = "0.5.3" @@ -221,6 +259,20 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +[[package]] +name = "bigdecimal" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a22f228ab7a1b23027ccc6c350b72868017af7ea8356fbdf19f8d991c690013" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits 0.2.19", + "serde", +] + [[package]] name = "binascii" version = "0.1.4" @@ -238,6 +290,21 @@ name = "bitflags" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] [[package]] name = "blake2" @@ -257,12 +324,57 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borsh" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +dependencies = [ + "once_cell", + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.21.0" @@ -334,6 +446,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits 0.2.19", + "serde", "wasm-bindgen", "windows-targets 0.52.6", ] @@ -366,7 +479,7 @@ version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn 2.0.96", @@ -384,6 +497,21 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "cookie" version = "0.18.1" @@ -410,6 +538,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -419,6 +562,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crunchy" version = "0.2.3" @@ -435,6 +593,51 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -442,6 +645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -484,6 +688,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -499,11 +704,20 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] [[package]] name = "encoding_rs" @@ -553,6 +767,28 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -583,12 +819,29 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -604,6 +857,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.31" @@ -634,6 +893,28 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.31" @@ -768,11 +1049,40 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "heck" @@ -804,6 +1114,33 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99e1673f5f7a251bfd8e3411babca6442952c7ba37289aa4f9d18fcb45411481" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "http" version = "0.2.12" @@ -1099,6 +1436,12 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.0.3" @@ -1127,10 +1470,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.2", "serde", ] +[[package]] +name = "inherent" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c38228f24186d9cc68c729accb4d413be9eaed6ad07ff79e0270d9e56f3de13" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "inlinable_string" version = "0.1.15" @@ -1160,6 +1514,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" @@ -1196,6 +1559,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1234,6 +1600,23 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "libm" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -1286,6 +1669,16 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.4" @@ -1377,6 +1770,23 @@ dependencies = [ "num-traits 0.2.19", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits 0.2.19", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-complex" version = "0.4.6" @@ -1439,6 +1849,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1466,7 +1877,7 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", @@ -1493,12 +1904,51 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits 0.2.19", +] + +[[package]] +name = "ouroboros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.96", +] + [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1566,12 +2016,30 @@ dependencies = [ "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pgvector" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0e8871b6d7ca78348c6cd29b911b94851f3429f0cd403130ca17f26c1fb91a6" +dependencies = [ + "serde", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1584,6 +2052,33 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "polariton" version = "0.2.0" @@ -1638,6 +2133,37 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit 0.22.24", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "proc-macro2" version = "1.0.93" @@ -1660,6 +2186,26 @@ dependencies = [ "yansi", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quinn" version = "0.11.6" @@ -1721,6 +2267,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.4.6" @@ -1827,6 +2379,7 @@ dependencies = [ name = "rc_chat_room" version = "0.2.0" dependencies = [ + "async-trait", "clap", "env_logger", "log", @@ -1845,6 +2398,7 @@ name = "rc_core" version = "0.2.0" dependencies = [ "argon2", + "async-trait", "chrono", "hex", "jsonwebtoken", @@ -1852,11 +2406,21 @@ dependencies = [ "log", "polariton", "polariton_server", + "rc_database", "serde", "serde_json", "tokio", ] +[[package]] +name = "rc_database" +version = "0.2.0" +dependencies = [ + "itertools", + "sea-orm", + "sea-orm-migration", +] + [[package]] name = "rc_microtransactions" version = "0.2.0" @@ -1884,6 +2448,7 @@ dependencies = [ name = "rc_services_room" version = "0.2.0" dependencies = [ + "async-trait", "base64 0.22.1", "chrono", "clap", @@ -1917,6 +2482,7 @@ dependencies = [ name = "rc_singleplayer_room" version = "0.2.0" dependencies = [ + "async-trait", "clap", "env_logger", "log", @@ -1944,6 +2510,7 @@ dependencies = [ name = "rc_social_room" version = "0.2.0" dependencies = [ + "async-trait", "clap", "env_logger", "log", @@ -2047,6 +2614,15 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "reqwest" version = "0.12.12" @@ -2105,6 +2681,35 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rocket" version = "0.5.1" @@ -2187,6 +2792,42 @@ dependencies = [ "uncased", ] +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits 0.2.19", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rust_decimal" +version = "1.37.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa7de2ba56ac291bd90c6b9bece784a52ae1411f9506544b3eae36dd2356d50" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits 0.2.19", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2280,6 +2921,171 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sea-bae" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f694a6ab48f14bc063cfadff30ab551d3c7e46d8f81836c51989d548f44a2a25" +dependencies = [ + "heck 0.4.1", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "sea-orm" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e61af841881c137d4bc8e0d8411cee9168548b404f9e4788e8af7e8f94bd4e" +dependencies = [ + "async-stream", + "async-trait", + "bigdecimal", + "chrono", + "futures-util", + "log", + "ouroboros", + "pgvector", + "rust_decimal", + "sea-orm-macros", + "sea-query", + "sea-query-binder", + "serde", + "serde_json", + "sqlx", + "strum", + "thiserror 2.0.11", + "time", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "sea-orm-cli" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3d39b35e43a05998228f6c4abaa50b7fcaf001dea94fbdc04188a8be82a016" +dependencies = [ + "chrono", + "clap", + "dotenvy", + "glob", + "regex", + "sea-schema", + "tracing", + "tracing-subscriber", + "url", +] + +[[package]] +name = "sea-orm-macros" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b86e3e77b548e6c6c1f612a1ca024d557dffdb81b838bf482ad3222140c77b" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "sea-bae", + "syn 2.0.96", + "unicode-ident", +] + +[[package]] +name = "sea-orm-migration" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae8b2af58a56c8f850e8d8a18a462255eb36a571f085f3405f6b83d9cf23e0f" +dependencies = [ + "async-trait", + "clap", + "dotenvy", + "sea-orm", + "sea-orm-cli", + "sea-schema", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "sea-query" +version = "0.32.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d99447c24da0cded00089e2021e1624af90878c65f7534319448d01da3df869d" +dependencies = [ + "bigdecimal", + "chrono", + "inherent", + "ordered-float", + "rust_decimal", + "sea-query-derive", + "serde_json", + "time", + "uuid", +] + +[[package]] +name = "sea-query-binder" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0019f47430f7995af63deda77e238c17323359af241233ec768aba1faea7608" +dependencies = [ + "bigdecimal", + "chrono", + "rust_decimal", + "sea-query", + "serde_json", + "sqlx", + "time", + "uuid", +] + +[[package]] +name = "sea-query-derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae0cbad6ab996955664982739354128c58d16e126114fe88c2a493642502aab" +dependencies = [ + "darling", + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.96", + "thiserror 2.0.11", +] + +[[package]] +name = "sea-schema" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef5dd7848c993f3789d09a2616484c72c9330cae2b048df59d8c9b8c0343e95" +dependencies = [ + "futures", + "sea-query", + "sea-schema-derive", +] + +[[package]] +name = "sea-schema-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "debdc8729c37fdbf88472f97fd470393089f997a909e535ff67c544d18cfccf0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "serde" version = "1.0.217" @@ -2337,6 +3143,28 @@ dependencies = [ name = "servers" version = "0.2.0" +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -2361,6 +3189,22 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "simple-rijndael" version = "0.3.2" @@ -2393,6 +3237,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -2409,6 +3256,229 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlx" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3" +dependencies = [ + "base64 0.22.1", + "bigdecimal", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.2", + "hashlink", + "indexmap", + "log", + "memchr", + "once_cell", + "percent-encoding", + "rust_decimal", + "rustls", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror 2.0.11", + "time", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid", + "webpki-roots", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.96", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7" +dependencies = [ + "dotenvy", + "either", + "heck 0.5.0", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.96", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0afdd3aa7a629683c2d750c2df343025545087081ab5942593a5288855b1b7a7" +dependencies = [ + "atoi", + "base64 0.22.1", + "bigdecimal", + "bitflags 2.8.0", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rsa", + "rust_decimal", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.11", + "time", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6" +dependencies = [ + "atoi", + "base64 0.22.1", + "bigdecimal", + "bitflags 2.8.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "num-bigint", + "once_cell", + "rand 0.8.5", + "rust_decimal", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.11", + "time", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror 2.0.11", + "time", + "tracing", + "url", + "uuid", +] [[package]] name = "stable-pattern" @@ -2434,6 +3504,12 @@ dependencies = [ "loom", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "steamworks" version = "0.11.0" @@ -2452,12 +3528,29 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef6b00f8fe8eaaaff22cb9b70822a48c1a5d772bc682c202a57c0b438175845" +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + [[package]] name = "subtle" version = "2.6.1" @@ -2506,6 +3599,12 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.15.0" @@ -2697,7 +3796,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.22", + "toml_edit 0.22.24", ] [[package]] @@ -2722,15 +3821,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.24", + "winnow 0.7.6", ] [[package]] @@ -2766,6 +3865,7 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2852,12 +3952,33 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -2925,6 +4046,7 @@ checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" dependencies = [ "getrandom 0.2.15", "rand 0.8.5", + "serde", ] [[package]] @@ -2933,6 +4055,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -2963,6 +4091,12 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -3063,6 +4197,16 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "whoami" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +dependencies = [ + "redox_syscall", + "wasite", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3133,6 +4277,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -3283,9 +4436,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.24" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] @@ -3311,6 +4464,15 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "yansi" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index c0f2ade..18bbb2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,8 @@ members = [ "rc_static_data", "rc_microtransactions", "rc_social", "rc_social_room", "rc_chat", "rc_chat_room", - "rc_singleplayer", "rc_singleplayer_room", "rc_core", + "rc_singleplayer", "rc_singleplayer_room", + "rc_core", "rc_database", ] [workspace.dependencies] @@ -32,3 +33,4 @@ polariton = { version = "0.2", path = "../polariton", features = [ "tokio-async" polariton_server = { version = "0.2", path = "../polariton/server", features = [ "tokio-async" ] } serde = { version = "1.0", features = [ "derive" ] } serde_json = "1.0" +async-trait = "0.1" diff --git a/auth/src/common/cli.rs b/auth/src/common/cli.rs index c189444..31a01f3 100644 --- a/auth/src/common/cli.rs +++ b/auth/src/common/cli.rs @@ -7,6 +7,9 @@ pub struct CliArgs { /// Robocraft user data root #[arg(long, default_value_t = {"../data/robocraft".to_string()})] pub data_robocraft: String, + /// Robocraft asset data root + #[arg(long, default_value_t = {"../assets/robocraft".to_string()})] + pub assets_robocraft: String, } impl CliArgs { @@ -14,10 +17,10 @@ impl CliArgs { Self::parse() } - pub fn preloaded(self) -> Config { + pub async fn preloaded(self) -> Config { Config { #[cfg(feature = "robocraft")] - robocraft: crate::robocraft::RcConfig::from_args(&self), + robocraft: crate::robocraft::RcConfig::from_args(&self).await, } } } diff --git a/auth/src/main.rs b/auth/src/main.rs index 52b19c2..33a5023 100644 --- a/auth/src/main.rs +++ b/auth/src/main.rs @@ -17,12 +17,12 @@ fn index() -> String { } #[rocket::launch] -fn rocket() -> _ { +async fn rocket() -> _ { env_logger::init(); let args = common::cli::CliArgs::get(); #[allow(unused_mut)] let mut builder = rocket::build().mount("/", rocket::routes![index]) - .manage(args.preloaded()); + .manage(args.preloaded().await); #[cfg(feature = "cardlife")] {builder = builder.attach(cardlife::stage());} diff --git a/auth/src/robocraft/email.rs b/auth/src/robocraft/email.rs index a5c5212..94f8ee1 100644 --- a/auth/src/robocraft/email.rs +++ b/auth/src/robocraft/email.rs @@ -2,7 +2,7 @@ use rc_core::UserAuthenticator; use rocket::{post, routes, serde::json::Json, http::Status, State}; #[post("/authenticate/robocraft/game", data = "")] -pub fn email_password_auth(body: Json, config: &State) -> Result, Status> { +pub async fn email_password_auth(body: Json, config: &State) -> Result, Status> { log::info!("Authenticating {} user {}", body.target, body.display_name); let payload = libfj::robocraft::TokenPayload { public_id: body.display_name.clone(), @@ -16,7 +16,7 @@ pub fn email_password_auth(body: Json Self { + pub async fn from_args(args: &crate::common::cli::CliArgs) -> Self { + let conf = rc_core::persist::config::ConfigImpl::load(&args.assets_robocraft).expect("Bad config data"); Self { - account_provider: rc_core::UserImpl::load_for_auth(&args.data_robocraft).expect("Invalid Robocraft user data"), + account_provider: rc_core::UserImpl::load(&args.data_robocraft, &conf).await.expect("Invalid Robocraft user data"), root: args.data_robocraft.clone().into(), } } diff --git a/auth/src/robocraft/steam.rs b/auth/src/robocraft/steam.rs index 1d77e67..b15e1f0 100644 --- a/auth/src/robocraft/steam.rs +++ b/auth/src/robocraft/steam.rs @@ -2,7 +2,7 @@ use rc_core::UserAuthenticator; use rocket::{http::Status, post, routes, serde::json::Json, State}; #[post("/authenticate/steam/game", data = "")] -pub fn steam_auth(body: Json, config: &State) -> Result, Status> { +pub async fn steam_auth(body: Json, config: &State) -> Result, Status> { let steam_id = crate::common::steam_utils::authenticate_steam_ticket(&body.steam_ticket) .map_err(|_| Status { code: 401 })?; log::info!("Authenticating {} steam user {}", body.target, steam_id); @@ -18,7 +18,7 @@ pub fn steam_auth(body: Json, conf payload, extra: rc_core::persist::user::ExtraUserInfo::Steam { id: steam_id }, }; - let response = config.robocraft.account_provider.login(user_info) + let response = config.robocraft.account_provider.login(user_info).await .map_err(|e| { log::error!("Failed to authenticate {} steam user {}: {}", body.target, steam_id, e); Status { code: 401 } diff --git a/rc_chat_room/Cargo.toml b/rc_chat_room/Cargo.toml index 41056e1..68f600c 100644 --- a/rc_chat_room/Cargo.toml +++ b/rc_chat_room/Cargo.toml @@ -19,3 +19,4 @@ rc_core = { version = "*", path = "../rc_core" } serde.workspace = true serde_json.workspace = true regex = "1" +async-trait.workspace = true diff --git a/rc_chat_room/src/main.rs b/rc_chat_room/src/main.rs index 46a86b8..8f19175 100644 --- a/rc_chat_room/src/main.rs +++ b/rc_chat_room/src/main.rs @@ -24,7 +24,7 @@ async fn main() -> std::io::Result<()> { log::debug!("Got cli args {:?}", args); let cubes = rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data"); - let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).expect("Bad user data")); + let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).await.expect("Bad user data")); let chat_system = state::chat::ChatImpl::new(&args.assets, &args.data).expect("Bad chat config data"); diff --git a/rc_chat_room/src/operations/more_auth.rs b/rc_chat_room/src/operations/more_auth.rs index bbdd916..435d83a 100644 --- a/rc_chat_room/src/operations/more_auth.rs +++ b/rc_chat_room/src/operations/more_auth.rs @@ -31,9 +31,9 @@ impl MoreLobbyAuth { Some(map) } - fn do_auth(&self, params: std::collections::HashMap>, user: &crate::UserTy) -> Result, i16> { + async fn do_auth(&self, params: std::collections::HashMap>, user: &crate::UserTy) -> Result, i16> { if let Some(Typed::Str(auth_payload)) = params.get(&Self::AUTH_PAYLOAD_KEY) { - if user.update_with_auth_ext(&auth_payload.string, |t| self.build_ext_map(t)) { + if user.update_with_auth_ext(&auth_payload.string, |t| self.build_ext_map(t)).await { let user_impl = user.user()?; let name = user_impl.token().uuid.clone(); let chat_user = super::get_chat_user(user_impl.as_ref().as_ref()); @@ -49,12 +49,13 @@ impl MoreLobbyAuth { } } +#[async_trait::async_trait] impl Operation for MoreLobbyAuth { type User = crate::UserTy; - fn handle(&self, params: polariton::operation::ParameterTable, user: &Self::User) -> polariton::operation::OperationResponse { + async fn handle_async(&self, params: polariton::operation::ParameterTable, user: &Self::User) -> polariton::operation::OperationResponse { let params_dict = params.to_dict(); - match self.do_auth(params_dict, user) { + match self.do_auth(params_dict, user).await { Ok(params) => { polariton::operation::OperationResponse { code: Self::op_code(), diff --git a/rc_core/Cargo.toml b/rc_core/Cargo.toml index a70fe94..0dad21a 100644 --- a/rc_core/Cargo.toml +++ b/rc_core/Cargo.toml @@ -16,8 +16,11 @@ serde_json.workspace = true chrono = "0.4" polariton_server.workspace = true tokio = { version = "1.43", features = [ "net", "macros", "rt-multi-thread", "io-util", "time" ] } +async-trait.workspace = true # auth libfj.workspace = true jsonwebtoken = "9" argon2 = { version = "0.5", features = [ "std" ] } + +rc_database = { version = "0.2", path = "../rc_database" } diff --git a/rc_core/src/data/weapon_list.rs b/rc_core/src/data/weapon_list.rs index fc5a622..ec8953b 100644 --- a/rc_core/src/data/weapon_list.rs +++ b/rc_core/src/data/weapon_list.rs @@ -217,4 +217,43 @@ impl ItemCategory { pub fn but_bigger(&self) -> i32 { (*self as i32) * 100_000 } + + pub fn from_bigger(num: i32) -> Option { + Self::from_smaller(num / 100_000) + } + + pub fn from_smaller(num: i32) -> Option { + match num { + 0 => Some(Self::NoFunction), + 1 => Some(Self::Wheel), + 2 => Some(Self::Hover), + 3 => Some(Self::Wing), + 4 => Some(Self::Rudder), + 5 => Some(Self::Thruster), + 6 => Some(Self::InsectLeg), + 7 => Some(Self::MechLeg), + 8 => Some(Self::Ski), + 9 => Some(Self::TankTrack), + 10 => Some(Self::Rotor), + 11 => Some(Self::SprinterLeg), + 12 => Some(Self::Propeller), + 100 => Some(Self::Laser), + 200 => Some(Self::Plasma), + 250 => Some(Self::Mortar), + 300 => Some(Self::Rail), + 400 => Some(Self::Nano), + 500 => Some(Self::Tesla), + 600 => Some(Self::Aeroflak), + 650 => Some(Self::Ion), + 701 => Some(Self::Seeker), + 750 => Some(Self::Chaingun), + 800 => Some(Self::ShieldModule), + 801 => Some(Self::GhostModule), + 802 => Some(Self::BlinkModule), + 803 => Some(Self::EmpModule), + 804 => Some(Self::WindowmakerModule), + 900 => Some(Self::EnergyModule), + _ => None, + } + } } diff --git a/rc_core/src/persist/config/cubes_json.rs b/rc_core/src/persist/config/cubes_json.rs index ea558a7..19e59f1 100644 --- a/rc_core/src/persist/config/cubes_json.rs +++ b/rc_core/src/persist/config/cubes_json.rs @@ -255,4 +255,10 @@ impl super::ConfigProvider for CubeConfig { items: self.chat.public_channels.iter().map(|s| Typed::Str(s.into())).collect(), }) } + + fn server_config(&self) -> super::ServerConfig { + super::ServerConfig { + database: self.settings.server.database.clone(), + } + } } diff --git a/rc_core/src/persist/config/mod.rs b/rc_core/src/persist/config/mod.rs index 8d58647..9157a2c 100644 --- a/rc_core/src/persist/config/mod.rs +++ b/rc_core/src/persist/config/mod.rs @@ -2,7 +2,7 @@ mod cubes_json; pub use cubes_json::CubeConfig; mod traits; -pub use traits::{ConfigProvider, CompleteCampaignProvider, DevMessageProvider}; +pub use traits::{ConfigProvider, CompleteCampaignProvider, DevMessageProvider, ServerConfig}; pub type ConfigImpl = CubeConfig; diff --git a/rc_core/src/persist/config/traits.rs b/rc_core/src/persist/config/traits.rs index 1eb3976..27a124a 100644 --- a/rc_core/src/persist/config/traits.rs +++ b/rc_core/src/persist/config/traits.rs @@ -18,6 +18,7 @@ pub trait ConfigProvider { fn client_config(&self) -> Typed; fn login_messages(&self) -> DevMessageProvider; fn public_channels(&self) -> Typed; + fn server_config(&self) -> ServerConfig; } pub struct CompleteCampaignProvider { @@ -83,3 +84,7 @@ pub struct TypedDevMessage { pub message: Typed, pub display_time: Typed, } + +pub struct ServerConfig { + pub database: String, +} diff --git a/rc_core/src/persist/garage.rs b/rc_core/src/persist/garage.rs index b3a4866..0b286ac 100644 --- a/rc_core/src/persist/garage.rs +++ b/rc_core/src/persist/garage.rs @@ -99,6 +99,65 @@ impl std::convert::Into for GarageSlot } } +pub fn db_into_data(garage: rc_database::schema::garage::Model) -> crate::data::garage_bay::GarageSlotInfo { + crate::data::garage_bay::GarageSlotInfo { + name: garage.name, + cubes: 0, // TODO garage.cubes, + crf_id: garage.crf_id.unwrap_or(0), + was_rated: garage.was_rated, + movement_categories: movement_category_into_data(&garage.movement_categories), + uuid: i64_split(garage.uuid), + thumbnail_version: garage.thumbnail_version, + total_robot_cpu: garage.total_robot_cpu, + total_cosmetic_cpu: garage.total_cosmetic_cpu, + total_robot_ranking: garage.total_robot_ranking, + bay_cpu: garage.bay_cpu, + tutorial_robot: garage.tutorial_robot, + starter_robot_index: garage.starter_robot_index.map(|x| x as i32).unwrap_or(-1), + control_type: control_ty_into_data(garage.control_type), + control_options: crate::data::garage_bay::ControlOptions { + vertical_strafing: garage.vertical_strafing, + sideways_driving: garage.sideways_driving, + tracks_turn_on_spot: garage.tracks_turn_on_spot, + }, + mastery_level: garage.mastery_level as i32, + bay_skin_id: garage.bay_skin_id, + weapon_order: rc_database::schema::parse_int_csv(&garage.weapon_order).into_iter().map(|x| x as i32).collect(), + } +} + +fn movement_category_into_data(mov_cat: &str) -> Vec { + rc_database::schema::parse_int_csv(mov_cat) + .into_iter() + .filter_map(|num| crate::data::weapon_list::ItemCategory::from_bigger(num as _)) + .collect() +} + +pub fn i64_split(num: i64) -> (u32, u32) { + let bytes = (num as u64).to_le_bytes(); + ( + u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]), + u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) + ) +} + +#[allow(dead_code)] +pub fn u64_join(uuid: (u32, u32)) -> u64 { + let bytes = (uuid.0.to_le_bytes(), uuid.1.to_le_bytes()); + u64::from_le_bytes( + [bytes.0[0], bytes.0[1], bytes.0[2], bytes.0[3], + bytes.1[0], bytes.1[1], bytes.1[2], bytes.1[3]] + ) +} + +pub fn control_ty_into_data(control_ty: rc_database::schema::garage::ControlType) -> crate::data::garage_bay::ControlType { + match control_ty { + rc_database::schema::garage::ControlType::Camera => crate::data::garage_bay::ControlType::Camera, + rc_database::schema::garage::ControlType::Keyboard => crate::data::garage_bay::ControlType::Keyboard, + rc_database::schema::garage::ControlType::Count => crate::data::garage_bay::ControlType::Count, + } +} + #[derive(Serialize, Deserialize, Copy, Clone, Debug, Default)] pub enum ControlType { #[default] diff --git a/rc_core/src/persist/settings.rs b/rc_core/src/persist/settings.rs index 8370a46..2c09d95 100644 --- a/rc_core/src/persist/settings.rs +++ b/rc_core/src/persist/settings.rs @@ -6,6 +6,8 @@ pub struct Settings { pub gameplay: super::GameplaySettings, #[serde(default = "default_dev_messages")] pub banners: Vec, + #[serde(default = "default_server_conf")] + pub server: ServerSettings, } fn default_gameplay_settings() -> super::GameplaySettings { @@ -32,3 +34,19 @@ pub struct BannerMessage { fn default_dev_messages() -> Vec { Vec::default() } + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ServerSettings { + #[serde(default = "default_db_conn")] + pub database: String, +} + +fn default_db_conn() -> String { + "sqlite:../data/robocraft/accounts.sqlite.db?mode=rwc".to_owned() +} + +fn default_server_conf() -> ServerSettings { + ServerSettings { + database: default_db_conn(), + } +} diff --git a/rc_core/src/persist/user/account_json.rs b/rc_core/src/persist/user/account_json.rs index 52f0236..ba00394 100644 --- a/rc_core/src/persist/user/account_json.rs +++ b/rc_core/src/persist/user/account_json.rs @@ -1,74 +1,81 @@ use argon2::PasswordVerifier; -use serde::{Serialize, Deserialize}; use crate::persist::config::ConfigProvider; pub struct AccountProvider { - root: std::path::PathBuf, cubes: std::sync::Arc>, secret: Vec, + db: std::sync::Arc, } impl AccountProvider { - pub fn load(root: impl AsRef, cubes: &crate::persist::config::ConfigImpl) -> std::io::Result { + pub async fn load(root: impl AsRef, conf: &crate::persist::config::ConfigImpl) -> std::io::Result { let token_path = root.as_ref().join(super::TOKEN_SECRET_FILENAME); + let database_uri = >::server_config(conf).database; + log::debug!("Connecting to user database URI: {}", database_uri); + let db = rc_database::Database::init(&database_uri).await + .map_err(|e| std::io::Error::new(std::io::ErrorKind::NotConnected, e))?; let root = root.as_ref().join(super::USERS_DIR); std::fs::create_dir_all(&root)?; Ok(Self { - root, - cubes: std::sync::Arc::new(>::ids(cubes)), - secret: std::fs::read(&token_path)?, - }) - } - - pub fn load_for_auth(root: impl AsRef) -> std::io::Result { - let token_path = root.as_ref().join(super::TOKEN_SECRET_FILENAME); - let root = root.as_ref().join(super::USERS_DIR); - std::fs::create_dir_all(&root)?; - Ok(Self { - root, - cubes: std::sync::Arc::new(Vec::default()), + cubes: std::sync::Arc::new(>::ids(conf)), secret: std::fs::read(&token_path)?, + db: std::sync::Arc::new(db), }) } } +#[async_trait::async_trait] impl super::UserProvider for AccountProvider { - fn authenticate(&self, token: super::UserToken, ext: std::collections::HashMap>) -> Result + Send + Sync>, String> { - let new_root = self.root.join(&token.uuid); + async fn authenticate(&self, token: super::UserToken, ext: std::collections::HashMap>) -> Result + Send + Sync>, String> { + //let new_root = self.root.join(&token.uuid); let secret = jsonwebtoken::DecodingKey::from_secret(&self.secret); let mut validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256); validation.set_required_spec_claims::<&str>(&[]); jsonwebtoken::decode::(&token.token, &secret, &validation).map_err(|e| e.to_string())?; - let account_info = AccountInfo::load(&new_root).map_err(|e| e.to_string())?; + let user_info = if let Some(user_info) = self.db.user_by_public_id(token.uuid.clone()).await.map_err(|e| e.to_string())? { + user_info + } else { + return Err("User not found".to_owned()); + }; + let user_perms = if let Some(user_perms) = self.db.perms_by_user_id(user_info.id).await.map_err(|e| e.to_string())? { + user_perms + } else { + return Err("User permissions not found".to_owned()); + }; + //let account_info = AccountInfo::load(&new_root).map_err(|e| e.to_string())?; Ok(Box::new(UserData { - root: new_root, token, - account: account_info, + account: user_info, + perms: user_perms, cubes: self.cubes.clone(), extensions: ext, + db: self.db.clone(), })) //Err("Unable to authenticate".to_string()) } } + +#[async_trait::async_trait] impl super::UserAuthenticator for AccountProvider { - fn login(&self, info: super::UserInfo) -> Result { - let new_root = self.root.join(&info.payload.public_id); - let is_new_user = !new_root.exists(); - if is_new_user { - std::fs::create_dir(&new_root).map_err(|e| e.to_string())?; + async fn login(&self, info: super::UserInfo) -> Result { + //let new_root = self.root.join(&info.payload.public_id); + let is_new_user; + let mut user_info = if let Some(user_info) = self.db.user_by_public_id(info.payload.public_id.clone()).await.map_err(|e| e.to_string())? { + is_new_user = false; + user_info + } else { + is_new_user = true; log::info!("New user {}", info.payload.public_id); - super::setup_directory(&new_root).map_err(|e| e.to_string())?; - } - let mut account_info = AccountInfo::load(&new_root).map_err(|e| e.to_string())?; - let is_new_user = is_new_user || (account_info.password.is_none() && account_info.steam_id.is_none()); // migration + super::setup_new_user(&info, &self.db).await.map_err(|e| e.to_string())?; + self.db.user_by_public_id(info.payload.public_id.clone()).await.map_err(|e| e.to_string())?.unwrap() + }; + let override_password = user_info.password.is_empty() && user_info.steam_id.is_none(); match info.extra { super::ExtraUserInfo::Steam { id } => { - if is_new_user { - account_info.steam_id = Some(id); - } - if let Some(expected_steam_id) = account_info.steam_id { - if expected_steam_id != id { + let id_str = id.to_string(); + if let Some(expected_steam_id) = user_info.steam_id { + if expected_steam_id != id_str { return Err("SteamID does not match".to_owned()) } } else { @@ -78,13 +85,13 @@ impl super::UserAuthenticator for AccountProvider { super::ExtraUserInfo::Standalone { password } => { use argon2::password_hash::PasswordHasher; let argon2_algo = argon2::Argon2::default(); - if is_new_user { + if override_password { let salt = argon2::password_hash::SaltString::generate(&mut argon2::password_hash::rand_core::OsRng); let password_hash = argon2_algo.hash_password(password.as_bytes(), &salt).map_err(|e| e.to_string())?.to_string(); - account_info.password = Some(password_hash); + user_info.password = password_hash; } - if let Some(expected_password) = &account_info.password { - let expected = argon2::password_hash::PasswordHash::new(expected_password).map_err(|e| e.to_string())?; + if !user_info.password.is_empty() { + let expected = argon2::password_hash::PasswordHash::new(&user_info.password).map_err(|e| e.to_string())?; argon2_algo.verify_password(password.as_bytes(), &expected).map_err(|e| e.to_string())?; } else { return Err("Password not supported for this user".to_owned()) @@ -92,9 +99,6 @@ impl super::UserAuthenticator for AccountProvider { } } // authentication has now definitely succeeded - if is_new_user { - account_info.save(new_root).map_err(|e| e.to_string())?; - } // build token let header = jsonwebtoken::Header { typ: Some("JWT".to_string()), @@ -121,45 +125,35 @@ impl super::UserAuthenticator for AccountProvider { #[allow(dead_code)] struct UserData { - root: std::path::PathBuf, token: super::UserToken, - account: AccountInfo, + account: rc_database::schema::user::Model, + perms: rc_database::schema::permissions::Model, cubes: std::sync::Arc>, extensions: std::collections::HashMap>, + db: std::sync::Arc, } impl UserData { - fn load_garage_by_id(&self, id: u32) -> std::io::Result { - let path = self.root.join(super::GARAGE_DIR).join(format!("{}.json", id)); - crate::persist::GarageSlot::load(&path) + async fn load_garage_by_slot(&self, slot: u32) -> Result, rc_database::sea_orm::DbErr> { + //let path = self.root.join(super::GARAGE_DIR).join(format!("{}.json", id)); + //crate::persist::GarageSlot::load(&path) + self.db.garage_by_user_id_and_slot(self.account.id, slot).await } - fn save_garage(&self, slot: &crate::persist::GarageSlot) -> std::io::Result<()> { - let path = self.root.join(super::GARAGE_DIR).join(format!("{}.json", slot.slot)); - slot.save(path) + async fn save_garage_by_slot(&self, data: rc_database::schema::garage::ActiveModel, slot: u32) -> Result<(), rc_database::sea_orm::DbErr> { + self.db.update_garage_by_user_id_and_slot(data, self.account.id, slot).await?; + Ok(()) } - fn all_vehicles(&self) -> std::io::Result> { - let path = self.root.join(super::GARAGE_DIR); - let mut slots = Vec::new(); - for entry in std::fs::read_dir(path)? { - let entry = entry?; - let filepath = entry.path(); - if filepath.is_file() { - let slot = crate::persist::GarageSlot::load(&filepath)?; - slots.push(slot); - } else { - log::warn!("Ignoring non-file {} in {} dir", filepath.display(), super::GARAGE_DIR); - } - } - slots.sort_by_key(|slot| slot.slot); - Ok(slots) + async fn all_vehicles(&self) -> Result, rc_database::sea_orm::DbErr> { + self.db.garages_by_user_id(self.account.id).await } } -const INVALID_ROBOT_ERR: i16 = 140; -const DATABASE_ERR: i16 = 8; +const INVALID_ROBOT_ERR: i16 = crate::data::error_codes::WebServicesError::InvalidRobot as i16; // 140 +const DATABASE_ERR: i16 = crate::data::error_codes::WebServicesError::DatabaseError as i16; // 8 +#[async_trait::async_trait] impl super::User for UserData { fn ext(&self, ty: std::any::TypeId) -> Option<&'_ (dyn std::any::Any + Send + Sync + 'static)> { self.extensions.get(&ty).map(|x| x.as_ref()) @@ -170,35 +164,58 @@ impl super::User for UserData { } fn is_mod(&self) -> bool { - self.account.is_mod + self.perms.moderator } fn is_admin(&self) -> bool { - self.account.is_admin + self.perms.administrator } fn is_dev(&self) -> bool { - self.account.is_dev + self.perms.developer } - fn unlocked_parts(&self) -> Vec { - match self.account.inventory.override_ { - super::inventory::UnlockOverride::Normal => self.account.inventory.unlocked.clone(), - super::inventory::UnlockOverride::UnlockNone => Vec::default(), - super::inventory::UnlockOverride::UnlockAll => self.cubes.as_ref().to_owned(), + async fn unlocked_parts(&self) -> Vec { + match self.db.user_aux_by_user_id_and_descriptor(self.account.id, rc_database::schema::user_aux::Descriptor::UnlockedParts).await { + Ok(Some(parts)) => { + match serde_json::from_str::(&parts.data) { + Ok(json) => { + match json.override_ { + super::inventory::UnlockOverride::Normal => json.unlocked.clone(), + super::inventory::UnlockOverride::UnlockNone => Vec::default(), + super::inventory::UnlockOverride::UnlockAll => self.cubes.as_ref().to_owned(), + } + }, + Err(e) => { + log::error!("Failed to deserialize Descriptor::UnlockedParts for user_id {}: {}", self.account.id, e); + Vec::default() + } + } + }, + Ok(None) => Vec::default(), + Err(e) => { + log::error!("Failed to retrieve Descriptor::UnlockedParts for user_id {}: {}", self.account.id, e); + Vec::default() + } } } - fn selected_garage_uuid(&self) -> String { - self.account.garage.uuid_str() + async fn selected_garage(&self) -> (String, u32) { + match self.db.garage_selected(self.account.id).await { + Ok(Some(selected)) => (super::i64_as_uuid_str(selected.uuid), selected.slot), + Ok(None) => { + log::warn!("User {} does not have a selected garage", self.account.id); + ("0_0".to_owned(), 0) + }, + Err(e) => { + log::error!("Failed to retrieve selected garage for user_id {}: {}", self.account.id, e); + ("0_0".to_owned(), 0) + } + } } - fn selected_garage_slot(&self) -> u32 { - self.account.garage.slot - } - - fn all_slots_by_id(&self) -> super::UserSlots { - let slots = match self.all_vehicles() { + async fn all_slots_by_id(&self) -> super::UserSlots { + let slots = match self.all_vehicles().await { Ok(slots) => slots, Err(e) => { log::error!("Failed to load all vehicles: {}", e); @@ -211,7 +228,7 @@ impl super::User for UserData { val_ty: polariton::serdes:: TypePrefix::HashMap, items: slots.into_iter().map(|slot| { let slot_index = slot.slot; - let garage_data: crate::data::garage_bay::GarageSlotInfo = slot.into(); + let garage_data: crate::data::garage_bay::GarageSlotInfo = crate::persist::garage::db_into_data(slot); (polariton::operation::Typed::Int(slot_index as _), garage_data.as_transmissible()) }).collect(), }); @@ -220,77 +237,67 @@ impl super::User for UserData { } } - fn slot_by_id(&self, id: i32) -> Result, i16> { - match self.load_garage_by_id(id as _) { - Ok(slot) => { - let control_ty: crate::data::garage_bay::ControlType = slot.control_type.into(); - let control_options: crate::data::garage_bay::ControlOptions = slot.control_options.into(); + async fn slot_by_id(&self, id: i32) -> Result, i16> { + match self.load_garage_by_slot(id as _).await { + Ok(Some(slot)) => { + let cube_count = slot.cube_count() as i32; + let control_ty: crate::data::garage_bay::ControlType = crate::persist::garage::control_ty_into_data(slot.control_type); + let control_options = crate::data::garage_bay::ControlOptions { + vertical_strafing: slot.vertical_strafing, + sideways_driving: slot.sideways_driving, + tracks_turn_on_spot: slot.tracks_turn_on_spot, + }; Ok(crate::persist::user::UserSlotData { data: polariton::operation::Typed::Bytes(slot.robot_data.into()), colour_data: polariton::operation::Typed::Bytes(slot.colour_data.into()), - cube_count: polariton::operation::Typed::Int(slot.cubes as _), - weapon_order: polariton::operation::Typed::IntArr(slot.weapon_order.clone().into()), - movement_categories: polariton::operation::Typed::IntArr(slot.movement_categories.into_iter().map(|cat| { - let cat: crate::data::weapon_list::ItemCategory = cat.into(); - cat.but_bigger() - }).collect::>().into()), + cube_count: polariton::operation::Typed::Int(cube_count), + weapon_order: polariton::operation::Typed::IntArr(rc_database::schema::parse_int_csv(&slot.weapon_order).into_iter().map(|x| x as i32).collect::>().into()), + movement_categories: polariton::operation::Typed::IntArr(rc_database::schema::parse_int_csv(&slot.movement_categories).into_iter().map(|x| x as i32).collect::>().into()), control_type: polariton::operation::Typed::Int(control_ty as _), control_options: control_options.as_transmissible(), - mastery_level: polariton::operation::Typed::Int(0), // TODO + mastery_level: polariton::operation::Typed::Int(slot.mastery_level as i32), robot_rank: polariton::operation::Typed::Int(slot.total_robot_ranking as _), cpu: polariton::operation::Typed::Int(slot.total_robot_cpu as _), cosmetic_cpu: polariton::operation::Typed::Int(slot.total_cosmetic_cpu as _), - uuid: polariton::operation::Typed::Str(format!("{}_{}", slot.uuid.0, slot.uuid.1).into()), + uuid: polariton::operation::Typed::Str(super::i64_as_uuid_str(slot.uuid).into()), }) }, + Ok(None) => { + log::error!("Failed to find vehicle slot {} for user_id {}", id, self.account.id); + Err(DATABASE_ERR) + } Err(e) => { - log::error!("Failed to load vehicle {}: {}", id, e); + log::error!("Failed to retrieve vehicle {} for user_id {}: {}", id, self.account.id, e); Err(INVALID_ROBOT_ERR) } } } - fn save_slot(&self, vehicle: crate::persist::user::VehicleData) -> Result<(), i16> { - let id = vehicle.id as u32; - let mut existing_data = self.load_garage_by_id(id).map_err(|e| { - log::error!("Failed to load vehicle {}: {}", id, e); - INVALID_ROBOT_ERR - })?; - existing_data.slot = id; - existing_data.robot_data = vehicle.robot_data; - existing_data.colour_data = vehicle.colour_data; - log::debug!("weapon order: {:?}", vehicle.weapon_order); - existing_data.weapon_order = vehicle.weapon_order; - self.save_garage(&existing_data).map_err(|e| { - log::error!("Failed to save vehicle {}: {}", id, e); + async fn save_slot(&self, vehicle: crate::persist::user::VehicleData) -> Result<(), i16> { + let entity = rc_database::schema::garage::ActiveModel { + robot_data: rc_database::sea_orm::ActiveValue::Set(vehicle.robot_data), + colour_data: rc_database::sea_orm::ActiveValue::Set(vehicle.colour_data), + weapon_order: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::dump_csv(&vehicle.weapon_order)), + ..Default::default() + }; + self.save_garage_by_slot(entity, vehicle.slot as u32).await.map_err(|e| { + log::error!("Failed to save vehicle slot {} for user_id {}: {}", vehicle.slot, self.account.id, e); DATABASE_ERR })?; Ok(()) } fn signup_date(&self) -> i64 { - match self.root.metadata() { - Ok(meta) => { - match meta.created() { - Ok(created) => { - match created.duration_since(std::time::SystemTime::UNIX_EPOCH) { - Ok(dur) => { - return super::since_windows_epoch(dur.as_secs() as i64); - }, - Err(e) => log::error!("could not get duration since unix epoch of {}: {}", self.root.display(), e), - } - }, - Err(e) => log::error!("could not read creation time of {}: {}", self.root.display(), e), - } - }, - Err(e) => log::error!("could not retrieve metadata of {}: {}", self.root.display(), e), - } - super::since_windows_epoch(0) + super::since_windows_epoch(self.account.creation_time) } - fn singleplayer_robots(&self) -> Result, i16> { - let current_slot = self.load_garage_by_id(self.account.garage.slot).map_err(|e| { - log::error!("Failed to load current vehicle: {}", e); + async fn singleplayer_robots(&self) -> Result, i16> { + let current_slot = self.db.garage_selected(self.account.id).await.map_err(|e| { + log::error!("Failed to retrieve selected vehicle for user_id {} (singleplayer_robots): {}", self.account.id, e); + DATABASE_ERR + })? + .ok_or_else(|| { + log::error!("Failed to find selected vehicle for user_id {} (singleplayer_robots)", self.account.id); INVALID_ROBOT_ERR })?; let user_uuid = self.token.uuid.clone(); @@ -298,52 +305,24 @@ impl super::User for UserData { players: vec![ crate::data::player_data::PlayerData { name: user_uuid.clone(), - display_name: user_uuid, - mastery: current_slot.mastery_level, + display_name: self.account.display_name.clone(), + mastery: current_slot.mastery_level as i32, tier: 1, // FIXME robot_name: current_slot.name, robot_map: current_slot.robot_data, team: 0, has_premium: true, // FIXME - robot_uuid: format!("{}_{}", current_slot.uuid.0, current_slot.uuid.1), + robot_uuid: super::i64_as_uuid_str(current_slot.uuid).into(), cpu: current_slot.total_robot_cpu as i32, - weapon_order: current_slot.weapon_order.clone(), + weapon_order: rc_database::schema::parse_int_csv(¤t_slot.weapon_order).into_iter().map(|x| x as i32).collect::>(), colour_map: current_slot.colour_data, is_ai: false, spawn_effect: "Spawn_Warp".to_owned(), // FIXME death_effect: "Explosion_Warp".to_owned(), // FIXME player_rank: 1, // FIXME - weapon_rank: current_slot.weapon_order.into_iter().map(|x| (x, if x == 0 { 0 } else { 1 })).collect(), + weapon_rank: rc_database::schema::parse_int_csv(¤t_slot.weapon_order).into_iter().map(|x| (x as i32, if x == 0 { 0 } else { 1 })).collect(), } ], }.as_transmissible()) } } - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct AccountInfo { - pub is_mod: bool, - pub is_admin: bool, - pub is_dev: bool, - pub password: Option, - pub steam_id: Option, - pub inventory: super::UnlockedParts, - pub garage: super::SelectedGarage, -} - -impl AccountInfo { - fn load(root: impl AsRef) -> std::io::Result { - let file = std::fs::File::open(root.as_ref().join(super::USER_FILE))?; - let buffered = std::io::BufReader::new(file); - let result = serde_json::from_reader(buffered)?; - Ok(result) - } - - pub fn save(&self, root: impl AsRef) -> std::io::Result<()> { - let file = std::fs::File::create(root.as_ref().join(super::USER_FILE))?; - let buffered = std::io::BufWriter::new(file); - serde_json::to_writer_pretty(buffered, self)?; - Ok(()) - } -} - diff --git a/rc_core/src/persist/user/initial_data.rs b/rc_core/src/persist/user/initial_data.rs index 2be1da6..a8c2191 100644 --- a/rc_core/src/persist/user/initial_data.rs +++ b/rc_core/src/persist/user/initial_data.rs @@ -1,30 +1,68 @@ -const REFERENCE_DIR: &str = "layout folder"; +//const REFERENCE_DIR: &str = "layout folder"; -fn build_reference_directory(root: impl AsRef) -> std::io::Result<()> { - std::fs::create_dir(&root)?; - let garage_dir = root.as_ref().join(super::GARAGE_DIR); - std::fs::create_dir(&garage_dir)?; - default_user_data().save(&root)?; - for slot in default_garage_slots() { +fn current_unix_time() -> i64 { + chrono::Utc::now().timestamp() +} + +async fn build_new_account_data(user: &super::UserInfo, db: &rc_database::Database) -> Result<(), rc_database::sea_orm::DbErr> { + //std::fs::create_dir(&root)?; + //let garage_dir = root.as_ref().join(super::GARAGE_DIR); + //std::fs::create_dir(&garage_dir)?; + let user_data = db.insert_user(default_user_data(user)).await?; + db.insert_perms(default_user_perms(user_data.id)).await?; + db.insert_user_aux(default_user_aux_data(user_data.id)).await?; + db.insert_garages(default_garage_slots(user_data.id)).await?; + /*for slot in default_garage_slots() { let filepath = garage_dir.join(format!("{}.json", slot.slot)); slot.save(filepath)?; - } + }*/ Ok(()) } -pub fn setup_directory(new_dir: impl AsRef) -> std::io::Result<()> { - let ref_path = new_dir.as_ref().parent().unwrap().join(REFERENCE_DIR); +pub async fn setup_new_user(user: &super::UserInfo, db: &rc_database::Database) -> Result<(), rc_database::sea_orm::DbErr> { + /*let ref_path = new_dir.as_ref().parent().unwrap().join(REFERENCE_DIR); if !ref_path.exists() { log::debug!("Initialising reference directory {}", ref_path.display()); - build_reference_directory(&ref_path)?; + build_reference_directory(user)?; } log::debug!("Copying reference directory for new user: {}", new_dir.as_ref().display()); - so::copy_dir_all(ref_path, new_dir)?; + so::copy_dir_all(ref_path, new_dir)?;*/ + build_new_account_data(user, db).await?; Ok(()) } -fn default_user_data() -> super::AccountInfo { - super::AccountInfo { +fn default_user_data(user: &super::UserInfo) -> rc_database::schema::user::ActiveModel { + let password = if let super::ExtraUserInfo::Standalone { password } = &user.extra { + //password.to_owned() + use argon2::password_hash::PasswordHasher; + let argon2_algo = argon2::Argon2::default(); + let salt = argon2::password_hash::SaltString::generate(&mut argon2::password_hash::rand_core::OsRng); + match argon2_algo.hash_password(password.as_bytes(), &salt) { + Err(e) => { + log::error!("Failed to hash password for user {}/{}: {}", user.payload.public_id, user.payload.display_name, e); + "".to_owned() + }, + Ok(password) => password.to_string(), + } + } else { + Default::default() + }; + let steam_id = if let super::ExtraUserInfo::Steam { id } = &user.extra { + Some(id.to_string()) + } else { + None + }; + rc_database::schema::user::ActiveModel { + id: Default::default(), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + public_id: rc_database::sea_orm::ActiveValue::Set(user.payload.public_id.clone()), + display_name: rc_database::sea_orm::ActiveValue::Set(user.payload.display_name.clone()), + password: rc_database::sea_orm::ActiveValue::Set(password), + email: rc_database::sea_orm::ActiveValue::Set("//TODO".to_owned()), + steam_id: rc_database::sea_orm::ActiveValue::Set(steam_id), + } + + /*super::AccountInfo { is_mod: false, is_admin: false, is_dev: false, @@ -38,11 +76,112 @@ fn default_user_data() -> super::AccountInfo { uuid: (0, 0), slot: 0, }, + }*/ +} + +fn default_user_aux_data(user_id: u32) -> Vec { + vec![ + rc_database::schema::user_aux::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::UserXP), + data: rc_database::sea_orm::ActiveValue::Set("0".to_owned()), + }, + rc_database::schema::user_aux::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::PremiumExpiry), + data: rc_database::sea_orm::ActiveValue::Set(current_unix_time().to_string()), + }, + rc_database::schema::user_aux::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::UnlockedParts), + data: rc_database::sea_orm::ActiveValue::Set( +r#"{ + "unlocked": [], + "override": "UnlockAll" +}"#.to_owned()), + }, + rc_database::schema::user_aux::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::TechPoints), + data: rc_database::sea_orm::ActiveValue::Set("1337".to_owned()), + }, + rc_database::schema::user_aux::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::UserRank), + data: rc_database::sea_orm::ActiveValue::Set("1".to_owned()), + }, + rc_database::schema::user_aux::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::UserFreeCurrency), + data: rc_database::sea_orm::ActiveValue::Set("10000".to_owned()), + }, + rc_database::schema::user_aux::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::UserPaidCurrency), + data: rc_database::sea_orm::ActiveValue::Set("1000".to_owned()), + } + ] +} + +fn default_user_perms(user_id: u32) -> rc_database::schema::permissions::ActiveModel { + rc_database::schema::permissions::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + moderator: rc_database::sea_orm::ActiveValue::Set(false), + administrator: rc_database::sea_orm::ActiveValue::Set(false), + developer: rc_database::sea_orm::ActiveValue::Set(false), + royalty: rc_database::sea_orm::ActiveValue::Set(false), + banned: rc_database::sea_orm::ActiveValue::Set(false), } } -fn default_garage_slots() -> Vec { +fn default_garage_slots(user_id: u32) -> Vec { + let current_time = current_unix_time(); vec![ + rc_database::schema::garage::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_time), + slot: rc_database::sea_orm::ActiveValue::Set(0), + name: rc_database::sea_orm::ActiveValue::Set("Bay 0".to_owned()), + crf_id: rc_database::sea_orm::ActiveValue::Set(None), + was_rated: rc_database::sea_orm::ActiveValue::Set(false), + movement_categories: rc_database::sea_orm::ActiveValue::Set("".to_owned()), + uuid: rc_database::sea_orm::ActiveValue::Set(current_time), + thumbnail_version: rc_database::sea_orm::ActiveValue::Set(0), + total_robot_cpu: rc_database::sea_orm::ActiveValue::Set(0), + total_cosmetic_cpu: rc_database::sea_orm::ActiveValue::Set(0), + total_robot_ranking: rc_database::sea_orm::ActiveValue::Set(0), + bay_cpu: rc_database::sea_orm::ActiveValue::Set(2_000), + tutorial_robot: rc_database::sea_orm::ActiveValue::Set(false), + starter_robot_index: rc_database::sea_orm::ActiveValue::Set(None), + control_type: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::garage::ControlType::Camera), + vertical_strafing: rc_database::sea_orm::ActiveValue::Set(false), + sideways_driving: rc_database::sea_orm::ActiveValue::Set(false), + tracks_turn_on_spot: rc_database::sea_orm::ActiveValue::Set(false), + mastery_level: rc_database::sea_orm::ActiveValue::Set(1), + bay_skin_id: rc_database::sea_orm::ActiveValue::Set("RC_MothershipSkin_Neptune_01".to_owned()), + weapon_order: rc_database::sea_orm::ActiveValue::Set("".to_owned()), + robot_data: rc_database::sea_orm::ActiveValue::Set(vec![0u8; 4]), + colour_data: rc_database::sea_orm::ActiveValue::Set(vec![0u8; 4]), + selected: rc_database::sea_orm::ActiveValue::Set(true), + } + ] + /*vec![ crate::persist::GarageSlot { slot: 0, name: "Bay 1".to_owned(), @@ -66,25 +205,5 @@ fn default_garage_slots() -> Vec { robot_data: vec![0; 4], colour_data: vec![0; 4], } - ] -} - -mod so { - // from https://stackoverflow.com/questions/26958489/how-to-copy-a-folder-recursively-in-rust - use std::path::Path; - use std::{io, fs}; - - pub(super) fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { - fs::create_dir_all(&dst)?; - for entry in fs::read_dir(src)? { - let entry = entry?; - let ty = entry.file_type()?; - if ty.is_dir() { - copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; - } else { - fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; - } - } - Ok(()) - } + ]*/ } diff --git a/rc_core/src/persist/user/mod.rs b/rc_core/src/persist/user/mod.rs index 9ab4f0c..8fac9b6 100644 --- a/rc_core/src/persist/user/mod.rs +++ b/rc_core/src/persist/user/mod.rs @@ -1,11 +1,11 @@ mod account_json; -pub use account_json::{AccountProvider, AccountInfo}; +pub use account_json::AccountProvider; mod garage_data; pub use garage_data::SelectedGarage; mod initial_data; -pub use initial_data::setup_directory; +pub use initial_data::setup_new_user; mod inventory; pub use inventory::UnlockedParts; @@ -34,3 +34,11 @@ pub fn since_windows_epoch(since_unix_epoch: i64) -> i64 { //let time_in = chrono::Utc.from_utc_datetime(&chrono::NaiveDateTime::from_timestamp(since_unix_epoch, 0)); time_in.signed_duration_since(windows_epoch).num_milliseconds() * 10_000 } + +pub fn uuid_str(uuid: &(u32, u32)) -> String { + format!("{}_{}", uuid.0, uuid.1) +} + +pub fn i64_as_uuid_str(num: i64) -> String { + uuid_str(&super::garage::i64_split(num)) +} diff --git a/rc_core/src/persist/user/traits.rs b/rc_core/src/persist/user/traits.rs index c47e73b..1d4dcda 100644 --- a/rc_core/src/persist/user/traits.rs +++ b/rc_core/src/persist/user/traits.rs @@ -25,28 +25,30 @@ pub struct UserLoginInfo { pub is_new: bool, } +#[async_trait::async_trait] pub trait UserProvider { - fn authenticate(&self, user: UserToken, ext: std::collections::HashMap>) -> Result + Send + Sync>, String>; + async fn authenticate(&self, user: UserToken, ext: std::collections::HashMap>) -> Result + Send + Sync>, String>; } +#[async_trait::async_trait] pub trait UserAuthenticator { - fn login(&self, info: UserInfo) -> Result; + async fn login(&self, info: UserInfo) -> Result; } +#[async_trait::async_trait] pub trait User { fn ext(&self, ty: std::any::TypeId) -> Option<&'_ (dyn std::any::Any + Send + Sync + 'static)>; fn token(&self) -> &'_ super::UserToken; fn is_mod(&self) -> bool; fn is_admin(&self) -> bool; fn is_dev(&self) -> bool; - fn unlocked_parts(&self) -> Vec; - fn selected_garage_uuid(&self) -> String; - fn selected_garage_slot(&self) -> u32; - fn all_slots_by_id(&self) -> UserSlots; - fn slot_by_id(&self, id: i32) -> Result, i16>; - fn save_slot(&self, vehicle: VehicleData) -> Result<(), i16>; + async fn unlocked_parts(&self) -> Vec; + async fn selected_garage(&self) -> (String, u32); + async fn all_slots_by_id(&self) -> UserSlots; + async fn slot_by_id(&self, id: i32) -> Result, i16>; + async fn save_slot(&self, vehicle: VehicleData) -> Result<(), i16>; fn signup_date(&self) -> i64; - fn singleplayer_robots(&self) -> Result, i16>; + async fn singleplayer_robots(&self) -> Result, i16>; } pub struct UserSlots { @@ -70,7 +72,7 @@ pub struct UserSlotData { } pub struct VehicleData { - pub id: i32, + pub slot: i32, pub robot_data: Vec, pub colour_data: Vec, pub weapon_order: Vec, diff --git a/rc_core/src/state.rs b/rc_core/src/state.rs index ce02458..791ea67 100644 --- a/rc_core/src/state.rs +++ b/rc_core/src/state.rs @@ -7,13 +7,14 @@ pub struct UserState { } impl UserState { - pub fn update_with_auth(&self, auth_str: &str) -> bool { - self.update_with_auth_ext(auth_str, |_| Some(Default::default())) + pub async fn update_with_auth(&self, auth_str: &str) -> bool { + self.update_with_auth_ext(auth_str, |_| Some(Default::default())).await } - pub fn update_with_auth_ext Option>>>(&self, auth_str: &str, ext_f: F) -> bool { - let mut lock = self.state.write().unwrap(); - match &*lock { + pub async fn update_with_auth_ext Option>>>(&self, auth_str: &str, ext_f: F) -> bool { + //let mut lock = self.state.write().unwrap(); + let init_state_clone = self.state.write().unwrap().clone(); + match init_state_clone { InitState::Unauthenticated(auth) => { let splits: Vec<&str> = auth_str.split(';').collect(); if splits.len() != 3 { @@ -30,8 +31,9 @@ impl UserState { } else { return false; }; - match auth.authenticate(token, ext) { + match auth.authenticate(token, ext).await { Ok(user) => { + let mut lock = self.state.write().unwrap(); *lock = InitState::Authenticated(std::sync::Arc::new(user)); true }, @@ -74,6 +76,7 @@ impl UserState { } } +#[derive(Clone)] enum InitState { Unauthenticated(std::sync::Arc), Authenticated(std::sync::Arc + Send + Sync>>), diff --git a/rc_database/Cargo.toml b/rc_database/Cargo.toml new file mode 100644 index 0000000..c32fefe --- /dev/null +++ b/rc_database/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "rc_database" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +authors.workspace = true +readme.workspace = true + +[features] +all_databases = ["mysql", "postgres", "sqlite"] +mysql = [ "sea-orm/sqlx-mysql" ] +postgres = [ "sea-orm/sqlx-postgres" ] +sqlite = [ "sea-orm/sqlx-sqlite" ] +default = [ "all_databases" ] + +[dependencies] +sea-orm = { version = "1.1.10", features = [ "runtime-tokio-rustls", "macros" ] } +sea-orm-migration = "1.1.10" +itertools = "0.14" diff --git a/rc_database/src/lib.rs b/rc_database/src/lib.rs new file mode 100644 index 0000000..df02b9c --- /dev/null +++ b/rc_database/src/lib.rs @@ -0,0 +1,9 @@ +mod migration; +pub use migration::Migrator; + +pub mod schema; + +mod wrapper; +pub use wrapper::Database; + +pub use sea_orm; diff --git a/rc_database/src/migration/m20250424_000001_create_user_table.rs b/rc_database/src/migration/m20250424_000001_create_user_table.rs new file mode 100644 index 0000000..c2000bb --- /dev/null +++ b/rc_database/src/migration/m20250424_000001_create_user_table.rs @@ -0,0 +1,43 @@ +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20250424_000001_create_user_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + // Define how to apply this migration: Create the Users table. + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(crate::schema::user::Entity) + .col( + ColumnDef::new(crate::schema::user::Column::Id) + .unsigned() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(crate::schema::user::Column::CreationTime).big_integer().not_null()) + .col(ColumnDef::new(crate::schema::user::Column::PublicId).string().not_null()) + .col(ColumnDef::new(crate::schema::user::Column::DisplayName).string().not_null()) + .col(ColumnDef::new(crate::schema::user::Column::Password).string().not_null()) + .col(ColumnDef::new(crate::schema::user::Column::Email).string().not_null()) + .col(ColumnDef::new(crate::schema::user::Column::SteamId).string()) + .to_owned(), + ) + .await + } + + // Define how to rollback this migration: Drop the Users table. + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(crate::schema::user::Entity).to_owned()) + .await + } +} diff --git a/rc_database/src/migration/m20250424_000002_create_user_permissions_table.rs b/rc_database/src/migration/m20250424_000002_create_user_permissions_table.rs new file mode 100644 index 0000000..aedb585 --- /dev/null +++ b/rc_database/src/migration/m20250424_000002_create_user_permissions_table.rs @@ -0,0 +1,50 @@ +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20250424_000002_create_user_permissions_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + // Define how to apply this migration: Create the Permissions table. + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(crate::schema::permissions::Entity) + .col( + ColumnDef::new(crate::schema::permissions::Column::Id) + .unsigned() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(crate::schema::permissions::Column::UserId).unsigned().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-permissions-user_id") + .from(crate::schema::permissions::Entity, crate::schema::permissions::Column::UserId) + .to(crate::schema::user::Entity, crate::schema::user::Column::Id), + ) + .col(ColumnDef::new(crate::schema::permissions::Column::Moderator).boolean().not_null()) + //.col(crate::schema::permissions::Column::Moderator.def()) // I wish this worked... + .col(ColumnDef::new(crate::schema::permissions::Column::Administrator).boolean().not_null()) + .col(ColumnDef::new(crate::schema::permissions::Column::Developer).boolean().not_null()) + .col(ColumnDef::new(crate::schema::permissions::Column::Royalty).boolean().not_null()) + .col(ColumnDef::new(crate::schema::permissions::Column::Banned).boolean().not_null()) + .to_owned(), + ) + .await + } + + // Define how to rollback this migration: Drop the Permissions table. + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(crate::schema::permissions::Entity).to_owned()) + .await + } +} diff --git a/rc_database/src/migration/m20250424_000003_create_garage_table.rs b/rc_database/src/migration/m20250424_000003_create_garage_table.rs new file mode 100644 index 0000000..23ed84e --- /dev/null +++ b/rc_database/src/migration/m20250424_000003_create_garage_table.rs @@ -0,0 +1,68 @@ +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20250424_000003_create_garage_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + // Define how to apply this migration: Create the Garages table. + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(crate::schema::garage::Entity) + .col( + ColumnDef::new(crate::schema::garage::Column::Id) + .unsigned() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(crate::schema::garage::Column::UserId).unsigned().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-garage-user_id") + .from(crate::schema::garage::Entity, crate::schema::garage::Column::UserId) + .to(crate::schema::user::Entity, crate::schema::user::Column::Id), + ) + .col(ColumnDef::new(crate::schema::garage::Column::CreationTime).big_integer().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::Slot).unsigned().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::Name).string().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::CrfId).unsigned()) + .col(ColumnDef::new(crate::schema::garage::Column::WasRated).boolean().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::MovementCategories).string().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::Uuid).big_integer().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::ThumbnailVersion).unsigned().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::TotalRobotCpu).unsigned().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::TotalCosmeticCpu).unsigned().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::TotalRobotRanking).unsigned().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::BayCpu).unsigned().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::TutorialRobot).boolean().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::StarterRobotIndex).unsigned()) + .col(ColumnDef::new(crate::schema::garage::Column::ControlType).tiny_unsigned().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::VerticalStrafing).boolean().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::SidewaysDriving).boolean().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::TracksTurnOnSpot).boolean().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::MasteryLevel).unsigned().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::BaySkinId).string().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::WeaponOrder).string().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::RobotData).blob().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::ColourData).blob().not_null()) + .col(ColumnDef::new(crate::schema::garage::Column::Selected).boolean().not_null()) + .to_owned(), + ) + .await + } + + // Define how to rollback this migration: Drop the Garages table. + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(crate::schema::garage::Entity).to_owned()) + .await + } +} diff --git a/rc_database/src/migration/m20250424_000004_create_user_aux_table.rs b/rc_database/src/migration/m20250424_000004_create_user_aux_table.rs new file mode 100644 index 0000000..d06d069 --- /dev/null +++ b/rc_database/src/migration/m20250424_000004_create_user_aux_table.rs @@ -0,0 +1,47 @@ +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20250424_000004_create_user_aux_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + // Define how to apply this migration: Create the User auxiliary table. + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(crate::schema::user_aux::Entity) + .col( + ColumnDef::new(crate::schema::user_aux::Column::Id) + .unsigned() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(crate::schema::user_aux::Column::UserId).unsigned().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-users_aux-user_id") + .from(crate::schema::user_aux::Entity, crate::schema::user_aux::Column::UserId) + .to(crate::schema::user::Entity, crate::schema::user::Column::Id), + ) + .col(ColumnDef::new(crate::schema::user_aux::Column::CreationTime).big_integer().not_null()) + .col(ColumnDef::new(crate::schema::user_aux::Column::Descriptor).string().not_null()) + .col(ColumnDef::new(crate::schema::user_aux::Column::Data).string().not_null()) + .to_owned(), + ) + .await + } + + // Define how to rollback this migration: Drop the User auxiliary table. + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(crate::schema::user_aux::Entity).to_owned()) + .await + } +} diff --git a/rc_database/src/migration/m20250424_000005_create_campaign_tables.rs b/rc_database/src/migration/m20250424_000005_create_campaign_tables.rs new file mode 100644 index 0000000..b6e6f87 --- /dev/null +++ b/rc_database/src/migration/m20250424_000005_create_campaign_tables.rs @@ -0,0 +1,74 @@ +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20250424_000005_create_campaign_tables" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + // Define how to apply this migration: Create the Campaigns table. + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(crate::schema::campaign::Entity) + .col( + ColumnDef::new(crate::schema::campaign::Column::Id) + .unsigned() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(crate::schema::campaign::Column::UserId).unsigned().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-campaign-user_id") + .from(crate::schema::campaign::Entity, crate::schema::campaign::Column::UserId) + .to(crate::schema::user::Entity, crate::schema::user::Column::Id), + ) + .col(ColumnDef::new(crate::schema::campaign::Column::CreationTime).big_integer().not_null()) + .col(ColumnDef::new(crate::schema::campaign::Column::CampaignId).string().not_null()) + .to_owned(), + ) + .await?; + manager + .create_table( + Table::create() + .table(crate::schema::campaign_difficulty_completion::Entity) + .col( + ColumnDef::new(crate::schema::campaign_difficulty_completion::Column::Id) + .unsigned() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(crate::schema::campaign_difficulty_completion::Column::CampaignId).unsigned().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-campaigns_completion-campaign_id") + .from(crate::schema::campaign_difficulty_completion::Entity, crate::schema::campaign_difficulty_completion::Column::CampaignId) + .to(crate::schema::campaign::Entity, crate::schema::campaign::Column::Id), + ) + .col(ColumnDef::new(crate::schema::campaign_difficulty_completion::Column::CreationTime).big_integer().not_null()) + .col(ColumnDef::new(crate::schema::campaign_difficulty_completion::Column::Level).unsigned().not_null()) + .col(ColumnDef::new(crate::schema::campaign_difficulty_completion::Column::Wave).unsigned().not_null()) + .col(ColumnDef::new(crate::schema::campaign_difficulty_completion::Column::Complete).boolean().not_null()) + .to_owned(), + ) + .await + } + + // Define how to rollback this migration: Drop the Campaigns table. + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(crate::schema::campaign::Entity).to_owned()) + .await?; + manager + .drop_table(Table::drop().table(crate::schema::campaign_difficulty_completion::Entity).to_owned()) + .await + } +} diff --git a/rc_database/src/migration/mod.rs b/rc_database/src/migration/mod.rs new file mode 100644 index 0000000..29fb197 --- /dev/null +++ b/rc_database/src/migration/mod.rs @@ -0,0 +1,22 @@ +use sea_orm_migration::{MigratorTrait, MigrationTrait, prelude::async_trait}; + +mod m20250424_000001_create_user_table; +mod m20250424_000002_create_user_permissions_table; +mod m20250424_000003_create_garage_table; +mod m20250424_000004_create_user_aux_table; +mod m20250424_000005_create_campaign_tables; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![ + Box::new(m20250424_000001_create_user_table::Migration), + Box::new(m20250424_000002_create_user_permissions_table::Migration), + Box::new(m20250424_000003_create_garage_table::Migration), + Box::new(m20250424_000004_create_user_aux_table::Migration), + Box::new(m20250424_000005_create_campaign_tables::Migration), + ] + } +} diff --git a/rc_database/src/schema/campaign.rs b/rc_database/src/schema/campaign.rs new file mode 100644 index 0000000..777aaf5 --- /dev/null +++ b/rc_database/src/schema/campaign.rs @@ -0,0 +1,37 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "campaigns")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: u32, + pub user_id: u32, + pub creation_time: i64, // seconds since unix epoch + pub campaign_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id" + )] + User, + #[sea_orm(has_many = "super::campaign_difficulty_completion::Entity")] + CampaignCompletion, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CampaignCompletion.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/rc_database/src/schema/campaign_difficulty_completion.rs b/rc_database/src/schema/campaign_difficulty_completion.rs new file mode 100644 index 0000000..82a9655 --- /dev/null +++ b/rc_database/src/schema/campaign_difficulty_completion.rs @@ -0,0 +1,31 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "campaigns_completion")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: u32, + pub campaign_id: u32, + pub creation_time: i64, // seconds since unix epoch + pub level: u32, + pub wave: u32, + pub complete: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::campaign::Entity", + from = "Column::CampaignId", + to = "super::campaign::Column::Id" + )] + Campaign, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Campaign.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/rc_database/src/schema/common_query.rs b/rc_database/src/schema/common_query.rs new file mode 100644 index 0000000..d426af3 --- /dev/null +++ b/rc_database/src/schema/common_query.rs @@ -0,0 +1,6 @@ +use sea_orm::FromQueryResult; + +#[derive(FromQueryResult)] +pub struct Id { + pub id: u32, +} diff --git a/rc_database/src/schema/garage.rs b/rc_database/src/schema/garage.rs new file mode 100644 index 0000000..14d9794 --- /dev/null +++ b/rc_database/src/schema/garage.rs @@ -0,0 +1,70 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "garages")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: u32, + pub user_id: u32, + pub creation_time: i64, // seconds since unix epoch + pub slot: u32, + pub name: String, + pub crf_id: Option, + pub was_rated: bool, + pub movement_categories: String, // csv? + pub uuid: i64, + pub thumbnail_version: u32, + pub total_robot_cpu: u32, + pub total_cosmetic_cpu: u32, + pub total_robot_ranking: u32, + pub bay_cpu: u32, + pub tutorial_robot: bool, + pub starter_robot_index: Option, + pub control_type: ControlType, + pub vertical_strafing: bool, + pub sideways_driving: bool, + pub tracks_turn_on_spot: bool, + pub mastery_level: u32, + pub bay_skin_id: String, + pub weapon_order: String, // csv? + pub robot_data: Vec, + pub colour_data: Vec, + pub selected: bool, +} + +impl Model { + pub fn cube_count(&self) -> u32 { + if self.robot_data.len() >= 4 { + u32::from_le_bytes([self.robot_data[0], self.robot_data[1], self.robot_data[2], self.robot_data[3]]) + } else { + 0 + } + } +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} + +#[repr(u8)] +#[derive(Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] +#[sea_orm(rs_type = "u8", db_type = "TinyInteger")] +pub enum ControlType { + Camera = 0, + Keyboard = 1, + Count = 2, +} diff --git a/rc_database/src/schema/mod.rs b/rc_database/src/schema/mod.rs new file mode 100644 index 0000000..409f753 --- /dev/null +++ b/rc_database/src/schema/mod.rs @@ -0,0 +1,17 @@ +pub mod user; +pub mod user_aux; +pub mod permissions; +pub mod garage; +pub mod campaign; +pub mod campaign_difficulty_completion; +pub mod common_query; + +pub fn parse_int_csv(s: &str) -> Vec { + s.split(',').filter_map(|i_as_s| { + i_as_s.parse().ok() + }).collect() +} + +pub fn dump_csv(slice: &[T]) -> String { + itertools::Itertools::intersperse(slice.iter().map(|x| x.to_string()), ",".to_owned()).collect() +} diff --git a/rc_database/src/schema/permissions.rs b/rc_database/src/schema/permissions.rs new file mode 100644 index 0000000..0d7941b --- /dev/null +++ b/rc_database/src/schema/permissions.rs @@ -0,0 +1,32 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "permissions")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: u32, + pub user_id: u32, + pub moderator: bool, + pub administrator: bool, + pub developer: bool, + pub royalty: bool, + pub banned: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/rc_database/src/schema/user.rs b/rc_database/src/schema/user.rs new file mode 100644 index 0000000..996f9ba --- /dev/null +++ b/rc_database/src/schema/user.rs @@ -0,0 +1,52 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "users")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: u32, + pub creation_time: i64, // seconds since unix epoch + pub public_id: String, + pub display_name: String, + pub password: String, + pub email: String, + pub steam_id: Option, // u64 +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_one = "super::permissions::Entity")] + Permission, + #[sea_orm(has_many = "super::garage::Entity")] + Garages, + #[sea_orm(has_many = "super::user_aux::Entity")] + Aux, + #[sea_orm(has_many = "super::campaign::Entity")] + Campaigns, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Permission.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Garages.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Aux.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Campaigns.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/rc_database/src/schema/user_aux.rs b/rc_database/src/schema/user_aux.rs new file mode 100644 index 0000000..29c4218 --- /dev/null +++ b/rc_database/src/schema/user_aux.rs @@ -0,0 +1,42 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "users_aux")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: u32, + pub user_id: u32, + pub creation_time: i64, // seconds since unix epoch + pub descriptor: Descriptor, + pub data: String, // usually JSON +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} + +#[derive(Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] +#[sea_orm(rs_type = "String", db_type = "String(StringLen::None)", rename_all = "PascalCase")] +pub enum Descriptor { + UserXP, // u32 + PremiumExpiry, // u64, seconds since Unix epoch + UnlockedParts, // rc_core::persist::user::UnlockedParts + TechPoints, // u32 + UserRank, // u32 + UserFreeCurrency, // u64 + UserPaidCurrency, // u64 +} diff --git a/rc_database/src/wrapper.rs b/rc_database/src/wrapper.rs new file mode 100644 index 0000000..2c3d0d5 --- /dev/null +++ b/rc_database/src/wrapper.rs @@ -0,0 +1,115 @@ +use sea_orm_migration::MigratorTrait; +use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect}; + +pub struct Database { + orm: sea_orm::DatabaseConnection, +} + +impl Database { + pub async fn init(uri: &str) -> Result{ + let db = sea_orm::Database::connect(uri).await?; + //let schema_manager = SchemaManager::new(&db); + super::Migrator::up(&db, None).await?; + Ok(Self { + orm: db, + }) + } + + pub async fn user_by_public_id(&self, public_id: String) -> Result, sea_orm::DbErr> { + crate::schema::user::Entity::find() + .filter(crate::schema::user::Column::PublicId.eq(public_id)) + .one(&self.orm) + .await + } + + pub async fn insert_user(&self, entity: crate::schema::user::ActiveModel) -> Result { + entity.insert(&self.orm).await + } + + pub async fn user_aux_by_user_id(&self, user_id: u32) -> Result, sea_orm::DbErr> { + crate::schema::user_aux::Entity::find() + .filter(crate::schema::user_aux::Column::UserId.eq(user_id)) + .all(&self.orm) + .await + } + + pub async fn user_aux_by_user_id_and_descriptor(&self, user_id: u32, descriptor: crate::schema::user_aux::Descriptor) -> Result, sea_orm::DbErr> { + crate::schema::user_aux::Entity::find() + .filter(crate::schema::user_aux::Column::UserId.eq(user_id)) + .filter(crate::schema::user_aux::Column::Descriptor.eq(descriptor)) + .one(&self.orm) + .await + } + + pub async fn insert_user_aux(&self, entities: Vec) -> Result<(), sea_orm::DbErr> { + crate::schema::user_aux::Entity::insert_many(entities.into_iter()).exec(&self.orm).await?; + Ok(()) + } + + pub async fn perms_by_user_id(&self, user_id: u32) -> Result, sea_orm::DbErr> { + crate::schema::permissions::Entity::find() + .filter(crate::schema::permissions::Column::UserId.eq(user_id)) + .one(&self.orm) + .await + } + + pub async fn insert_perms(&self, entity: crate::schema::permissions::ActiveModel) -> Result { + entity.insert(&self.orm).await + } + + pub async fn garage_selected(&self, user_id: u32) -> Result, sea_orm::DbErr> { + crate::schema::garage::Entity::find() + .filter(crate::schema::garage::Column::UserId.eq(user_id)) + .filter(crate::schema::garage::Column::Selected.eq(true)) + .one(&self.orm) + .await + } + + pub async fn garage_by_user_id_and_slot(&self, user_id: u32, garage_slot: u32) -> Result, sea_orm::DbErr> { + crate::schema::garage::Entity::find() + .filter(crate::schema::garage::Column::UserId.eq(user_id)) + .filter(crate::schema::garage::Column::Slot.eq(garage_slot)) + .one(&self.orm) + .await + } + + pub async fn garages_by_user_id(&self, user_id: u32) -> Result, sea_orm::DbErr> { + crate::schema::garage::Entity::find() + .filter(crate::schema::garage::Column::UserId.eq(user_id)) + .order_by_asc(crate::schema::garage::Column::Slot) + .all(&self.orm) + .await + } + + pub async fn insert_garages(&self, entities: Vec) -> Result<(), sea_orm::DbErr> { + crate::schema::garage::Entity::insert_many(entities.into_iter()).exec(&self.orm).await?; + Ok(()) + } + + pub async fn update_garage(&self, entity: crate::schema::garage::ActiveModel, id: u32) -> Result { + crate::schema::garage::Entity::update(entity) + .filter(crate::schema::garage::Column::Id.eq(id)) + .exec(&self.orm) + .await + } + + pub async fn update_garage_by_user_id_and_slot(&self, mut entity: crate::schema::garage::ActiveModel, user_id: u32, slot: u32) -> Result, sea_orm::DbErr> { + let id_opt = crate::schema::garage::Entity::find() + .select_only() + .column(crate::schema::garage::Column::Id) + .filter(crate::schema::garage::Column::UserId.eq(user_id)) + .filter(crate::schema::garage::Column::Slot.eq(slot)) + .into_model::() + .one(&self.orm) + .await?; + if let Some(id) = id_opt { + entity.id = sea_orm::ActiveValue::Set(id.id); + Ok(Some(crate::schema::garage::Entity::update(entity) + .exec(&self.orm) + .await?)) + } else { + Ok(None) + } + + } +} diff --git a/rc_services_room/Cargo.toml b/rc_services_room/Cargo.toml index ed7942f..ec25406 100644 --- a/rc_services_room/Cargo.toml +++ b/rc_services_room/Cargo.toml @@ -22,3 +22,4 @@ serde_json.workspace = true chrono = "0.4" rc_core = { version = "*", path = "../rc_core" } rand = "0.9" +async-trait.workspace = true diff --git a/rc_services_room/src/main.rs b/rc_services_room/src/main.rs index ba185a9..c9ee279 100644 --- a/rc_services_room/src/main.rs +++ b/rc_services_room/src/main.rs @@ -25,7 +25,7 @@ async fn main() -> std::io::Result<()> { log::debug!("Got cli args {:?}", args); let cubes = rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data"); - let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).expect("Bad user data")); + let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).await.expect("Bad user data")); let init_ctx = std::sync::Arc::new(InitConfig { cubes, users, diff --git a/rc_services_room/src/operations/garage_bay_uuid.rs b/rc_services_room/src/operations/garage_bay_uuid.rs index cd30e70..46b5f3b 100644 --- a/rc_services_room/src/operations/garage_bay_uuid.rs +++ b/rc_services_room/src/operations/garage_bay_uuid.rs @@ -1,13 +1,33 @@ -use polariton_server::operations::SimpleFunc; -use polariton::operation::{ParameterTable, Typed}; +use polariton::operation::{ParameterTable, Typed, OperationResponse}; + +const CODE: u8 = 177; const PARAM_KEY: u8 = 54; -pub(super) fn garage_id_provider() -> SimpleFunc<177, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, user: &crate::UserTy| { - let user_info = user.user()?; - let mut params = params.to_dict(); - params.insert(PARAM_KEY, Typed::Str(user_info.selected_garage_uuid().into())); - Ok(params.into()) - }) +pub(super) fn garage_id_provider() -> GarageIdProvider { + GarageIdProvider +} + +async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + let user_info = user.user()?; + let mut params = params.to_dict(); + params.insert(PARAM_KEY, Typed::Str(user_info.selected_garage().await.0.into())); + Ok(params.into()) +} + +pub struct GarageIdProvider; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for GarageIdProvider { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_handling(params, user).await) + } +} + +impl polariton_server::operations::OperationCode for GarageIdProvider { + fn op_code() -> u8 { + CODE + } } diff --git a/rc_services_room/src/operations/garage_slots.rs b/rc_services_room/src/operations/garage_slots.rs index 1c0427d..a967f54 100644 --- a/rc_services_room/src/operations/garage_slots.rs +++ b/rc_services_room/src/operations/garage_slots.rs @@ -1,18 +1,38 @@ -use polariton_server::operations::SimpleFunc; -use polariton::operation::{ParameterTable, Typed}; +use polariton::operation::{ParameterTable, Typed, OperationResponse}; + +const CODE: u8 = 40; const SLOTS_PARAM_KEY: u8 = 44; const SELECTED_SLOT_PARAM_KEY: u8 = 43; const SLOT_ORDER_PARAM_KEY: u8 = 58; -pub(super) fn garage_slot_provider() -> SimpleFunc<40, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, user: &crate::UserTy| { - let user_info = user.user()?; - let mut params = params.to_dict(); - let all_slots = user_info.all_slots_by_id(); - params.insert(SLOTS_PARAM_KEY, all_slots.slot_info); - params.insert(SELECTED_SLOT_PARAM_KEY, Typed::Int(user_info.selected_garage_slot() as _)); - params.insert(SLOT_ORDER_PARAM_KEY, all_slots.slot_order); - Ok(params.into()) - }) +pub(super) fn garage_slot_provider() -> GarageSlotsProvider { + GarageSlotsProvider +} + +async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + let user_info = user.user()?; + let mut params = params.to_dict(); + let all_slots = user_info.all_slots_by_id().await; + params.insert(SLOTS_PARAM_KEY, all_slots.slot_info); + params.insert(SELECTED_SLOT_PARAM_KEY, Typed::Int(user_info.selected_garage().await.1 as _)); + params.insert(SLOT_ORDER_PARAM_KEY, all_slots.slot_order); + Ok(params.into()) +} + +pub struct GarageSlotsProvider; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for GarageSlotsProvider { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_handling(params, user).await) + } +} + +impl polariton_server::operations::OperationCode for GarageSlotsProvider { + fn op_code() -> u8 { + CODE + } } diff --git a/rc_services_room/src/operations/machine.rs b/rc_services_room/src/operations/machine.rs index 911d6db..b0886e9 100644 --- a/rc_services_room/src/operations/machine.rs +++ b/rc_services_room/src/operations/machine.rs @@ -1,5 +1,7 @@ -use polariton_server::operations::SimpleFunc; -use polariton::operation::{ParameterTable, Typed}; +use polariton::operation::{ParameterTable, Typed, OperationResponse}; + +const CODE_MACHINE_PROVIDER: u8 = 43; +const CODE_MACHINE_SAVER: u8 = 41; const SLOT_PARAM_KEY: u8 = 45; // uint const DATA_PARAM_KEY: u8 = 49; // byte arr @@ -10,25 +12,44 @@ const CONTROL_TYPE_PARAM_KEY: u8 = 59; // int const CONTROL_OPTIONS_PARAM_KEY: u8 = 60; // bool arr const MASTERY_LEVEL_PARAM_KEY: u8 = 18; // int -pub(super) fn garage_machine_provider() -> SimpleFunc<43, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, user: &crate::UserTy| { - let mut params = params.to_dict(); - let user_info = user.user()?; - if let Some(Typed::Int(garage_slot)) = params.get(&SLOT_PARAM_KEY) { - log::debug!("Got machine request for slot {:?}", garage_slot); - let machine = user_info.slot_by_id(*garage_slot)?; - params.insert(DATA_PARAM_KEY, machine.data); - params.insert(CUBES_COUNT_PARAM_KEY, machine.cube_count); - params.insert(WEAPON_ORDER_PARAM_KEY, machine.weapon_order); - params.insert(MOVEMENT_CATEGORIES_PARAM_KEY, machine.movement_categories); - params.insert(CONTROL_TYPE_PARAM_KEY, machine.control_type); - params.insert(CONTROL_OPTIONS_PARAM_KEY, machine.control_options); - params.insert(MASTERY_LEVEL_PARAM_KEY, machine.mastery_level); - } else { - params.insert(SLOT_PARAM_KEY, Typed::Int(user_info.selected_garage_slot() as _)); - } - Ok(params.into()) - }) +pub(super) fn garage_machine_provider() -> MachineProvider { + MachineProvider +} + +async fn do_get(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + let mut params = params.to_dict(); + let user_info = user.user()?; + if let Some(Typed::Int(garage_slot)) = params.get(&SLOT_PARAM_KEY) { + log::debug!("Got machine request for slot {:?}", garage_slot); + let machine = user_info.slot_by_id(*garage_slot).await?; + params.insert(DATA_PARAM_KEY, machine.data); + params.insert(CUBES_COUNT_PARAM_KEY, machine.cube_count); + params.insert(WEAPON_ORDER_PARAM_KEY, machine.weapon_order); + params.insert(MOVEMENT_CATEGORIES_PARAM_KEY, machine.movement_categories); + params.insert(CONTROL_TYPE_PARAM_KEY, machine.control_type); + params.insert(CONTROL_OPTIONS_PARAM_KEY, machine.control_options); + params.insert(MASTERY_LEVEL_PARAM_KEY, machine.mastery_level); + } else { + params.insert(SLOT_PARAM_KEY, Typed::Int(user_info.selected_garage().await.1 as _)); + } + Ok(params.into()) +} + +pub struct MachineProvider; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for MachineProvider { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_get(params, user).await) + } +} + +impl polariton_server::operations::OperationCode for MachineProvider { + fn op_code() -> u8 { + CODE_MACHINE_PROVIDER + } } const ERROR_PARAM_KEY: u8 = 47; // int @@ -38,39 +59,58 @@ const COMPRESSED_COLOUR_DATA_PARAM_KEY: u8 = 33; // byte arr const INVALID_ROBOT_ERR: i16 = 140; -pub(super) fn garage_machine_save_provider() -> SimpleFunc<41, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, user: &crate::UserTy| { - log::debug!("machine save params: {:?}", params); - let mut params = params.to_dict(); - if let Some(Typed::Int(slot_index)) = params.remove(&SLOT_PARAM_KEY) { - if let Some(Typed::Bytes(robot_data)) = params.remove(&COMPRESSED_ROBOT_DATA_PARAM_KEY) { - if let Some(Typed::Bytes(colour_data)) = params.remove(&COMPRESSED_COLOUR_DATA_PARAM_KEY) { - if let Some(Typed::Arr(weapon_order)) = params.remove(&WEAPON_ORDER_PARAM_KEY) { - let weapon_order_filtered: Vec<_> = weapon_order.items.into_iter().filter_map(|ty| if let Typed::Int(i) = ty { Some(i) } else { None }).collect(); - let user_info = user.user()?; - let vehicle_data = rc_core::persist::user::VehicleData { - id: slot_index, - robot_data: robot_data.vec, - colour_data: colour_data.vec, - weapon_order: weapon_order_filtered, - }; - user_info.save_slot(vehicle_data)?; - let mut params_out = std::collections::HashMap::with_capacity(1); - params_out.insert(ERROR_PARAM_KEY, Typed::Int(0)); - return Ok(params_out.into()); - } else { - log::warn!("weapon order is not this type (or does not exist)"); - } +pub(super) fn garage_machine_save_provider() -> MachineSaver { + MachineSaver +} + +async fn do_save(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + log::debug!("machine save params: {:?}", params); + let mut params = params.to_dict(); + if let Some(Typed::Int(slot_index)) = params.remove(&SLOT_PARAM_KEY) { + if let Some(Typed::Bytes(robot_data)) = params.remove(&COMPRESSED_ROBOT_DATA_PARAM_KEY) { + if let Some(Typed::Bytes(colour_data)) = params.remove(&COMPRESSED_COLOUR_DATA_PARAM_KEY) { + if let Some(Typed::Arr(weapon_order)) = params.remove(&WEAPON_ORDER_PARAM_KEY) { + let weapon_order_filtered: Vec<_> = weapon_order.items.into_iter().filter_map(|ty| if let Typed::Int(i) = ty { Some(i) } else { None }).collect(); + let user_info = user.user()?; + let vehicle_data = rc_core::persist::user::VehicleData { + slot: slot_index, + robot_data: robot_data.vec, + colour_data: colour_data.vec, + weapon_order: weapon_order_filtered, + }; + user_info.save_slot(vehicle_data).await?; + let mut params_out = std::collections::HashMap::with_capacity(1); + params_out.insert(ERROR_PARAM_KEY, Typed::Int(0)); + return Ok(params_out.into()); } else { - log::warn!("colour data is not this type (or does not exist)"); + log::warn!("weapon order is not this type (or does not exist)"); } } else { - log::warn!("robot data is not this type (or does not exist)"); + log::warn!("colour data is not this type (or does not exist)"); } } else { - log::warn!("slot is not this type (or does not exist)"); + log::warn!("robot data is not this type (or does not exist)"); } - Err(INVALID_ROBOT_ERR) - }) + } else { + log::warn!("slot is not this type (or does not exist)"); + } + Err(INVALID_ROBOT_ERR) +} + +pub struct MachineSaver; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for MachineSaver { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_save(params, user).await) + } +} + +impl polariton_server::operations::OperationCode for MachineSaver { + fn op_code() -> u8 { + CODE_MACHINE_SAVER + } } diff --git a/rc_services_room/src/operations/machine_colour.rs b/rc_services_room/src/operations/machine_colour.rs index ed20ad8..7d54a53 100644 --- a/rc_services_room/src/operations/machine_colour.rs +++ b/rc_services_room/src/operations/machine_colour.rs @@ -1,22 +1,42 @@ -use polariton_server::operations::SimpleFunc; -use polariton::operation::{ParameterTable, Typed}; +use polariton::operation::{ParameterTable, Typed, OperationResponse}; + +const CODE: u8 = 33; // const USERNAME_PARAM_KEY: u8 = 30; // str const SLOT_PARAM_KEY: u8 = 31; // int const DATA_PARAM_KEY: u8 = 33; // byte arr -pub(super) fn garage_machine_colour_provider() -> SimpleFunc<33, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, user: &crate::UserTy| { - let mut params = params.to_dict(); - let user_info = user.user()?; - if let Some(Typed::Int(garage_slot)) = params.get(&SLOT_PARAM_KEY) { - log::debug!("Got machine colour request for slot {:?}", garage_slot); - let machine = user_info.slot_by_id(*garage_slot)?; - params.insert(DATA_PARAM_KEY, machine.colour_data); - } else { - params.insert(SLOT_PARAM_KEY, Typed::Int(user_info.selected_garage_slot() as _)); - } - - Ok(params.into()) - }) +pub(super) fn garage_machine_colour_provider() -> MachineColour { + MachineColour +} + +async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + let mut params = params.to_dict(); + let user_info = user.user()?; + if let Some(Typed::Int(garage_slot)) = params.get(&SLOT_PARAM_KEY) { + log::debug!("Got machine colour request for slot {:?}", garage_slot); + let machine = user_info.slot_by_id(*garage_slot).await?; + params.insert(DATA_PARAM_KEY, machine.colour_data); + } else { + params.insert(SLOT_PARAM_KEY, Typed::Int(user_info.selected_garage().await.1 as _)); + } + + Ok(params.into()) +} + +pub struct MachineColour; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for MachineColour { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_handling(params, user).await) + } +} + +impl polariton_server::operations::OperationCode for MachineColour { + fn op_code() -> u8 { + CODE + } } diff --git a/rc_services_room/src/operations/more_auth.rs b/rc_services_room/src/operations/more_auth.rs index c3db130..3140dc0 100644 --- a/rc_services_room/src/operations/more_auth.rs +++ b/rc_services_room/src/operations/more_auth.rs @@ -7,18 +7,19 @@ impl MoreLobbyAuth { const AUTH_PAYLOAD_KEY: u8 = 245; } +#[async_trait::async_trait] impl Operation for MoreLobbyAuth { type User = crate::UserTy; - fn handle(&self, params: polariton::operation::ParameterTable, user: &Self::User) -> polariton::operation::OperationResponse { + async fn handle_async(&self, params: polariton::operation::ParameterTable, user: &Self::User) -> polariton::operation::OperationResponse { let params_dict = params.to_dict(); if let Some(Typed::Str(auth_payload)) = params_dict.get(&Self::AUTH_PAYLOAD_KEY) { //let mut write_lock = user.write().unwrap(); - if user.update_with_auth(&auth_payload.string) { + if user.update_with_auth(&auth_payload.string).await { let mut resp_params = std::collections::HashMap::new(); resp_params.insert(Self::AUTH_PAYLOAD_KEY, polariton::operation::Typed::Byte(0)); return polariton::operation::OperationResponse { - code: 230, + code: Self::op_code(), return_code: 0, message: polariton::operation::Typed::Null, params: resp_params.into(), @@ -26,12 +27,16 @@ impl Operation for MoreLobbyAuth { } } polariton::operation::OperationResponse { - code: 230, + code: Self::op_code(), return_code: 120, message: polariton::operation::Typed::Null, params: std::collections::HashMap::new().into(), } } + + fn handle(&self, _params: polariton::operation::ParameterTable, _user: &Self::User) -> polariton::operation::OperationResponse { + unreachable!() + } } impl OperationCode for MoreLobbyAuth { diff --git a/rc_services_room/src/operations/platform_config.rs b/rc_services_room/src/operations/platform_config.rs index f93a3fe..a715fb4 100644 --- a/rc_services_room/src/operations/platform_config.rs +++ b/rc_services_room/src/operations/platform_config.rs @@ -21,7 +21,7 @@ pub(super) fn platform_config_provider() -> SimpleFunc<165, crate::UserTy, impl (Typed::Str("UseDecimalSystem".into()), Typed::Bool(false.into())), (Typed::Str("FeedbackURL".into()), Typed::Str("https://mstdn.ca/@ngram".into())), (Typed::Str("SupportURL".into()), Typed::Str("https://git.ngni.us/OpenJam/servers".into())), - (Typed::Str("WikiURL".into()), Typed::Str("https://docs.rs/libfj/latest/libfj/".into())), + (Typed::Str("WikiURL".into()), Typed::Str("https://git.ngram.ca/OpenJam/servers/wiki".into())), ].into(), })); Ok(params.into()) diff --git a/rc_services_room/src/operations/player_robot_rank.rs b/rc_services_room/src/operations/player_robot_rank.rs index 56afe05..6e10bf7 100644 --- a/rc_services_room/src/operations/player_robot_rank.rs +++ b/rc_services_room/src/operations/player_robot_rank.rs @@ -1,22 +1,40 @@ -use polariton_server::operations::SimpleFunc; -use polariton::operation::{ParameterTable, Typed}; +use polariton::operation::{ParameterTable, Typed, OperationResponse}; const USERNAME_PARAM_KEY: u8 = 30; // in; str const RANK_PARAM_KEY: u8 = 84; // out; int const CPU_PARAM_KEY: u8 = 177; // out; int const COSMETIC_CPU_PARAM_KEY: u8 = 176; // out; int -pub(super) fn player_robot_rank_provider() -> SimpleFunc<79, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, user: &crate::UserTy| { - let user = user.user()?; - let mut params = params.to_dict(); - if let Some(Typed::Str(username)) = params.get(&USERNAME_PARAM_KEY) { - log::debug!("Get robot rank for user {}", username.string); - } - let robot = user.slot_by_id(user.selected_garage_slot() as i32)?; - params.insert(RANK_PARAM_KEY, robot.robot_rank); - params.insert(CPU_PARAM_KEY, robot.cpu); - params.insert(COSMETIC_CPU_PARAM_KEY, robot.cosmetic_cpu); - Ok(params.into()) - }) +pub(super) fn player_robot_rank_provider() -> PlayerRank { + PlayerRank +} + +async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + let user = user.user()?; + let mut params = params.to_dict(); + if let Some(Typed::Str(username)) = params.get(&USERNAME_PARAM_KEY) { + log::debug!("Get robot rank for user {}", username.string); + } + let robot = user.slot_by_id(user.selected_garage().await.1 as i32).await?; + params.insert(RANK_PARAM_KEY, robot.robot_rank); + params.insert(CPU_PARAM_KEY, robot.cpu); + params.insert(COSMETIC_CPU_PARAM_KEY, robot.cosmetic_cpu); + Ok(params.into()) +} + +pub struct PlayerRank; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for PlayerRank { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::<79, ()>(do_handling(params, user).await) + } +} + +impl polariton_server::operations::OperationCode for PlayerRank { + fn op_code() -> u8 { + 79 + } } diff --git a/rc_singleplayer_room/Cargo.toml b/rc_singleplayer_room/Cargo.toml index 1eee753..bd780a9 100644 --- a/rc_singleplayer_room/Cargo.toml +++ b/rc_singleplayer_room/Cargo.toml @@ -16,3 +16,4 @@ polariton.workspace = true polariton_auth = { version = "*", path = "../polariton_auth" } polariton_server.workspace = true rc_core = { version = "*", path = "../rc_core" } +async-trait.workspace = true diff --git a/rc_singleplayer_room/src/main.rs b/rc_singleplayer_room/src/main.rs index fbf44d9..77f39ee 100644 --- a/rc_singleplayer_room/src/main.rs +++ b/rc_singleplayer_room/src/main.rs @@ -19,7 +19,7 @@ async fn main() -> std::io::Result<()> { log::debug!("Got cli args {:?}", args); let cubes = rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data"); - let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).expect("Bad user data")); + let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).await.expect("Bad user data")); let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler(), polariton_server::events::EventsHandler::new())); diff --git a/rc_singleplayer_room/src/operations/load_ai_robots.rs b/rc_singleplayer_room/src/operations/load_ai_robots.rs index 6e387ba..dcba960 100644 --- a/rc_singleplayer_room/src/operations/load_ai_robots.rs +++ b/rc_singleplayer_room/src/operations/load_ai_robots.rs @@ -1,38 +1,58 @@ -use polariton_server::operations::SimpleFunc; -use polariton::operation::{ParameterTable, Typed}; +use polariton::operation::{ParameterTable, Typed, OperationResponse}; + +const CODE: u8 = 1; const PARAM_KEY: u8 = 8; -pub(super) fn tdm_machines_provider() -> SimpleFunc<1, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, user: &crate::UserTy| { - let ulock = user.user()?; - let mut params = params.to_dict(); - params.insert(PARAM_KEY, ulock.singleplayer_robots()?); - let event_tx = user.event_sender(); - let user_bot_data = ulock.slot_by_id(ulock.selected_garage_slot() as _)?; - tokio::spawn(async move { - tokio::time::sleep(std::time::Duration::from_secs(20)).await; - log::debug!("Sending singleplayer event"); - let mut spawn_params = std::collections::HashMap::with_capacity(4); - spawn_params.insert(2 /* robot GUID */, Typed::Str("1337_1337".into())); - spawn_params.insert(3 /* machine model */, user_bot_data.data); - spawn_params.insert(4 /* robot name */, Typed::Str("RE_robot_spawn_name0".into())); // FIXME - spawn_params.insert(7 /* color model */, user_bot_data.colour_data); - event_tx.send(polariton_server::ToSend::Data { - data: polariton::packet::Data::Event(polariton::operation::Event { code: 3, params: spawn_params.into() }), - encrypt: true, - channel: 0, - reliable: true, - }).unwrap(); - /*let mut update_params = std::collections::HashMap::with_capacity(1); - update_params.insert(6 /* ??? */, Typed::Int(5)); - event_tx.send(polariton_server::ToSend::Data { - data: polariton::packet::Data::Event(polariton::operation::Event { code: 5, params: update_params.into() }), - encrypt: true, - channel: 0, - reliable: true, - }).unwrap();*/ - }); - Ok(params.into()) - }) +pub(super) fn tdm_machines_provider() -> AiRobots { + AiRobots +} + +async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy) -> Result, i16> { + let ulock = user.user()?; + let mut params = params.to_dict(); + params.insert(PARAM_KEY, ulock.singleplayer_robots().await?); + let event_tx = user.event_sender(); + let user_bot_data = ulock.slot_by_id(ulock.selected_garage().await.1 as _).await?; + tokio::spawn(async move { + tokio::time::sleep(std::time::Duration::from_secs(20)).await; + log::debug!("Sending singleplayer event"); + let mut spawn_params = std::collections::HashMap::with_capacity(4); + spawn_params.insert(2 /* robot GUID */, Typed::Str("1337_1337".into())); + spawn_params.insert(3 /* machine model */, user_bot_data.data); + spawn_params.insert(4 /* robot name */, Typed::Str("RE_robot_spawn_name0".into())); // FIXME + spawn_params.insert(7 /* color model */, user_bot_data.colour_data); + event_tx.send(polariton_server::ToSend::Data { + data: polariton::packet::Data::Event(polariton::operation::Event { code: 3, params: spawn_params.into() }), + encrypt: true, + channel: 0, + reliable: true, + }).unwrap(); + /*let mut update_params = std::collections::HashMap::with_capacity(1); + update_params.insert(6 /* ??? */, Typed::Int(5)); + event_tx.send(polariton_server::ToSend::Data { + data: polariton::packet::Data::Event(polariton::operation::Event { code: 5, params: update_params.into() }), + encrypt: true, + channel: 0, + reliable: true, + }).unwrap();*/ + }); + Ok(params.into()) +} + +pub struct AiRobots; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for AiRobots { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_handling(params, user).await) + } +} + +impl polariton_server::operations::OperationCode for AiRobots { + fn op_code() -> u8 { + CODE + } } diff --git a/rc_singleplayer_room/src/operations/more_auth.rs b/rc_singleplayer_room/src/operations/more_auth.rs index c3db130..6b0f69f 100644 --- a/rc_singleplayer_room/src/operations/more_auth.rs +++ b/rc_singleplayer_room/src/operations/more_auth.rs @@ -7,18 +7,19 @@ impl MoreLobbyAuth { const AUTH_PAYLOAD_KEY: u8 = 245; } +#[async_trait::async_trait] impl Operation for MoreLobbyAuth { type User = crate::UserTy; - fn handle(&self, params: polariton::operation::ParameterTable, user: &Self::User) -> polariton::operation::OperationResponse { + async fn handle_async(&self, params: polariton::operation::ParameterTable, user: &Self::User) -> polariton::operation::OperationResponse { let params_dict = params.to_dict(); if let Some(Typed::Str(auth_payload)) = params_dict.get(&Self::AUTH_PAYLOAD_KEY) { //let mut write_lock = user.write().unwrap(); - if user.update_with_auth(&auth_payload.string) { + if user.update_with_auth(&auth_payload.string).await { let mut resp_params = std::collections::HashMap::new(); resp_params.insert(Self::AUTH_PAYLOAD_KEY, polariton::operation::Typed::Byte(0)); return polariton::operation::OperationResponse { - code: 230, + code: Self::op_code(), return_code: 0, message: polariton::operation::Typed::Null, params: resp_params.into(), @@ -26,7 +27,7 @@ impl Operation for MoreLobbyAuth { } } polariton::operation::OperationResponse { - code: 230, + code: Self::op_code(), return_code: 120, message: polariton::operation::Typed::Null, params: std::collections::HashMap::new().into(), diff --git a/rc_social_room/Cargo.toml b/rc_social_room/Cargo.toml index 5118239..9d8459d 100644 --- a/rc_social_room/Cargo.toml +++ b/rc_social_room/Cargo.toml @@ -16,3 +16,4 @@ polariton.workspace = true polariton_auth = { version = "*", path = "../polariton_auth" } polariton_server.workspace = true rc_core = { version = "*", path = "../rc_core" } +async-trait.workspace = true diff --git a/rc_social_room/src/main.rs b/rc_social_room/src/main.rs index f852303..62be28f 100644 --- a/rc_social_room/src/main.rs +++ b/rc_social_room/src/main.rs @@ -19,7 +19,7 @@ async fn main() -> std::io::Result<()> { log::debug!("Got cli args {:?}", args); let cubes = rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data"); - let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).expect("Bad user data")); + let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).await.expect("Bad user data")); let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler(), polariton_server::events::EventsHandler::new())); diff --git a/rc_social_room/src/operations/more_auth.rs b/rc_social_room/src/operations/more_auth.rs index fcb6985..47d515e 100644 --- a/rc_social_room/src/operations/more_auth.rs +++ b/rc_social_room/src/operations/more_auth.rs @@ -7,17 +7,18 @@ impl MoreLobbyAuth { const AUTH_PAYLOAD_KEY: u8 = 245; } +#[async_trait::async_trait] impl Operation for MoreLobbyAuth { type User = crate::UserTy; - fn handle(&self, params: polariton::operation::ParameterTable, user: &Self::User) -> polariton::operation::OperationResponse { + async fn handle_async(&self, params: polariton::operation::ParameterTable, user: &Self::User) -> polariton::operation::OperationResponse { let params_dict = params.to_dict(); if let Some(Typed::Str(auth_payload)) = params_dict.get(&Self::AUTH_PAYLOAD_KEY) { - if user.update_with_auth(&auth_payload.string) { + if user.update_with_auth(&auth_payload.string).await { let mut resp_params = std::collections::HashMap::with_capacity(1); resp_params.insert(Self::AUTH_PAYLOAD_KEY, polariton::operation::Typed::Byte(0)); return polariton::operation::OperationResponse { - code: 230, + code: Self::op_code(), return_code: 0, message: polariton::operation::Typed::Null, params: resp_params.into(), @@ -25,7 +26,7 @@ impl Operation for MoreLobbyAuth { } } polariton::operation::OperationResponse { - code: 230, + code: Self::op_code(), return_code: 120, message: polariton::operation::Typed::Null, params: std::collections::HashMap::new().into(),