diff --git a/Cargo.lock b/Cargo.lock index 5b56ad3..f134f13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,15 +28,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" - -[[package]] -name = "ahash" -version = "0.6.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "796540673305a66d127804eef19ad696f1f204b8c1025aaca4958c17eab32877" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ "getrandom 0.2.6", "once_cell", @@ -309,15 +303,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.2" @@ -370,12 +355,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - [[package]] name = "bytes" version = "1.1.0" @@ -388,28 +367,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" -[[package]] -name = "cargo-platform" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f" -dependencies = [ - "cargo-platform", - "semver 0.11.0", - "semver-parser", - "serde", - "serde_json", -] - [[package]] name = "cc" version = "1.0.73" @@ -514,15 +471,20 @@ dependencies = [ ] [[package]] -name = "crossbeam-channel" -version = "0.5.4" +name = "crc" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" +checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" dependencies = [ - "cfg-if", - "crossbeam-utils", + "crc-catalog", ] +[[package]] +name = "crc-catalog" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" + [[package]] name = "crossbeam-queue" version = "0.3.5" @@ -553,16 +515,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "ctor" version = "0.1.22" @@ -588,22 +540,33 @@ dependencies = [ [[package]] name = "digest" -version = "0.9.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "generic-array", + "block-buffer", + "crypto-common", + "subtle", ] [[package]] -name = "digest" -version = "0.10.3" +name = "dirs" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ - "block-buffer 0.10.2", - "crypto-common", - "subtle", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", ] [[package]] @@ -630,7 +593,7 @@ dependencies = [ "bcrypt", "byteorder", "chrono", - "crc", + "crc 1.8.1", "derive_more", "enum-utils", "fern", @@ -789,6 +752,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.21" @@ -901,28 +875,22 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -dependencies = [ - "ahash 0.4.7", -] - [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] [[package]] name = "hashlink" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8" +checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" dependencies = [ - "hashbrown 0.9.1", + "hashbrown", ] [[package]] @@ -934,6 +902,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -950,13 +927,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "hmac" -version = "0.10.1" +name = "hkdf" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" dependencies = [ - "crypto-mac", - "digest 0.9.0", + "hmac", ] [[package]] @@ -965,7 +941,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest", ] [[package]] @@ -986,7 +962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg 1.1.0", - "hashbrown 0.11.2", + "hashbrown", ] [[package]] @@ -1007,12 +983,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.1" @@ -1091,24 +1061,13 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" -[[package]] -name = "md-5" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "md-5" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" dependencies = [ - "digest 0.10.3", + "digest", ] [[package]] @@ -1307,19 +1266,16 @@ dependencies = [ ] [[package]] -name = "percent-encoding" -version = "2.1.0" +name = "paste" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" [[package]] -name = "pest" -version = "2.1.3" +name = "percent-encoding" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "phf" @@ -1376,7 +1332,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb76d6535496f633fa799bb872ffb4790e9cbdedda9d35564ca0252f930c0dd5" dependencies = [ - "bytes 1.1.0", + "bytes", "fallible-iterator", "futures", "log", @@ -1392,13 +1348,13 @@ checksum = "79ec03bce71f18b4a27c4c64c6ba2ddf74686d69b91d8714fb32ead3adaed713" dependencies = [ "base64", "byteorder", - "bytes 1.1.0", + "bytes", "fallible-iterator", - "hmac 0.12.1", - "md-5 0.10.1", + "hmac", + "md-5", "memchr", "rand 0.8.5", - "sha2 0.10.2", + "sha2", "stringprep", ] @@ -1408,7 +1364,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04619f94ba0cc80999f4fc7073607cb825bc739a883cb6d20900fc5e009d6b0d" dependencies = [ - "bytes 1.1.0", + "bytes", "fallible-iterator", "postgres-protocol", ] @@ -1642,6 +1598,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.6", + "redox_syscall", + "thiserror", +] + [[package]] name = "refinery" version = "0.5.0" @@ -1735,7 +1702,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.7", + "semver", ] [[package]] @@ -1792,31 +1759,12 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", - "serde", -] - [[package]] name = "semver" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "serde" version = "1.0.136" @@ -1854,36 +1802,20 @@ version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ - "indexmap", - "itoa 1.0.1", + "itoa", "ryu", "serde", ] [[package]] name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.9.9" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ - "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "digest", ] [[package]] @@ -1894,7 +1826,7 @@ checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest", ] [[package]] @@ -1957,9 +1889,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.4.2" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1a98f9bf17b690f026b6fec565293a995b46dfbd6293debcb654dcffd2d1b34" +checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b" dependencies = [ "sqlx-core", "sqlx-macros", @@ -1967,41 +1899,44 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.4.2" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36bb6a2ca3345a86493bc3b71eabc2c6c16a8bb1aa476cf5303bee27f67627d7" +checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" dependencies = [ - "ahash 0.6.3", + "ahash", "atoi", "base64", "bitflags", "byteorder", - "bytes 0.5.6", + "bytes", "chrono", - "crc", - "crossbeam-channel", + "crc 2.1.0", "crossbeam-queue", - "crossbeam-utils", + "dirs", "either", + "event-listener", "futures-channel", "futures-core", + "futures-intrusive", "futures-util", "hashlink", "hex", - "hmac 0.10.1", - "itoa 0.4.8", + "hkdf", + "hmac", + "indexmap", + "itoa", "libc", "log", - "md-5 0.9.1", + "md-5", "memchr", "once_cell", - "parking_lot", + "paste", "percent-encoding", - "rand 0.7.3", + "rand 0.8.5", "serde", "serde_json", "sha-1", - "sha2 0.9.9", + "sha2", "smallvec", "sqlformat", "sqlx-rt", @@ -2013,20 +1948,18 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.4.2" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5ada8b3b565331275ce913368565a273a74faf2a34da58c4dc010ce3286844" +checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1" dependencies = [ - "cargo_metadata", "dotenv", "either", - "futures", - "heck", - "lazy_static", + "heck 0.4.0", + "once_cell", "proc-macro2", "quote", "serde_json", - "sha2 0.9.9", + "sha2", "sqlx-core", "sqlx-rt", "syn", @@ -2035,9 +1968,9 @@ dependencies = [ [[package]] name = "sqlx-rt" -version = "0.2.0" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63fc5454c9dd7aaea3a0eeeb65ca40d06d0d8e7413a8184f7c3a3ffa5056190b" +checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae" dependencies = [ "async-native-tls", "async-std", @@ -2066,7 +1999,7 @@ version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e61bb0be289045cb80bfce000512e32d09f8337e54c186725da381377ad1f8d5" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2", "quote", "syn", @@ -2154,7 +2087,7 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f48b6d60512a392e34dbf7fd456249fd2de3c83669ab642e021903f4015185b" dependencies = [ - "bytes 1.1.0", + "bytes", "libc", "memchr", "mio", @@ -2172,7 +2105,7 @@ checksum = "4b6c8b33df661b548dcd8f9bf87debb8c56c05657ed291122e1188698c2ece95" dependencies = [ "async-trait", "byteorder", - "bytes 1.1.0", + "bytes", "fallible-iterator", "futures", "log", @@ -2193,7 +2126,7 @@ version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" dependencies = [ - "bytes 1.1.0", + "bytes", "futures-core", "futures-sink", "log", @@ -2216,12 +2149,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - [[package]] name = "unicode-bidi" version = "0.3.8" diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index e510da3..3eb948c 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -20,11 +20,11 @@ pub trait EntityGateway: Send + Sync + Clone { unimplemented!() } - async fn get_user_by_id(&self, _id: UserAccountId) -> Result { + async fn get_user_by_id(&mut self, _id: UserAccountId) -> Result { unimplemented!(); } - async fn get_user_by_name(&self, _username: String) -> Result { + async fn get_user_by_name(&mut self, _username: String) -> Result { unimplemented!(); } @@ -36,7 +36,7 @@ pub trait EntityGateway: Send + Sync + Clone { unimplemented!(); } - async fn get_user_settings_by_user(&self, _user: &UserAccountEntity) -> Result { + async fn get_user_settings_by_user(&mut self, _user: &UserAccountEntity) -> Result { unimplemented!(); } @@ -49,7 +49,7 @@ pub trait EntityGateway: Send + Sync + Clone { } // TODO: just make this a vec sorted by slot order? - async fn get_characters_by_user(&self, _user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { + async fn get_characters_by_user(&mut self, _user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { unimplemented!(); } @@ -57,7 +57,7 @@ pub trait EntityGateway: Send + Sync + Clone { unimplemented!(); } - async fn get_guild_card_data_by_user(&self, _user: &UserAccountEntity) -> Result { + async fn get_guild_card_data_by_user(&mut self, _user: &UserAccountEntity) -> Result { unimplemented!(); } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 7d616dc..0234ddd 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -140,12 +140,12 @@ impl EntityGateway for InMemoryGateway { Ok(user) } - async fn get_user_by_id(&self, id: UserAccountId) -> Result { + async fn get_user_by_id(&mut self, id: UserAccountId) -> Result { let users = self.users.lock().unwrap(); users.get(&id).cloned().ok_or(GatewayError::Error) } - async fn get_user_by_name(&self, username: String) -> Result { + async fn get_user_by_name(&mut self, username: String) -> Result { let users = self.users.lock().unwrap(); users .iter() @@ -175,7 +175,7 @@ impl EntityGateway for InMemoryGateway { Ok(new_settings) } - async fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Result { + async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result { let user_settings = self.user_settings.lock().unwrap(); user_settings .iter() @@ -184,7 +184,7 @@ impl EntityGateway for InMemoryGateway { .ok_or(GatewayError::Error) } - async fn get_characters_by_user(&self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { + async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { let characters = self.characters.lock().unwrap(); const NONE: Option = None; let mut chars = [NONE; 4]; @@ -231,7 +231,7 @@ impl EntityGateway for InMemoryGateway { Ok(()) } - async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result { + async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result { Ok(GuildCardDataEntity::new(user.id)) } diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 39be9ac..10961a5 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -42,241 +42,544 @@ impl PostgresGateway { pool, } } +} + - async fn apply_item_modifications(&self, item: ItemEntity) -> ItemEntity { - let ItemEntity {id, item} = item; +async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntity) -> ItemEntity +{ + let ItemEntity {id, item} = item; - let item = match item { - ItemDetail::Weapon(mut weapon) => { - let q = r#"select weapon, modifier + let item = match item { + ItemDetail::Weapon(mut weapon) => { + let q = r#"select weapon, modifier from weapon_modifier where weapon = $1 order by created_at"#; - let weapon_modifiers = sqlx::query_as::<_, PgWeaponModifier>(q) - .bind(id.0 as i32) - .fetch(&self.pool); + let weapon_modifiers = sqlx::query_as::<_, PgWeaponModifier>(q) + .bind(id.0 as i32) + .fetch(conn); - weapon_modifiers.for_each(|modifier| { - if let Ok(modifier) = modifier { - weapon.apply_modifier(&modifier.modifier); - } - }).await; + weapon_modifiers.for_each(|modifier| { + if let Ok(modifier) = modifier { + weapon.apply_modifier(&modifier.modifier); + } + }).await; - ItemDetail::Weapon(weapon) - }, - ItemDetail::Mag(mut mag) => { - let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell + ItemDetail::Weapon(weapon) + }, + ItemDetail::Mag(mut mag) => { + let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell from mag_modifier left join item on item.id = cast (modifier ->> 'FeedMag' as integer) left join item as item2 on item2.id = cast (modifier ->> 'MagCell' as integer) where mag = $1 order by created_at"#; - let mag_modifiers = sqlx::query_as::<_, PgMagModifierWithParameters>(q) - .bind(id.0 as i32) - .fetch(&self.pool); - - mag_modifiers.for_each(|modifier| { - let PgMagModifierWithParameters {modifier, feed, cell, ..} = modifier.unwrap(); - let modifier: mag::MagModifier = modifier.0.into(); - match modifier { - mag::MagModifier::FeedMag{..} => { - mag.feed(feed.unwrap().tool) - }, - mag::MagModifier::BankMag => { - mag.bank() - }, - mag::MagModifier::MagCell(_) => { - mag.apply_mag_cell(mag::MagCell::try_from(Into::::into(cell.unwrap().0).tool).unwrap()) - }, - mag::MagModifier::OwnerChange(class, section_id) => { - mag.change_owner(class, section_id) - }, - } - }).await; - - ItemDetail::Mag(mag) + let mag_modifiers = sqlx::query_as::<_, PgMagModifierWithParameters>(q) + .bind(id.0 as i32) + .fetch(conn); + + mag_modifiers.for_each(|modifier| { + let PgMagModifierWithParameters {modifier, feed, cell, ..} = modifier.unwrap(); + let modifier: mag::MagModifier = modifier.0.into(); + match modifier { + mag::MagModifier::FeedMag{..} => { + mag.feed(feed.unwrap().tool) + }, + mag::MagModifier::BankMag => { + mag.bank() + }, + mag::MagModifier::MagCell(_) => { + mag.apply_mag_cell(mag::MagCell::try_from(Into::::into(cell.unwrap().0).tool).unwrap()) + }, + mag::MagModifier::OwnerChange(class, section_id) => { + mag.change_owner(class, section_id) + }, + } + }).await; + + ItemDetail::Mag(mag) + }, + item => item + }; + + ItemEntity { + id, + item, + } +} + +async fn create_user(conn: &mut sqlx::PgConnection, user: NewUserAccountEntity) -> Result +{ + let new_user = sqlx::query_as::<_, PgUserAccount>("insert into user_accounts (email, username, password, activated) values ($1, $2, $3, $4) returning *;") + .bind(user.email) + .bind(user.username) + .bind(user.password) + .bind(user.activated) + .fetch_one(conn).await?; + Ok(new_user.into()) +} + +async fn get_user_by_id(conn: &mut sqlx::PgConnection, id: UserAccountId) -> Result +{ + let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where id = $1") + .bind(id.0) + .fetch_one(conn).await?; + Ok(user.into()) +} + +async fn get_user_by_name(conn: &mut sqlx::PgConnection, username: String) -> Result +{ + let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where username = $1") + .bind(username) + .fetch_one(conn).await?; + Ok(user.into()) +} + + +async fn save_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<(), GatewayError> +{ + sqlx::query("UPDATE user_accounts set username=$1, password=$2, banned=$3, muted=$4, flags=$5 where id=$6") + .bind(&user.username) + .bind(&user.password) + .bind(&user.banned_until) + .bind(&user.muted_until) + .bind(&user.flags) + .bind(&user.id.0) + .execute(conn).await?; + Ok(()) +} + +async fn create_user_settings(conn: &mut sqlx::PgConnection, settings: NewUserSettingsEntity) -> Result +{ + let new_settings = sqlx::query_as::<_, PgUserSettings>("insert into user_settings (user_account, blocked_users, key_config, joystick_config, option_flags, shortcuts, symbol_chats, team_name) + values ($1, $2, $3, $4, $5, $6, $7, $8) returning *;") + .bind(settings.user_id.0) + .bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) + .bind(settings.settings.keyboard_config.to_vec()) + .bind(settings.settings.gamepad_config.to_vec()) + .bind(settings.settings.option_flags as i32) + .bind(settings.settings.shortcuts.to_vec()) + .bind(settings.settings.symbol_chats.to_vec()) + .bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) + .fetch_one(conn).await?; + Ok(new_settings.into()) +} + +async fn get_user_settings_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result +{ + let settings = sqlx::query_as::<_, PgUserSettings>("select * from user_settings where user_account = $1") + .bind(user.id.0) + .fetch_one(conn).await?; + Ok(settings.into()) +} + +async fn save_user_settings(conn: &mut sqlx::PgConnection, settings: &UserSettingsEntity) -> Result<(), GatewayError> +{ + sqlx::query("update user_settings set blocked_users=$1, key_config=$2, joystick_config=$3, option_flags=$4, shortcuts=$5, symbol_chats=$6, team_name=$7 where id=$8") + .bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) + .bind(&settings.settings.keyboard_config.to_vec()) + .bind(&settings.settings.gamepad_config.to_vec()) + .bind(&settings.settings.option_flags) + .bind(&settings.settings.shortcuts.to_vec()) + .bind(&settings.settings.symbol_chats.to_vec()) + .bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) + .bind(&settings.id.0) + .fetch_one(conn).await?; + Ok(()) +} + +async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntity) -> Result +{ + let q = r#"insert into player_character + (user_account, slot, name, exp, class, section_id, costume, skin, face, head, hair, hair_r, hair_g, hair_b, prop_x, prop_y, techs, + config, infoboard, guildcard, power, mind, def, evade, luck, hp, tp, tech_menu, option_flags) + values + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31) + returning *;"#; + let character = sqlx::query_as::<_, PgCharacter>(q) + .bind(char.user_id.0) + .bind(char.slot as i16) + .bind(char.name) + .bind(char.exp as i32) + .bind(char.char_class.to_string()) + .bind(char.section_id.to_string()) + .bind(char.appearance.costume as i16) + .bind(char.appearance.skin as i16) + .bind(char.appearance.face as i16) + .bind(char.appearance.head as i16) + .bind(char.appearance.hair as i16) + .bind(char.appearance.hair_r as i16) + .bind(char.appearance.hair_g as i16) + .bind(char.appearance.hair_b as i16) + .bind(char.appearance.prop_x) + .bind(char.appearance.prop_y) + .bind(&char.techs.as_bytes().to_vec()) + .bind(&char.config.as_bytes().to_vec()) + .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) + .bind(char.guildcard.description) + .bind(char.materials.power as i16) + .bind(char.materials.mind as i16) + .bind(char.materials.def as i16) + .bind(char.materials.evade as i16) + .bind(char.materials.luck as i16) + .bind(char.materials.hp as i16) + .bind(char.materials.tp as i16) + .bind(char.tech_menu.tech_menu.to_vec()) + .bind(char.option_flags as i32) + .fetch_one(conn).await?; + + Ok(character.into()) +} + +async fn get_characters_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> +{ + let mut stream = sqlx::query_as::<_, PgCharacter>("select * from player_character where user_account = $1 and slot < 4 order by slot") + .bind(user.id.0) + .fetch(conn); + const NONE: Option = None; + let mut result = [NONE; 4]; + while let Some(character) = stream.try_next().await? { + let index = character.slot as usize; + result[index] = Some(character.into()) + } + + Ok(result) +} + +async fn save_character(conn: &mut sqlx::PgConnection, char: &CharacterEntity) -> Result<(), GatewayError> +{ + let q = r#"update player_character set + user_account=$1, slot=$2, name=$3, exp=$4, class=$5, section_id=$6, costume=$7, skin=$8, face=$9, head=$10, hair=$11, hair_r=$12, + hair_g=$13, hair_b=$14, prop_x=$15, prop_y=$16, techs=$17, config=$18, infoboard=$19, guildcard=$20, power=$21, mind=$22, def=$23, + evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29 + where id=$32;"#; + sqlx::query(q) + .bind(char.user_id.0) + .bind(char.slot as i16) + .bind(&char.name) + .bind(char.exp as i32) + .bind(char.char_class.to_string()) + .bind(char.section_id.to_string()) + .bind(char.appearance.costume as i16) + .bind(char.appearance.skin as i16) + .bind(char.appearance.face as i16) + .bind(char.appearance.head as i16) + .bind(char.appearance.hair as i16) + .bind(char.appearance.hair_r as i16) + .bind(char.appearance.hair_g as i16) + .bind(char.appearance.hair_b as i16) + .bind(char.appearance.prop_x) + .bind(char.appearance.prop_y) + .bind(&char.techs.as_bytes().to_vec()) + .bind(&char.config.as_bytes().to_vec()) + .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) + .bind(&char.guildcard.description) + .bind(char.materials.power as i16) + .bind(char.materials.mind as i16) + .bind(char.materials.def as i16) + .bind(char.materials.evade as i16) + .bind(char.materials.luck as i16) + .bind(char.materials.hp as i16) + .bind(char.materials.tp as i16) + .bind(char.tech_menu.tech_menu.to_vec()) + .bind(char.option_flags as i32) + .bind(char.id.0 as i32) + .execute(conn).await?; + Ok(()) +} + +async fn create_item(conn: &mut sqlx::PgConnection, item: NewItemEntity) -> Result +{ + let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;") + .bind(sqlx::types::Json(PgItemDetail::from(item.item))) + .fetch_one(conn).await?; + + Ok(ItemEntity { + id: ItemEntityId(new_item.id as u32), + item: new_item.item.0.into(), + }) +} + +async fn add_item_note(conn: &mut sqlx::PgConnection, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> +{ + sqlx::query("insert into item_note(item, note) values ($1, $2)") + .bind(item_id.0) + .bind(sqlx::types::Json(PgItemNoteDetail::from(item_note))) + .execute(conn).await?; + Ok(()) +} + +async fn feed_mag(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> +{ + sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") + .bind(mag_item_id.0) + .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::FeedMag{food: *tool_item_id}))) + .execute(conn).await?; + Ok(()) +} + +async fn change_mag_owner(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> +{ + sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") + .bind(mag_item_id.0) + .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::OwnerChange(character.char_class, character.section_id)))) + .execute(conn).await?; + Ok(()) +} + +async fn use_mag_cell(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> +{ + sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") + .bind(mag_item_id.0) + .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::MagCell(*mag_cell_id)))) + .execute(conn).await?; + Ok(()) +} + +async fn add_weapon_modifier(conn: &mut sqlx::PgConnection, item_id: &ItemEntityId, modifier: weapon::WeaponModifier) -> Result<(), GatewayError> +{ + sqlx::query("insert into weapon_modifier (weapon, modifier) values ($1, $2);") + .bind(item_id.0) + .bind(sqlx::types::Json(modifier)) + .execute(conn).await?; + Ok(()) +} + +async fn get_character_inventory(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result +{ + let mut t = conn.begin().await?; + let inventory = sqlx::query_as::<_, PgInventoryEntity>("select * from inventory where pchar = $1") + .bind(char_id.0) + .fetch_one(&mut t).await?; + + // TODO: inefficient + let mut real_inventory = Vec::new(); + for inv_item in inventory.items.0.into_iter() { + match inv_item { + PgInventoryItemEntity::Individual(item) => { + let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") + .bind(item) + .fetch_one(&mut t).await + .map(|item| item.into()) + .map(|item| apply_item_modifications(&mut t, item))? + .await; + real_inventory.push(InventoryItemEntity::Individual(entity)); }, - item => item - }; + PgInventoryItemEntity::Stacked(items) => { + let mut stacked_item = Vec::new(); + for s_item in items { + stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") + .bind(s_item) + .fetch_one(&mut t).await + .map(|item| item.into())?) + } + real_inventory.push(InventoryItemEntity::Stacked(stacked_item)); + } + } + } + + Ok(InventoryEntity::new(real_inventory)) +} - ItemEntity { - id, - item, +async fn get_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_name: BankName) -> Result +{ + let mut t = conn.begin().await?; + let bank = sqlx::query_as::<_, PgInventoryEntity>("select * from bank where pchar = $1 and name = $2") + .bind(char_id.0) + .bind(bank_name.0) + .fetch_one(&mut t).await?; + // TODO: inefficient + let mut real_bank = Vec::new(); + for bank_item in bank.items.0.into_iter() { + match bank_item { + PgInventoryItemEntity::Individual(item) => { + let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") + .bind(item) + .fetch_one(&mut t).await + .map(|item| item.into()) + .map(|item| apply_item_modifications(&mut t, item))? + .await; + real_bank.push(BankItemEntity::Individual(entity)); + }, + PgInventoryItemEntity::Stacked(items) => { + let mut stacked_item = Vec::new(); + for s_item in items { + stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") + .bind(s_item) + .fetch_one(&mut t).await + .map(|item| item.into()) + .map(|item| apply_item_modifications(&mut t, item))? + .await) + } + real_bank.push(BankItemEntity::Stacked(stacked_item)); + } } } + + Ok(BankEntity::new(real_bank)) +} + +async fn set_character_inventory(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> +{ + let inventory = inventory.items.iter() + .map(|item| { + match item { + InventoryItemEntity::Individual(item) => { + PgInventoryItemEntity::Individual(item.id.0 as i32) + }, + InventoryItemEntity::Stacked(items) => { + PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect()) + }, + } + }) + .collect::>(); + + sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2") + .bind(char_id.0) + .bind(sqlx::types::Json(inventory)) + .execute(conn) + .await?; + Ok(()) +} + +async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: BankName) -> Result<(), GatewayError> +{ + let bank = bank.items.iter() + .map(|item| { + match item { + BankItemEntity::Individual(item) => { + PgInventoryItemEntity::Individual(item.id.0 as i32) + }, + BankItemEntity::Stacked(items) => { + PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect()) + }, + } + }) + .collect::>(); + + sqlx::query("insert into bank (pchar, items, name) values ($1, $2, $3) on conflict (pchar, name) do update set items = $2") + .bind(char_id.0) + .bind(sqlx::types::Json(bank)) + .bind(bank_name.0) + .execute(conn) + .await?; + Ok(()) +} + +async fn get_character_equips(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result +{ + let equips = sqlx::query_as::<_, PgEquipped>("select * from equipped where pchar = $1") + .bind(char_id.0) + .fetch_one(conn) + .await?; + + Ok(equips.into()) +} + +async fn set_character_equips(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> +{ + sqlx::query(r#"insert into equipped (pchar, weapon, armor, shield, unit0, unit1, unit2, unit3, mag) values ($1, $2, $3, $4, $5, $6, $7, $8, $9) + on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#) + .bind(char_id.0) + .bind(equips.weapon.map(|i| i.0 as i32)) + .bind(equips.armor.map(|i| i.0 as i32)) + .bind(equips.shield.map(|i| i.0 as i32)) + .bind(equips.unit[0].map(|i| i.0 as i32)) + .bind(equips.unit[1].map(|i| i.0 as i32)) + .bind(equips.unit[2].map(|i| i.0 as i32)) + .bind(equips.unit[3].map(|i| i.0 as i32)) + .bind(equips.mag.map(|i| i.0 as i32)) + .execute(conn) + .await?; + Ok(()) +} + +async fn set_character_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> +{ + sqlx::query("insert into character_meseta values ($1, $2) on conflict (pchar) do update set items = $2") + .bind(char_id.0) + .bind(meseta.0 as i32) + .execute(conn) + .await?; + Ok(()) +} + +async fn get_character_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result +{ + #[derive(sqlx::FromRow)] + struct PgMeseta(i32); + let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1"#) + .bind(char_id.0) + .fetch_one(conn) + .await?; + Ok(Meseta(meseta.0 as u32)) +} + +async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> +{ + sqlx::query("insert into bank_meseta values ($1, $2, $3) on conflict (pchar, bank) do update set items = $2") + .bind(char_id.0) + .bind(meseta.0 as i32) + .bind(bank.0) + .execute(conn) + .await?; + Ok(()) +} + +async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: BankName) -> Result +{ + #[derive(sqlx::FromRow)] + struct PgMeseta(i32); + let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1 and bank = $2"#) + .bind(char_id.0) + .bind(bank.0) + .fetch_one(conn) + .await?; + Ok(Meseta(meseta.0 as u32)) } #[async_trait::async_trait] impl EntityGateway for PostgresGateway { async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { - let new_user = sqlx::query_as::<_, PgUserAccount>("insert into user_accounts (email, username, password, activated) values ($1, $2, $3, $4) returning *;") - .bind(user.email) - .bind(user.username) - .bind(user.password) - .bind(user.activated) - .fetch_one(&self.pool).await?; - Ok(new_user.into()) + create_user(&mut *self.pool.acquire().await?, user).await } - async fn get_user_by_id(&self, id: UserAccountId) -> Result { - let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where id = $1") - .bind(id.0) - .fetch_one(&self.pool).await?; - Ok(user.into()) + async fn get_user_by_id(&mut self, id: UserAccountId) -> Result { + get_user_by_id(&mut *self.pool.acquire().await?, id).await } - async fn get_user_by_name(&self, username: String) -> Result { - let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where username = $1") - .bind(username) - .fetch_one(&self.pool).await?; - Ok(user.into()) + async fn get_user_by_name(&mut self, username: String) -> Result { + get_user_by_name(&mut *self.pool.acquire().await?, username).await } async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> { - sqlx::query("UPDATE user_accounts set username=$1, password=$2, banned=$3, muted=$4, flags=$5 where id=$6") - .bind(&user.username) - .bind(&user.password) - .bind(&user.banned_until) - .bind(&user.muted_until) - .bind(&user.flags) - .bind(&user.id.0) - .execute(&self.pool).await?; - Ok(()) + save_user(&mut *self.pool.acquire().await?, user).await } async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result { - let new_settings = sqlx::query_as::<_, PgUserSettings>("insert into user_settings (user_account, blocked_users, keyboard_config, gamepad_config, option_flags, shortcuts, symbol_chats, team_name) - values ($1, $2, $3, $4, $5, $6, $7, $8) returning *;") - .bind(settings.user_id.0) - .bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) - .bind(settings.settings.keyboard_config.to_vec()) - .bind(settings.settings.gamepad_config.to_vec()) - .bind(settings.settings.option_flags as i32) - .bind(settings.settings.shortcuts.to_vec()) - .bind(settings.settings.symbol_chats.to_vec()) - .bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) - .fetch_one(&self.pool).await?; - Ok(new_settings.into()) - } - - async fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Result { - let settings = sqlx::query_as::<_, PgUserSettings>("select * from user_settings where user_account = $1") - .bind(user.id.0) - .fetch_one(&self.pool).await?; - Ok(settings.into()) + create_user_settings(&mut *self.pool.acquire().await?, settings).await + } + + async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result { + get_user_settings_by_user(&mut *self.pool.acquire().await?, user).await } async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> { - sqlx::query("update user_settings set blocked_users=$1, keyboard_config=$2, gamepad_config=$3, option_flags=$4, shortcuts=$5, symbol_chats=$6, team_name=$7 where id=$8") - .bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) - .bind(&settings.settings.keyboard_config.to_vec()) - .bind(&settings.settings.gamepad_config.to_vec()) - .bind(&settings.settings.option_flags) - .bind(&settings.settings.shortcuts.to_vec()) - .bind(&settings.settings.symbol_chats.to_vec()) - .bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) - .bind(&settings.id.0) - .fetch_one(&self.pool).await?; - Ok(()) + save_user_settings(&mut *self.pool.acquire().await?, settings).await } async fn create_character(&mut self, char: NewCharacterEntity) -> Result { - let q = r#"insert into player_character - (user_account, slot, name, exp, class, section_id, costume, skin, face, head, hair, hair_r, hair_g, hair_b, prop_x, prop_y, techs, - config, infoboard, guildcard, power, mind, def, evade, luck, hp, tp, tech_menu, option_flags) - values - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31) - returning *;"#; - let character = sqlx::query_as::<_, PgCharacter>(q) - .bind(char.user_id.0) - .bind(char.slot as i16) - .bind(char.name) - .bind(char.exp as i32) - .bind(char.char_class.to_string()) - .bind(char.section_id.to_string()) - .bind(char.appearance.costume as i16) - .bind(char.appearance.skin as i16) - .bind(char.appearance.face as i16) - .bind(char.appearance.head as i16) - .bind(char.appearance.hair as i16) - .bind(char.appearance.hair_r as i16) - .bind(char.appearance.hair_g as i16) - .bind(char.appearance.hair_b as i16) - .bind(char.appearance.prop_x) - .bind(char.appearance.prop_y) - .bind(&char.techs.as_bytes().to_vec()) - .bind(&char.config.as_bytes().to_vec()) - .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) - .bind(char.guildcard.description) - .bind(char.materials.power as i16) - .bind(char.materials.mind as i16) - .bind(char.materials.def as i16) - .bind(char.materials.evade as i16) - .bind(char.materials.luck as i16) - .bind(char.materials.hp as i16) - .bind(char.materials.tp as i16) - .bind(char.tech_menu.tech_menu.to_vec()) - .bind(char.option_flags as i32) - .fetch_one(&self.pool).await?; - - Ok(character.into()) - } - - async fn get_characters_by_user(&self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { - let mut stream = sqlx::query_as::<_, PgCharacter>("select * from player_character where user_account = $1 and slot < 4 order by slot") - .bind(user.id.0) - .fetch(&self.pool); - const NONE: Option = None; - let mut result = [NONE; 4]; - while let Some(character) = stream.try_next().await? { - let index = character.slot as usize; - result[index] = Some(character.into()) - } + create_character(&mut *self.pool.acquire().await?, char).await + } - Ok(result) + async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { + get_characters_by_user(&mut *self.pool.acquire().await?, user).await } async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> { - let q = r#"update player_character set - user_account=$1, slot=$2, name=$3, exp=$4, class=$5, section_id=$6, costume=$7, skin=$8, face=$9, head=$10, hair=$11, hair_r=$12, - hair_g=$13, hair_b=$14, prop_x=$15, prop_y=$16, techs=$17, config=$18, infoboard=$19, guildcard=$20, power=$21, mind=$22, def=$23, - evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29 - where id=$32;"#; - sqlx::query(q) - .bind(char.user_id.0) - .bind(char.slot as i16) - .bind(&char.name) - .bind(char.exp as i32) - .bind(char.char_class.to_string()) - .bind(char.section_id.to_string()) - .bind(char.appearance.costume as i16) - .bind(char.appearance.skin as i16) - .bind(char.appearance.face as i16) - .bind(char.appearance.head as i16) - .bind(char.appearance.hair as i16) - .bind(char.appearance.hair_r as i16) - .bind(char.appearance.hair_g as i16) - .bind(char.appearance.hair_b as i16) - .bind(char.appearance.prop_x) - .bind(char.appearance.prop_y) - .bind(&char.techs.as_bytes().to_vec()) - .bind(&char.config.as_bytes().to_vec()) - .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) - .bind(&char.guildcard.description) - .bind(char.materials.power as i16) - .bind(char.materials.mind as i16) - .bind(char.materials.def as i16) - .bind(char.materials.evade as i16) - .bind(char.materials.luck as i16) - .bind(char.materials.hp as i16) - .bind(char.materials.tp as i16) - .bind(char.tech_menu.tech_menu.to_vec()) - .bind(char.option_flags as i32) - .bind(char.id.0 as i32) - .execute(&self.pool).await?; - Ok(()) - } - - async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result { + save_character(&mut *self.pool.acquire().await?, char).await + } + + async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result { Ok(GuildCardDataEntity { id: GuildCardDataId(0), user_id: user.id, @@ -285,305 +588,68 @@ impl EntityGateway for PostgresGateway { } async fn create_item(&mut self, item: NewItemEntity) -> Result { - let mut tx = self.pool.begin().await?; - let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;") - .bind(sqlx::types::Json(PgItemDetail::from(item.item))) - .fetch_one(&mut tx).await?; - - tx.commit().await?; - Ok(ItemEntity { - id: ItemEntityId(new_item.id as u32), - item: new_item.item.0.into(), - }) + create_item(&mut *self.pool.acquire().await?, item).await } async fn add_item_note(&mut self, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> { - sqlx::query("insert into item_note(item, note) values ($1, $2)") - .bind(item_id.0) - .bind(sqlx::types::Json(PgItemNoteDetail::from(item_note))) - .execute(&self.pool).await?; - Ok(()) - - /* - let mut tx = self.pool.begin().await?; - if let ItemLocation::Inventory{slot, ..} = &item_location { - sqlx::query("delete from inventory_slot where item = $1") - .bind(item_id.0 as i32) - .execute(&mut tx).await?; - sqlx::query("insert into inventory_slot (item, slot) values ($1, $2)") - .bind(item_id.0) - .bind(*slot as i32) - .execute(&mut tx).await?; - sqlx::query(r#"insert into item_location (item, location) - select $1, $2 - where (select jsonb_object_keys(location) from item_location where item=$1 - order by created_at desc limit 1) != 'Inventory'"#) - .bind(item_id.0) - .bind(sqlx::types::Json(PgItemLocationDetail::from(item_location))) - .execute(&mut tx).await?; - } - else { - sqlx::query("insert into item_location (item, location) values ($1, $2)") - .bind(item_id.0) - .bind(sqlx::types::Json(PgItemLocationDetail::from(item_location))) - .execute(&mut tx).await?; - } - tx.commit().await?; - Ok(()) - */ + add_item_note(&mut *self.pool.acquire().await?, item_id, item_note).await } async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> { - sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") - .bind(mag_item_id.0) - .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::FeedMag{food: *tool_item_id}))) - .execute(&self.pool).await?; - Ok(()) + feed_mag(&mut *self.pool.acquire().await?, mag_item_id, tool_item_id).await } async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> { - sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") - .bind(mag_item_id.0) - .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::OwnerChange(character.char_class, character.section_id)))) - .execute(&self.pool).await?; - Ok(()) + change_mag_owner(&mut *self.pool.acquire().await?, mag_item_id, character).await } async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> { - sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") - .bind(mag_item_id.0) - .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::MagCell(*mag_cell_id)))) - .execute(&self.pool).await?; - Ok(()) + use_mag_cell(&mut *self.pool.acquire().await?, mag_item_id, mag_cell_id).await } async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: weapon::WeaponModifier) -> Result<(), GatewayError> { - sqlx::query("insert into weapon_modifier (weapon, modifier) values ($1, $2);") - .bind(item_id.0) - .bind(sqlx::types::Json(modifier)) - .execute(&self.pool).await?; - Ok(()) - } - -/* - async fn get_items_by_character(&self, char_id: &CharacterEntityId) -> Result, GatewayError> { - let q = r#"select * from ( - select distinct on (item_location.item) - item.id, - case - when item_location.location -> 'Inventory' is not null then - jsonb_set(item_location.location, '{Inventory,slot}', inventory_slot.slot::text::jsonb) - else - item_location.location - end, - item.item - from item_location - join item on item.id = item_location.item - join inventory_slot on inventory_slot.item = item.id - order by item_location.item, item_location.created_at desc - ) as i - where cast (location -> 'Inventory' ->> 'character_id' as integer) = $1 - or cast (location -> 'Bank' ->> 'character_id' as integer) = $1 - "#; - let items = sqlx::query_as::<_, PgItemWithLocation>(q) - .bind(char_id.0) - .fetch(&self.pool); - Ok(join_all(items - .filter_map(|item: Result| { - let item = item.ok()?; - Some(ItemEntity { - id: ItemEntityId(item.id as u32), - item: item.item.0.into(), - location: item.location.0.into() - }) - }) - .map(|item: ItemEntity| { - self.apply_item_modifications(item) - }) - .collect::>() - .await - ).await) - } -*/ + add_weapon_modifier(&mut *self.pool.acquire().await?, item_id, modifier).await + } + async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result { - let inventory = sqlx::query_as::<_, PgInventoryEntity>("select * from inventory where pchar = $1") - .bind(char_id.0) - .fetch_one(&self.pool).await?; - // TODO: inefficient - let mut real_inventory = Vec::new(); - for inv_item in inventory.items.0.into_iter() { - match inv_item { - PgInventoryItemEntity::Individual(item) => { - let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(item) - .fetch_one(&self.pool).await - .map(|item| item.into()) - .map(|item| self.apply_item_modifications(item))? - .await; - real_inventory.push(InventoryItemEntity::Individual(entity)); - }, - PgInventoryItemEntity::Stacked(items) => { - let mut stacked_item = Vec::new(); - for s_item in items { - stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(s_item) - .fetch_one(&self.pool).await - .map(|item| item.into()) - .map(|item| self.apply_item_modifications(item))? - .await) - } - real_inventory.push(InventoryItemEntity::Stacked(stacked_item)); - } - } - } - - Ok(InventoryEntity::new(real_inventory)) + get_character_inventory(&mut *self.pool.acquire().await?, char_id).await } async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: BankName) -> Result { - let bank = sqlx::query_as::<_, PgInventoryEntity>("select * from bank where pchar = $1 and name = $2") - .bind(char_id.0) - .bind(bank_name.0) - .fetch_one(&self.pool).await?; - // TODO: inefficient - let mut real_bank = Vec::new(); - for bank_item in bank.items.0.into_iter() { - match bank_item { - PgInventoryItemEntity::Individual(item) => { - let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(item) - .fetch_one(&self.pool).await - .map(|item| item.into()) - .map(|item| self.apply_item_modifications(item))? - .await; - real_bank.push(BankItemEntity::Individual(entity)); - }, - PgInventoryItemEntity::Stacked(items) => { - let mut stacked_item = Vec::new(); - for s_item in items { - stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(s_item) - .fetch_one(&self.pool).await - .map(|item| item.into()) - .map(|item| self.apply_item_modifications(item))? - .await) - } - real_bank.push(BankItemEntity::Stacked(stacked_item)); - } - } - } - - Ok(BankEntity::new(real_bank)) + get_character_bank(&mut *self.pool.acquire().await?, char_id, bank_name).await } async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> { - let inventory = inventory.items.iter() - .map(|item| { - match item { - InventoryItemEntity::Individual(item) => { - PgInventoryItemEntity::Individual(item.id.0 as i32) - }, - InventoryItemEntity::Stacked(items) => { - PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect()) - }, - } - }) - .collect::>(); - - sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2") - .bind(char_id.0) - .bind(sqlx::types::Json(inventory)) - .execute(&self.pool) - .await?; - Ok(()) + set_character_inventory(&mut *self.pool.acquire().await?, char_id, inventory).await } async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: BankName) -> Result<(), GatewayError> { - let bank = bank.items.iter() - .map(|item| { - match item { - BankItemEntity::Individual(item) => { - PgInventoryItemEntity::Individual(item.id.0 as i32) - }, - BankItemEntity::Stacked(items) => { - PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect()) - }, - } - }) - .collect::>(); - - sqlx::query("insert into bank (pchar, items, name) values ($1, $2, $3) on conflict (pchar, name) do update set items = $2") - .bind(char_id.0) - .bind(sqlx::types::Json(bank)) - .bind(bank_name.0) - .execute(&self.pool) - .await?; - Ok(()) + set_character_bank(&mut *self.pool.acquire().await?, char_id, bank, bank_name).await } async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result { - let equips = sqlx::query_as::<_, PgEquipped>("select * from equipped where pchar = $1") - .bind(char_id.0) - .fetch_one(&self.pool) - .await?; - - Ok(equips.into()) + get_character_equips(&mut *self.pool.acquire().await?, char_id).await } async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> { - sqlx::query(r#"insert into equipped (pchar, weapon, armor, shield, unit0, unit1, unit2, unit3, mag) values ($1, $2, $3, $4, $5, $6, $7, $8, $9) - on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#) - .bind(char_id.0) - .bind(equips.weapon.map(|i| i.0 as i32)) - .bind(equips.armor.map(|i| i.0 as i32)) - .bind(equips.shield.map(|i| i.0 as i32)) - .bind(equips.unit[0].map(|i| i.0 as i32)) - .bind(equips.unit[1].map(|i| i.0 as i32)) - .bind(equips.unit[2].map(|i| i.0 as i32)) - .bind(equips.unit[3].map(|i| i.0 as i32)) - .bind(equips.mag.map(|i| i.0 as i32)) - .execute(&self.pool) - .await?; - Ok(()) + set_character_equips(&mut *self.pool.acquire().await?, char_id, equips).await } async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> { - sqlx::query("insert into character_meseta values ($1, $2) on conflict (pchar) do update set items = $2") - .bind(char_id.0) - .bind(meseta.0 as i32) - .execute(&self.pool) - .await?; - Ok(()) + set_character_meseta(&mut *self.pool.acquire().await?, char_id, meseta).await } async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result { - #[derive(sqlx::FromRow)] - struct PgMeseta(i32); - let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1"#) - .bind(char_id.0) - .fetch_one(&self.pool) - .await?; - Ok(Meseta(meseta.0 as u32)) + get_character_meseta(&mut *self.pool.acquire().await?, char_id).await } async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> { - sqlx::query("insert into bank_meseta values ($1, $2, $3) on conflict (pchar, bank) do update set items = $2") - .bind(char_id.0) - .bind(meseta.0 as i32) - .bind(bank.0) - .execute(&self.pool) - .await?; - Ok(()) + set_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank, meseta).await } async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName) -> Result { - #[derive(sqlx::FromRow)] - struct PgMeseta(i32); - let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1 and bank = $2"#) - .bind(char_id.0) - .bind(bank.0) - .fetch_one(&self.pool) - .await?; - Ok(Meseta(meseta.0 as u32)) + get_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank).await } } + + diff --git a/src/login/character.rs b/src/login/character.rs index 2a7d75b..11d0eb0 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -327,7 +327,7 @@ impl CharacterServerState { } async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result, anyhow::Error> { - match get_login_status(&self.entity_gateway, pkt).await { + match get_login_status(&mut self.entity_gateway, pkt).await { Ok(user) => { if let Some(connected_client) = self.connected_clients.get(&user.id) { if let Some(expires) = connected_client.expires { diff --git a/src/login/login.rs b/src/login/login.rs index 4181136..8e6020f 100644 --- a/src/login/login.rs +++ b/src/login/login.rs @@ -59,7 +59,8 @@ impl SendServerPacket for SendLoginPacket { } } -pub async fn get_login_status(entity_gateway: &impl EntityGateway, pkt: &Login) -> Result { +// TODO: MORE impl EntityGateway? +pub async fn get_login_status(entity_gateway: &mut impl EntityGateway, pkt: &Login) -> Result { let username = array_to_utf8(pkt.username).map_err(|_err| AccountStatus::Error)?; let password = array_to_utf8(pkt.password).map_err(|_err| AccountStatus::Error)?; let user = entity_gateway.get_user_by_name(username).await.map_err(|_| AccountStatus::InvalidUser)?; @@ -108,7 +109,7 @@ impl LoginServerState { } async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result, anyhow::Error> { - match get_login_status(&self.entity_gateway, pkt).await.and_then(check_if_already_online) { + match get_login_status(&mut self.entity_gateway, pkt).await.and_then(check_if_already_online) { Ok(mut user) => { user.at_login = true; self.entity_gateway.save_user(&user).await.map_err(|_| LoginError::DbError)?; @@ -351,7 +352,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { - async fn get_user_by_name(&self, name: String) -> Result { + async fn get_user_by_name(&mut self, name: String) -> Result { assert!(name == "testuser"); Ok(UserAccountEntity { id: UserAccountId(1),