From 9aab6e11ec588ae9ba3c94aaa8dc9ad474275aa6 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Fri, 12 Jun 2020 01:03:56 -0400 Subject: [PATCH] Implement BeatmapCache and Oppai interface --- Cargo.lock | 53 +++++++++++++++ youmubot-osu/Cargo.toml | 2 + youmubot-osu/src/discord/mod.rs | 4 +- youmubot-osu/src/discord/oppai_cache.rs | 90 +++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 youmubot-osu/src/discord/oppai_cache.rs diff --git a/Cargo.lock b/Cargo.lock index b540b26..740b93b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,6 +22,14 @@ name = "adler32" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "aho-corasick" version = "0.7.10" @@ -162,6 +170,24 @@ dependencies = [ "syn 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "const-random" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "const-random-macro" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "core-foundation" version = "0.7.0" @@ -244,6 +270,16 @@ dependencies = [ "sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dashmap" +version = "3.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ahash 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "digest" version = "0.8.1" @@ -796,6 +832,16 @@ dependencies = [ "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "oppai-rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot" version = "0.9.0" @@ -1710,7 +1756,9 @@ version = "0.1.0" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "dashmap 3.11.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "oppai-rs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1737,6 +1785,7 @@ dependencies = [ "checksum Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" "checksum addr2line 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" +"checksum ahash 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" "checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum backtrace 0.3.48 (registry+https://github.com/rust-lang/crates.io-index)" = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" @@ -1756,6 +1805,8 @@ dependencies = [ "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum codeforces 0.1.0 (git+https://github.com/natsukagami/rust-codeforces-api)" = "" "checksum command_attr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c27d6155f93d880b6379d93ddc9b2417b3b69b715360c5f25525e4576338a381" +"checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" +"checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" "checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" "checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" @@ -1765,6 +1816,7 @@ dependencies = [ "checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" "checksum ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113" +"checksum dashmap 3.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8cfcd41ae02d60edded204341d2798ba519c336c51a37330aa4b98a1128def32" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" "checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" @@ -1830,6 +1882,7 @@ dependencies = [ "checksum openssl 0.10.29 (registry+https://github.com/rust-lang/crates.io-index)" = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-sys 0.9.56 (registry+https://github.com/rust-lang/crates.io-index)" = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e" +"checksum oppai-rs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f143357550da5c04800333509df440fcbe5254120a5af05097a083caf23105b" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" diff --git a/youmubot-osu/Cargo.toml b/youmubot-osu/Cargo.toml index d851508..f92a0e8 100644 --- a/youmubot-osu/Cargo.toml +++ b/youmubot-osu/Cargo.toml @@ -15,6 +15,8 @@ bitflags = "1" rayon = "1.1" lazy_static = "1" regex = "1" +oppai-rs = "0.2.0" +dashmap = "3.11.4" youmubot-db = { path = "../youmubot-db" } youmubot-prelude = { path = "../youmubot-prelude" } diff --git a/youmubot-osu/src/discord/mod.rs b/youmubot-osu/src/discord/mod.rs index 418171a..22f5a58 100644 --- a/youmubot-osu/src/discord/mod.rs +++ b/youmubot-osu/src/discord/mod.rs @@ -20,6 +20,7 @@ mod cache; mod db; pub(crate) mod embeds; mod hook; +mod oppai_cache; mod server_rank; use db::OsuUser; @@ -58,9 +59,10 @@ pub fn setup( // API client let http_client = data.get_cloned::(); data.insert::(OsuHttpClient::new( - http_client, + http_client.clone(), std::env::var("OSU_API_KEY").expect("Please set OSU_API_KEY as osu! api key."), )); + data.insert::(oppai_cache::BeatmapCache::new(http_client)); // Announcer announcers.add(announcer::ANNOUNCER_KEY, announcer::updates); diff --git a/youmubot-osu/src/discord/oppai_cache.rs b/youmubot-osu/src/discord/oppai_cache.rs new file mode 100644 index 0000000..11b3a2e --- /dev/null +++ b/youmubot-osu/src/discord/oppai_cache.rs @@ -0,0 +1,90 @@ +use serenity::framework::standard::CommandError; +use std::{ffi::CString, sync::Arc}; +use youmubot_prelude::TypeMapKey; + +/// the information collected from a download/Oppai request. +#[derive(Clone, Debug)] +pub struct BeatmapContent { + id: u64, + content: Arc, +} + +/// the output of "one" oppai run. +#[derive(Clone, Copy, Debug)] +pub struct BeatmapInfo { + stars: f32, + pp: [f32; 4], // 95, 98, 99, 100 +} + +impl BeatmapContent { + /// Get pp given the combo and accuracy. + pub fn get_pp_from( + &self, + combo: oppai_rs::Combo, + accuracy: f32, + mods: impl Into, + ) -> Result { + Ok(oppai_rs::Oppai::new_from_content(&self.content[..])? + .combo(combo)? + .accuracy(accuracy)? + .mods(mods.into()) + .pp()) + } + + /// Get info given mods. + pub fn get_info_with( + &self, + mods: impl Into, + ) -> Result { + let mut oppai = oppai_rs::Oppai::new_from_content(&self.content[..])?; + oppai.mods(mods.into()).combo(oppai_rs::Combo::PERFECT)?; + let pp = [ + oppai.accuracy(95.0)?.pp(), + oppai.accuracy(98.0)?.pp(), + oppai.accuracy(99.0)?.pp(), + oppai.accuracy(100.0)?.pp(), + ]; + let stars = oppai.stars(); + Ok(BeatmapInfo { stars, pp }) + } +} + +/// A central cache for the beatmaps. +pub struct BeatmapCache { + client: reqwest::blocking::Client, + cache: Arc>, +} + +impl BeatmapCache { + /// Create a new cache. + pub fn new(client: reqwest::blocking::Client) -> Self { + BeatmapCache { + client, + cache: Arc::new(dashmap::DashMap::new()), + } + } + + fn download_beatmap(&self, id: u64) -> Result { + let content = self + .client + .get(&format!("https://osu.ppy.sh/u/{}", id)) + .send()? + .bytes()?; + Ok(BeatmapContent { + id, + content: Arc::new(CString::new(content.into_iter().collect::>())?), + }) + } + + /// Get a beatmap from the cache. + pub fn get_beatmap(&self, id: u64) -> Result { + self.cache + .entry(id) + .or_try_insert_with(|| self.download_beatmap(id)) + .map(|v| v.clone()) + } +} + +impl TypeMapKey for BeatmapCache { + type Value = BeatmapCache; +}