mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-19 08:48:54 +00:00
Temporarily move to rosu
This commit is contained in:
parent
a1975d5b54
commit
51db72acb2
10 changed files with 186 additions and 172 deletions
19
Cargo.lock
generated
19
Cargo.lock
generated
|
@ -1029,17 +1029,6 @@ dependencies = [
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "oppai-rs"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e060483dec5ed6590103a045f0a0fe09c06e185d582ec66efaa87e24cc1e877e"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
@ -1349,6 +1338,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rosu-pp"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4efb6f419a910e96683aada6e12c14f4a4b1da286f1a205a1edc3eca3b8fa69e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustbreak"
|
name = "rustbreak"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -2308,9 +2303,9 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"oppai-rs",
|
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"rosu-pp",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serenity",
|
"serenity",
|
||||||
|
|
|
@ -14,7 +14,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
oppai-rs = "0.2"
|
rosu-pp = "0.4"
|
||||||
dashmap = "4"
|
dashmap = "4"
|
||||||
bincode = "1"
|
bincode = "1"
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,7 @@ mod scores {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod table {
|
pub mod table {
|
||||||
|
use crate::discord::oppai_cache::Accuracy;
|
||||||
use crate::discord::{Beatmap, BeatmapCache, BeatmapInfo, BeatmapMetaCache};
|
use crate::discord::{Beatmap, BeatmapCache, BeatmapInfo, BeatmapMetaCache};
|
||||||
use crate::models::{Mode, Score};
|
use crate::models::{Mode, Score};
|
||||||
use serenity::{framework::standard::CommandResult, model::channel::Message};
|
use serenity::{framework::standard::CommandResult, model::channel::Message};
|
||||||
|
@ -183,8 +184,8 @@ mod scores {
|
||||||
let beatmap = osu.get_beatmap(play.beatmap_id, mode).await?;
|
let beatmap = osu.get_beatmap(play.beatmap_id, mode).await?;
|
||||||
let info = {
|
let info = {
|
||||||
let b = beatmap_cache.get_beatmap(beatmap.beatmap_id).await?;
|
let b = beatmap_cache.get_beatmap(beatmap.beatmap_id).await?;
|
||||||
mode.to_oppai_mode()
|
(if mode == Mode::Std { Some(mode) } else { None })
|
||||||
.and_then(|mode| b.get_info_with(Some(mode), play.mods).ok())
|
.and_then(|_| b.get_info_with(play.mods).ok())
|
||||||
};
|
};
|
||||||
Ok((beatmap, info)) as Result<(Beatmap, Option<BeatmapInfo>)>
|
Ok((beatmap, info)) as Result<(Beatmap, Option<BeatmapInfo>)>
|
||||||
})
|
})
|
||||||
|
@ -198,25 +199,23 @@ mod scores {
|
||||||
Some(v) => Ok(v),
|
Some(v) => Ok(v),
|
||||||
None => {
|
None => {
|
||||||
let b = beatmap_cache.get_beatmap(p.beatmap_id).await?;
|
let b = beatmap_cache.get_beatmap(p.beatmap_id).await?;
|
||||||
let r: Result<_> = Ok(mode
|
let r: Result<_> =
|
||||||
.to_oppai_mode()
|
Ok((if mode == Mode::Std { Some(mode) } else { None })
|
||||||
.and_then(|op| {
|
.and_then(|_| {
|
||||||
b.get_pp_from(
|
b.get_pp_from(
|
||||||
oppai_rs::Combo::NonFC {
|
Some(p.max_combo as usize),
|
||||||
max_combo: p.max_combo as u32,
|
Accuracy::ByCount(
|
||||||
misses: p.count_miss as u32,
|
p.count_300,
|
||||||
},
|
p.count_100,
|
||||||
oppai_rs::Accuracy::from_hits(
|
p.count_50,
|
||||||
p.count_100 as u32,
|
p.count_miss,
|
||||||
p.count_50 as u32,
|
),
|
||||||
),
|
p.mods,
|
||||||
Some(op),
|
)
|
||||||
p.mods,
|
.ok()
|
||||||
)
|
.map(|pp| format!("{:.2}pp [?]", pp))
|
||||||
.ok()
|
})
|
||||||
.map(|pp| format!("{:.2}pp [?]", pp))
|
.unwrap_or_else(|| "-".to_owned()));
|
||||||
})
|
|
||||||
.unwrap_or_else(|| "-".to_owned()));
|
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,7 +324,7 @@ mod scores {
|
||||||
page + 1,
|
page + 1,
|
||||||
self.total_pages()
|
self.total_pages()
|
||||||
));
|
));
|
||||||
if self.mode.to_oppai_mode().is_none() {
|
if self.mode != Mode::Std {
|
||||||
m.push_line("Note: star difficulty doesn't reflect mods applied.");
|
m.push_line("Note: star difficulty doesn't reflect mods applied.");
|
||||||
} else {
|
} else {
|
||||||
m.push_line("[?] means pp was predicted by oppai-rs.");
|
m.push_line("[?] means pp was predicted by oppai-rs.");
|
||||||
|
@ -400,12 +399,12 @@ mod beatmapset {
|
||||||
async fn get_beatmap_info(&self, ctx: &Context, b: &Beatmap) -> Option<BeatmapInfoWithPP> {
|
async fn get_beatmap_info(&self, ctx: &Context, b: &Beatmap) -> Option<BeatmapInfoWithPP> {
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let cache = data.get::<BeatmapCache>().unwrap();
|
let cache = data.get::<BeatmapCache>().unwrap();
|
||||||
let mode = self.mode.unwrap_or(b.mode).to_oppai_mode();
|
|
||||||
cache
|
cache
|
||||||
.get_beatmap(b.beatmap_id)
|
.get_beatmap(b.beatmap_id)
|
||||||
.map(move |v| {
|
.map(move |v| {
|
||||||
v.ok()
|
v.ok()
|
||||||
.and_then(move |v| v.get_possible_pp_with(Some(mode?), self.mods).ok())
|
.filter(|_| b.mode == Mode::Std)
|
||||||
|
.and_then(move |v| v.get_possible_pp_with(self.mods).ok())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::BeatmapWithMode;
|
use super::BeatmapWithMode;
|
||||||
use crate::{
|
use crate::{
|
||||||
discord::oppai_cache::{BeatmapContent, BeatmapInfo, BeatmapInfoWithPP, OppaiAccuracy},
|
discord::oppai_cache::{Accuracy, BeatmapContent, BeatmapInfo, BeatmapInfoWithPP},
|
||||||
models::{Beatmap, Mode, Mods, Rank, Score, User},
|
models::{Beatmap, Mode, Mods, Rank, Score, User},
|
||||||
};
|
};
|
||||||
use serenity::{builder::CreateEmbed, utils::MessageBuilder};
|
use serenity::{builder::CreateEmbed, utils::MessageBuilder};
|
||||||
|
@ -212,12 +212,14 @@ impl<'a> ScoreEmbedBuilder<'a> {
|
||||||
let content = self.content;
|
let content = self.content;
|
||||||
let u = self.u;
|
let u = self.u;
|
||||||
let accuracy = s.accuracy(mode);
|
let accuracy = s.accuracy(mode);
|
||||||
let info = mode
|
let info = if mode == Mode::Std {
|
||||||
.to_oppai_mode()
|
content.get_info_with(s.mods).ok()
|
||||||
.and_then(|mode| content.get_info_with(Some(mode), s.mods).ok());
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let stars = info
|
let stars = info
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|info| info.stars as f64)
|
.map(|info| info.stars)
|
||||||
.unwrap_or(b.difficulty.stars);
|
.unwrap_or(b.difficulty.stars);
|
||||||
let score_line = match s.rank {
|
let score_line = match s.rank {
|
||||||
Rank::SS | Rank::SSH => "SS".to_string(),
|
Rank::SS | Rank::SSH => "SS".to_string(),
|
||||||
|
@ -239,13 +241,12 @@ impl<'a> ScoreEmbedBuilder<'a> {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let pp = s.pp.map(|pp| (pp, format!("{:.2}pp", pp))).or_else(|| {
|
let pp = s.pp.map(|pp| (pp, format!("{:.2}pp", pp))).or_else(|| {
|
||||||
mode.to_oppai_mode()
|
(if mode == Mode::Std { Some(mode) } else { None })
|
||||||
.and_then(|op| {
|
.and_then(|_| {
|
||||||
content
|
content
|
||||||
.get_pp_from(
|
.get_pp_from(
|
||||||
oppai_rs::Combo::non_fc(s.max_combo as u32, s.count_miss as u32),
|
Some(s.max_combo as usize),
|
||||||
OppaiAccuracy::from_hits(s.count_100 as u32, s.count_50 as u32),
|
Accuracy::ByCount(s.count_300, s.count_100, s.count_50, s.count_miss),
|
||||||
Some(op),
|
|
||||||
s.mods,
|
s.mods,
|
||||||
)
|
)
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -253,13 +254,17 @@ impl<'a> ScoreEmbedBuilder<'a> {
|
||||||
.map(|pp| (pp as f64, format!("{:.2}pp [?]", pp)))
|
.map(|pp| (pp as f64, format!("{:.2}pp [?]", pp)))
|
||||||
});
|
});
|
||||||
let pp = if !s.perfect {
|
let pp = if !s.perfect {
|
||||||
mode.to_oppai_mode()
|
(if mode == Mode::Std { Some(mode) } else { None })
|
||||||
.and_then(|op| {
|
.and_then(|_| {
|
||||||
content
|
content
|
||||||
.get_pp_from(
|
.get_pp_from(
|
||||||
oppai_rs::Combo::FC(0),
|
None,
|
||||||
OppaiAccuracy::from_hits(s.count_100 as u32, s.count_50 as u32),
|
Accuracy::ByCount(
|
||||||
Some(op),
|
s.count_300 + s.count_miss,
|
||||||
|
s.count_100,
|
||||||
|
s.count_50,
|
||||||
|
0,
|
||||||
|
),
|
||||||
s.mods,
|
s.mods,
|
||||||
)
|
)
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -354,7 +359,7 @@ impl<'a> ScoreEmbedBuilder<'a> {
|
||||||
)
|
)
|
||||||
.field("Map stats", diff.format_info(mode, s.mods, b), false);
|
.field("Map stats", diff.format_info(mode, s.mods, b), false);
|
||||||
let mut footer = self.footer.take().unwrap_or_else(String::new);
|
let mut footer = self.footer.take().unwrap_or_else(String::new);
|
||||||
if mode.to_oppai_mode().is_none() && s.mods != Mods::NOMOD {
|
if mode != Mode::Std && s.mods != Mods::NOMOD {
|
||||||
footer += " Star difficulty does not reflect game mods.";
|
footer += " Star difficulty does not reflect game mods.";
|
||||||
}
|
}
|
||||||
if !footer.is_empty() {
|
if !footer.is_empty() {
|
||||||
|
|
|
@ -122,13 +122,13 @@ fn handle_old_links<'a>(
|
||||||
.map(|v| Mods::from_str(v.as_str()).pls_ok())
|
.map(|v| Mods::from_str(v.as_str()).pls_ok())
|
||||||
.flatten()
|
.flatten()
|
||||||
.unwrap_or(Mods::NOMOD);
|
.unwrap_or(Mods::NOMOD);
|
||||||
let info = match mode.unwrap_or(b.mode).to_oppai_mode() {
|
let info = match mode.unwrap_or(b.mode) {
|
||||||
Some(mode) => cache
|
Mode::Std => cache
|
||||||
.get_beatmap(b.beatmap_id)
|
.get_beatmap(b.beatmap_id)
|
||||||
.await
|
.await
|
||||||
.and_then(|b| b.get_possible_pp_with(Some(mode), mods))
|
.and_then(|b| b.get_possible_pp_with(mods))
|
||||||
.pls_ok(),
|
.pls_ok(),
|
||||||
None => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
Some(ToPrint {
|
Some(ToPrint {
|
||||||
embed: EmbedType::Beatmap(b, info, mods),
|
embed: EmbedType::Beatmap(b, info, mods),
|
||||||
|
@ -192,13 +192,13 @@ fn handle_new_links<'a>(
|
||||||
.name("mods")
|
.name("mods")
|
||||||
.and_then(|v| Mods::from_str(v.as_str()).pls_ok())
|
.and_then(|v| Mods::from_str(v.as_str()).pls_ok())
|
||||||
.unwrap_or(Mods::NOMOD);
|
.unwrap_or(Mods::NOMOD);
|
||||||
let info = match mode.unwrap_or(beatmap.mode).to_oppai_mode() {
|
let info = match mode.unwrap_or(beatmap.mode) {
|
||||||
Some(mode) => cache
|
Mode::Std => cache
|
||||||
.get_beatmap(beatmap.beatmap_id)
|
.get_beatmap(beatmap.beatmap_id)
|
||||||
.await
|
.await
|
||||||
.and_then(|b| b.get_possible_pp_with(Some(mode), mods))
|
.and_then(|b| b.get_possible_pp_with(mods))
|
||||||
.pls_ok(),
|
.pls_ok(),
|
||||||
None => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
Some(ToPrint {
|
Some(ToPrint {
|
||||||
embed: EmbedType::Beatmap(beatmap, info, mods),
|
embed: EmbedType::Beatmap(beatmap, info, mods),
|
||||||
|
@ -258,13 +258,13 @@ fn handle_short_links<'a>(
|
||||||
.name("mods")
|
.name("mods")
|
||||||
.and_then(|v| Mods::from_str(v.as_str()).pls_ok())
|
.and_then(|v| Mods::from_str(v.as_str()).pls_ok())
|
||||||
.unwrap_or(Mods::NOMOD);
|
.unwrap_or(Mods::NOMOD);
|
||||||
let info = match mode.unwrap_or(beatmap.mode).to_oppai_mode() {
|
let info = match mode.unwrap_or(beatmap.mode) {
|
||||||
Some(mode) => cache
|
Mode::Std => cache
|
||||||
.get_beatmap(beatmap.beatmap_id)
|
.get_beatmap(beatmap.beatmap_id)
|
||||||
.await
|
.await
|
||||||
.and_then(|b| b.get_possible_pp_with(Some(mode), mods))
|
.and_then(|b| b.get_possible_pp_with(mods))
|
||||||
.pls_ok(),
|
.pls_ok(),
|
||||||
None => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
let r: Result<_> = Ok(ToPrint {
|
let r: Result<_> = Ok(ToPrint {
|
||||||
embed: EmbedType::Beatmap(beatmap, info, mods),
|
embed: EmbedType::Beatmap(beatmap, info, mods),
|
||||||
|
|
|
@ -421,7 +421,7 @@ pub async fn last(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get_beatmap(b.beatmap_id)
|
.get_beatmap(b.beatmap_id)
|
||||||
.await?
|
.await?
|
||||||
.get_possible_pp_with(m.to_oppai_mode(), mods)
|
.get_possible_pp_with(mods)
|
||||||
.ok();
|
.ok();
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.send_message(&ctx, |f| {
|
.send_message(&ctx, |f| {
|
||||||
|
@ -589,14 +589,14 @@ async fn get_user(ctx: &Context, msg: &Message, mut args: Args, mode: Mode) -> C
|
||||||
{
|
{
|
||||||
Some(m) => {
|
Some(m) => {
|
||||||
let beatmap = cache.get_beatmap(m.beatmap_id, mode).await?;
|
let beatmap = cache.get_beatmap(m.beatmap_id, mode).await?;
|
||||||
let info = match mode.to_oppai_mode() {
|
let info = match mode {
|
||||||
Some(mode) => Some(
|
Mode::Std => Some(
|
||||||
oppai
|
oppai
|
||||||
.get_beatmap(m.beatmap_id)
|
.get_beatmap(m.beatmap_id)
|
||||||
.await?
|
.await?
|
||||||
.get_info_with(Some(mode), m.mods)?,
|
.get_info_with(m.mods)?,
|
||||||
),
|
),
|
||||||
None => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
Some((m, BeatmapWithMode(beatmap, mode), info))
|
Some((m, BeatmapWithMode(beatmap, mode), info))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,77 +1,106 @@
|
||||||
use std::{ffi::CString, sync::Arc};
|
use crate::mods::Mods;
|
||||||
|
use rosu_pp::{Beatmap, BeatmapExt};
|
||||||
|
use std::sync::Arc;
|
||||||
use youmubot_db_sql::{models::osu as models, Pool};
|
use youmubot_db_sql::{models::osu as models, Pool};
|
||||||
use youmubot_prelude::*;
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
pub use oppai_rs::Accuracy as OppaiAccuracy;
|
|
||||||
|
|
||||||
/// the information collected from a download/Oppai request.
|
/// the information collected from a download/Oppai request.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BeatmapContent {
|
pub struct BeatmapContent {
|
||||||
id: u64,
|
id: u64,
|
||||||
content: Arc<CString>,
|
content: Arc<Beatmap>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the output of "one" oppai run.
|
/// the output of "one" oppai run.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct BeatmapInfo {
|
pub struct BeatmapInfo {
|
||||||
pub objects: u32,
|
pub objects: usize,
|
||||||
pub stars: f32,
|
pub stars: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum Accuracy {
|
||||||
|
ByCount(u64, u64, u64, u64), // 300 / 100 / 50 / misses
|
||||||
|
#[allow(dead_code)]
|
||||||
|
ByValue(f64, u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<f64> for Accuracy {
|
||||||
|
fn into(self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Accuracy::ByValue(v, _) => v,
|
||||||
|
Accuracy::ByCount(n300, n100, n50, nmiss) => {
|
||||||
|
((6 * n300 + 2 * n100 + n50) as f64) / ((6 * (n300 + n100 + n50 + nmiss)) as f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Accuracy {
|
||||||
|
pub fn misses(&self) -> usize {
|
||||||
|
(match self {
|
||||||
|
Accuracy::ByCount(_, _, _, nmiss) => *nmiss,
|
||||||
|
Accuracy::ByValue(_, nmiss) => *nmiss,
|
||||||
|
}) as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Beatmap Info with attached 95/98/99/100% FC pp.
|
/// Beatmap Info with attached 95/98/99/100% FC pp.
|
||||||
pub type BeatmapInfoWithPP = (BeatmapInfo, [f32; 4]);
|
pub type BeatmapInfoWithPP = (BeatmapInfo, [f64; 4]);
|
||||||
|
|
||||||
impl BeatmapContent {
|
impl BeatmapContent {
|
||||||
/// Get pp given the combo and accuracy.
|
/// Get pp given the combo and accuracy.
|
||||||
pub fn get_pp_from(
|
pub fn get_pp_from(&self, combo: Option<usize>, accuracy: Accuracy, mods: Mods) -> Result<f64> {
|
||||||
&self,
|
let bm = self.content.as_ref();
|
||||||
combo: oppai_rs::Combo,
|
let mut rosu = rosu_pp::OsuPP::new(bm).mods(mods.bits() as u32);
|
||||||
accuracy: impl Into<OppaiAccuracy>,
|
if let Some(combo) = combo {
|
||||||
mode: Option<oppai_rs::Mode>,
|
rosu = rosu.combo(combo);
|
||||||
mods: impl Into<oppai_rs::Mods>,
|
|
||||||
) -> Result<f32> {
|
|
||||||
let mut oppai = oppai_rs::Oppai::new_from_content(&self.content[..])?;
|
|
||||||
oppai.combo(combo)?.accuracy(accuracy)?.mods(mods.into());
|
|
||||||
if let Some(mode) = mode {
|
|
||||||
oppai.mode(mode)?;
|
|
||||||
}
|
}
|
||||||
Ok(oppai.pp())
|
if let Accuracy::ByCount(n300, n100, n50, _) = accuracy {
|
||||||
|
rosu = rosu
|
||||||
|
.n300(n300 as usize)
|
||||||
|
.n100(n100 as usize)
|
||||||
|
.n50(n50 as usize);
|
||||||
|
}
|
||||||
|
Ok(rosu
|
||||||
|
.misses(accuracy.misses())
|
||||||
|
.accuracy(accuracy.into())
|
||||||
|
.calculate()
|
||||||
|
.pp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get info given mods.
|
/// Get info given mods.
|
||||||
pub fn get_info_with(
|
pub fn get_info_with(&self, mods: Mods) -> Result<BeatmapInfo> {
|
||||||
&self,
|
let stars = self.content.stars(mods.bits() as u32, None);
|
||||||
mode: Option<oppai_rs::Mode>,
|
Ok(BeatmapInfo {
|
||||||
mods: impl Into<oppai_rs::Mods>,
|
objects: stars.max_combo().unwrap_or(0),
|
||||||
) -> Result<BeatmapInfo> {
|
stars: stars.stars(),
|
||||||
let mut oppai = oppai_rs::Oppai::new_from_content(&self.content[..])?;
|
})
|
||||||
if let Some(mode) = mode {
|
|
||||||
oppai.mode(mode)?;
|
|
||||||
}
|
|
||||||
oppai.mods(mods.into());
|
|
||||||
let objects = oppai.num_objects();
|
|
||||||
let stars = oppai.stars();
|
|
||||||
Ok(BeatmapInfo { objects, stars })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_possible_pp_with(
|
pub fn get_possible_pp_with(&self, mods: Mods) -> Result<BeatmapInfoWithPP> {
|
||||||
&self,
|
let rosu = || self.content.pp().mods(mods.bits() as u32);
|
||||||
mode: Option<oppai_rs::Mode>,
|
let pp95 = rosu().accuracy(95.0).calculate();
|
||||||
mods: impl Into<oppai_rs::Mods>,
|
|
||||||
) -> Result<BeatmapInfoWithPP> {
|
|
||||||
let mut oppai = oppai_rs::Oppai::new_from_content(&self.content[..])?;
|
|
||||||
if let Some(mode) = mode {
|
|
||||||
oppai.mode(mode)?;
|
|
||||||
}
|
|
||||||
oppai.mods(mods.into()).combo(oppai_rs::Combo::PERFECT)?;
|
|
||||||
let pp = [
|
let pp = [
|
||||||
oppai.accuracy(95.0)?.pp(),
|
pp95.pp(),
|
||||||
oppai.accuracy(98.0)?.pp(),
|
rosu()
|
||||||
oppai.accuracy(99.0)?.pp(),
|
.attributes(pp95.clone())
|
||||||
oppai.accuracy(100.0)?.pp(),
|
.accuracy(98.0)
|
||||||
|
.calculate()
|
||||||
|
.pp(),
|
||||||
|
rosu()
|
||||||
|
.attributes(pp95.clone())
|
||||||
|
.accuracy(99.0)
|
||||||
|
.calculate()
|
||||||
|
.pp(),
|
||||||
|
rosu()
|
||||||
|
.attributes(pp95.clone())
|
||||||
|
.accuracy(100.0)
|
||||||
|
.calculate()
|
||||||
|
.pp(),
|
||||||
];
|
];
|
||||||
let objects = oppai.num_objects();
|
let objects = pp95.difficulty_attributes().max_combo().unwrap_or(0);
|
||||||
let stars = oppai.stars();
|
let stars = pp95.difficulty_attributes().stars();
|
||||||
Ok((BeatmapInfo { objects, stars }, pp))
|
Ok((BeatmapInfo { objects, stars }, pp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,40 +128,37 @@ impl BeatmapCache {
|
||||||
.await?
|
.await?
|
||||||
.bytes()
|
.bytes()
|
||||||
.await?;
|
.await?;
|
||||||
Ok(BeatmapContent {
|
let bm = BeatmapContent {
|
||||||
id,
|
id,
|
||||||
content: Arc::new(CString::new(content.into_iter().collect::<Vec<_>>())?),
|
content: Arc::new(Beatmap::parse(content.as_ref())?),
|
||||||
})
|
};
|
||||||
|
|
||||||
|
let mut bc = models::CachedBeatmapContent {
|
||||||
|
beatmap_id: id as i64,
|
||||||
|
cached_at: chrono::Utc::now(),
|
||||||
|
content: content.as_ref().to_owned(),
|
||||||
|
};
|
||||||
|
bc.store(&self.pool).await?;
|
||||||
|
Ok(bm)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_beatmap_db(&self, id: u64) -> Result<Option<BeatmapContent>> {
|
async fn get_beatmap_db(&self, id: u64) -> Result<Option<BeatmapContent>> {
|
||||||
Ok(models::CachedBeatmapContent::by_id(id as i64, &self.pool)
|
Ok(models::CachedBeatmapContent::by_id(id as i64, &self.pool)
|
||||||
.await?
|
.await?
|
||||||
.map(|v| BeatmapContent {
|
.map(|v| {
|
||||||
id,
|
Ok(BeatmapContent {
|
||||||
content: Arc::new(CString::new(v.content).unwrap()),
|
id,
|
||||||
}))
|
content: Arc::new(Beatmap::parse(&v.content[..])?),
|
||||||
}
|
}) as Result<_>
|
||||||
|
})
|
||||||
async fn save_beatmap(&self, b: &BeatmapContent) -> Result<()> {
|
.transpose()?)
|
||||||
let mut bc = models::CachedBeatmapContent {
|
|
||||||
beatmap_id: b.id as i64,
|
|
||||||
cached_at: chrono::Utc::now(),
|
|
||||||
content: b.content.as_ref().clone().into_bytes(),
|
|
||||||
};
|
|
||||||
bc.store(&self.pool).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a beatmap from the cache.
|
/// Get a beatmap from the cache.
|
||||||
pub async fn get_beatmap(&self, id: u64) -> Result<BeatmapContent> {
|
pub async fn get_beatmap(&self, id: u64) -> Result<BeatmapContent> {
|
||||||
match self.get_beatmap_db(id).await? {
|
match self.get_beatmap_db(id).await? {
|
||||||
Some(v) => Ok(v),
|
Some(v) => Ok(v),
|
||||||
None => {
|
None => self.download_beatmap(id).await,
|
||||||
let m = self.download_beatmap(id).await?;
|
|
||||||
self.save_beatmap(&m).await?;
|
|
||||||
Ok(m)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use super::{
|
||||||
use crate::{
|
use crate::{
|
||||||
discord::{
|
discord::{
|
||||||
display::ScoreListStyle,
|
display::ScoreListStyle,
|
||||||
oppai_cache::{BeatmapCache, OppaiAccuracy},
|
oppai_cache::{Accuracy, BeatmapCache},
|
||||||
BeatmapWithMode,
|
BeatmapWithMode,
|
||||||
},
|
},
|
||||||
models::{Mode, Mods, Score},
|
models::{Mode, Mods, Score},
|
||||||
|
@ -275,18 +275,9 @@ async fn show_leaderboard(
|
||||||
let mode = bm.1;
|
let mode = bm.1;
|
||||||
let oppai = data.get::<BeatmapCache>().unwrap();
|
let oppai = data.get::<BeatmapCache>().unwrap();
|
||||||
let oppai_map = oppai.get_beatmap(bm.0.beatmap_id).await?;
|
let oppai_map = oppai.get_beatmap(bm.0.beatmap_id).await?;
|
||||||
let get_oppai_pp = move |combo: u64, misses: u64, acc: OppaiAccuracy, mods: Mods| {
|
let get_oppai_pp = move |combo: u64, acc: Accuracy, mods: Mods| {
|
||||||
mode.to_oppai_mode().and_then(|mode| {
|
(if mode == Mode::Std { Some(mode) } else { None })
|
||||||
oppai_map
|
.and_then(|_| oppai_map.get_pp_from(Some(combo as usize), acc, mods).ok())
|
||||||
.get_pp_from(
|
|
||||||
oppai_rs::Combo::non_fc(combo as u32, misses as u32),
|
|
||||||
acc,
|
|
||||||
Some(mode),
|
|
||||||
mods,
|
|
||||||
)
|
|
||||||
.ok()
|
|
||||||
.map(|v| v as f64)
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let guild = m.guild_id.expect("Guild-only command");
|
let guild = m.guild_id.expect("Guild-only command");
|
||||||
|
@ -321,10 +312,11 @@ async fn show_leaderboard(
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
get_oppai_pp(
|
get_oppai_pp(
|
||||||
score.max_combo,
|
score.max_combo,
|
||||||
score.count_miss,
|
Accuracy::ByCount(
|
||||||
OppaiAccuracy::from_hits(
|
score.count_300,
|
||||||
score.count_100 as u32,
|
score.count_100,
|
||||||
score.count_50 as u32,
|
score.count_50,
|
||||||
|
score.count_miss,
|
||||||
),
|
),
|
||||||
score.mods,
|
score.mods,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use rosu_pp::GameMode;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -258,6 +259,17 @@ impl From<u8> for Mode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Mode> for GameMode {
|
||||||
|
fn from(n: Mode) -> Self {
|
||||||
|
match n {
|
||||||
|
Mode::Std => GameMode::STD,
|
||||||
|
Mode::Taiko => GameMode::TKO,
|
||||||
|
Mode::Catch => GameMode::CTB,
|
||||||
|
Mode::Mania => GameMode::MNA,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Mode {
|
impl fmt::Display for Mode {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
use Mode::*;
|
use Mode::*;
|
||||||
|
@ -275,15 +287,6 @@ impl fmt::Display for Mode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mode {
|
impl Mode {
|
||||||
/// Convert to oppai mode.
|
|
||||||
pub fn to_oppai_mode(self) -> Option<oppai_rs::Mode> {
|
|
||||||
Some(match self {
|
|
||||||
Mode::Std => oppai_rs::Mode::Std,
|
|
||||||
Mode::Taiko => oppai_rs::Mode::Taiko,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse from the display output of the enum itself.
|
/// Parse from the display output of the enum itself.
|
||||||
pub fn parse_from_display(s: &str) -> Option<Self> {
|
pub fn parse_from_display(s: &str) -> Option<Self> {
|
||||||
Some(match s {
|
Some(match s {
|
||||||
|
|
|
@ -136,9 +136,3 @@ impl fmt::Display for Mods {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Mods> for oppai_rs::Mods {
|
|
||||||
fn from(m: Mods) -> Self {
|
|
||||||
oppai_rs::Mods::from_bits_truncate(m.bits() as i32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue