return PerformanceAttributes instead of PpResult

This commit is contained in:
MaxOhn
2021-11-03 16:53:17 +01:00
parent b7caf35bb4
commit 5780559b70
16 changed files with 233 additions and 56 deletions
+2 -1
View File
@@ -1,6 +1,7 @@
## Upcoming
- added internal binary crate `pp-gen` to calculate difficulty & pp values via `PerformanceCalculator.dll`
- [BREAKING] Instead of returning `PpResult`, performance calculations now return `PerformanceAttributes` depending on the mode.
- osu: Updated up to commit [6944151486e677bfd11f2390163aca9161defbbf](https://github.com/ppy/osu/commit/6944151486e677bfd11f2390163aca9161defbbf) (2021-10-27)
# v0.2.3
@@ -33,7 +34,7 @@
## v0.2.0
- Async beatmap parsing through features `async_tokio` or `async_std` ([#1] - [@Pure-Peace])
- Hide various parsing related types further inwards, i.e. `rosu_pp::parse::some_type` instead of `rosu_pp::some_type`
- [BREAKING] Hide various parsing related types further inwards, i.e. `rosu_pp::parse::some_type` instead of `rosu_pp::some_type`
- Affected types: `DifficultyPoint`, `HitObject`, `Pos2`, `TimingPoint`, `HitObjectKind`, `PathType`, `HitSound`
## v0.1.1
+23 -4
View File
@@ -18,8 +18,6 @@ use crate::{
Beatmap, Mods, StarResult, Strains,
};
use std::convert::identity;
const SECTION_LENGTH: f32 = 750.0;
const STAR_SCALING_FACTOR: f32 = 0.153;
@@ -159,7 +157,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
}
HitObjectKind::Spinner { .. } | HitObjectKind::Hold { .. } => Some(None),
})
.filter_map(identity)
.flatten()
.flatten()
.take(take);
@@ -384,7 +382,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
}
HitObjectKind::Spinner { .. } | HitObjectKind::Hold { .. } => Some(None),
})
.filter_map(identity)
.flatten()
.flatten();
// Hyper dash business
@@ -602,3 +600,24 @@ pub struct DifficultyAttributes {
pub n_droplets: usize,
pub n_tiny_droplets: usize,
}
/// Various data created through the pp calculation.
#[derive(Clone, Debug, Default)]
pub struct PerformanceAttributes {
pub attributes: DifficultyAttributes,
pub pp: f32,
}
impl PerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f32 {
self.attributes.stars
}
/// Return the performance point value.
#[inline]
pub fn pp(&self) -> f32 {
self.pp
}
}
+1 -1
View File
@@ -57,7 +57,7 @@ impl Movement {
pub(crate) fn process(&mut self, current: &DifficultyObject) {
self.current_strain *= strain_decay(current.delta);
self.current_strain += self.strain_value_of(&current) * SKILL_MULTIPLIER;
self.current_strain += self.strain_value_of(current) * SKILL_MULTIPLIER;
self.current_section_peak = self.current_strain.max(self.current_section_peak);
self.prev_time.replace(current.start_time);
}
+16 -7
View File
@@ -1,4 +1,4 @@
use super::{stars, DifficultyAttributes};
use super::{stars, DifficultyAttributes, PerformanceAttributes};
use crate::{Beatmap, Mods, PpResult, StarResult};
/// Calculator for pp on osu!ctb maps.
@@ -241,7 +241,7 @@ impl<'m> FruitsPP<'m> {
/// Returns an object which contains the pp and [`DifficultyAttributes`](crate::fruits::DifficultyAttributes)
/// containing stars and other attributes.
pub fn calculate(mut self) -> PpResult {
pub fn calculate(mut self) -> PerformanceAttributes {
let attributes = self.attributes.take().unwrap_or_else(|| {
stars(self.map, self.mods, self.passed_objects)
.attributes()
@@ -310,10 +310,7 @@ impl<'m> FruitsPP<'m> {
pp *= 0.9;
}
PpResult {
pp,
attributes: StarResult::Fruits(attributes),
}
PerformanceAttributes { attributes, pp }
}
#[inline]
@@ -358,6 +355,13 @@ impl FruitsAttributeProvider for DifficultyAttributes {
}
}
impl FruitsAttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
Some(self.attributes)
}
}
impl FruitsAttributeProvider for StarResult {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
@@ -373,7 +377,12 @@ impl FruitsAttributeProvider for StarResult {
impl FruitsAttributeProvider for PpResult {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
self.attributes.attributes()
#[allow(irrefutable_let_patterns)]
if let Self::Fruits(attributes) = self {
Some(attributes.attributes)
} else {
None
}
}
}
+33 -9
View File
@@ -271,28 +271,28 @@ impl BeatmapExt for Beatmap {
panic!("`osu` feature is not enabled");
#[cfg(feature = "osu")]
OsuPP::new(self).mods(mods).calculate()
PpResult::Osu(OsuPP::new(self).mods(mods).calculate())
}
GameMode::MNA => {
#[cfg(not(feature = "mania"))]
panic!("`mania` feature is not enabled");
#[cfg(feature = "mania")]
ManiaPP::new(self).mods(mods).calculate()
PpResult::Mania(ManiaPP::new(self).mods(mods).calculate())
}
GameMode::TKO => {
#[cfg(not(feature = "taiko"))]
panic!("`osu` feature is not enabled");
#[cfg(feature = "taiko")]
TaikoPP::new(self).mods(mods).calculate()
PpResult::Taiko(TaikoPP::new(self).mods(mods).calculate())
}
GameMode::CTB => {
#[cfg(not(feature = "fruits"))]
panic!("`fruits` feature is not enabled");
#[cfg(feature = "fruits")]
FruitsPP::new(self).mods(mods).calculate()
PpResult::Fruits(FruitsPP::new(self).mods(mods).calculate())
}
}
}
@@ -406,22 +406,46 @@ impl StarResult {
/// Basic struct containing the result of a PP calculation.
#[derive(Clone, Debug)]
pub struct PpResult {
pub pp: f32,
pub attributes: StarResult,
pub enum PpResult {
#[cfg(feature = "fruits")]
Fruits(fruits::PerformanceAttributes),
#[cfg(feature = "mania")]
Mania(mania::PerformanceAttributes),
#[cfg(feature = "osu")]
Osu(osu::PerformanceAttributes),
#[cfg(feature = "taiko")]
Taiko(taiko::PerformanceAttributes),
}
impl PpResult {
/// The final pp value.
#[inline]
pub fn pp(&self) -> f32 {
self.pp
match self {
#[cfg(feature = "fruits")]
Self::Fruits(attributes) => attributes.pp,
#[cfg(feature = "mania")]
Self::Mania(attributes) => attributes.pp,
#[cfg(feature = "osu")]
Self::Osu(attributes) => attributes.pp,
#[cfg(feature = "taiko")]
Self::Taiko(attributes) => attributes.pp,
}
}
/// The final star value.
#[inline]
pub fn stars(&self) -> f32 {
self.attributes.stars()
match self {
#[cfg(feature = "fruits")]
Self::Fruits(attributes) => attributes.stars(),
#[cfg(feature = "mania")]
Self::Mania(attributes) => attributes.stars(),
#[cfg(feature = "osu")]
Self::Osu(attributes) => attributes.stars(),
#[cfg(feature = "taiko")]
Self::Taiko(attributes) => attributes.stars(),
}
}
}
+24 -1
View File
@@ -167,7 +167,30 @@ impl<'o> DifficultyHitObject<'o> {
/// Various data created through the star calculation.
/// This data is necessary to calculate PP.
#[derive(Clone, Debug, Default)]
#[derive(Copy, Clone, Debug, Default)]
pub struct DifficultyAttributes {
pub stars: f32,
}
/// Various data created through the pp calculation.
#[derive(Copy, Clone, Debug, Default)]
pub struct PerformanceAttributes {
pub attributes: DifficultyAttributes,
pub pp_acc: f32,
pub pp_strain: f32,
pub pp: f32,
}
impl PerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f32 {
self.attributes.stars
}
/// Return the performance point value.
#[inline]
pub fn pp(&self) -> f32 {
self.pp
}
}
+19 -5
View File
@@ -1,4 +1,4 @@
use super::{stars, DifficultyAttributes};
use super::{stars, DifficultyAttributes, PerformanceAttributes};
use crate::{Beatmap, Mods, PpResult, StarResult};
/// Calculator for pp on osu!mania maps.
@@ -90,7 +90,7 @@ impl<'m> ManiaPP<'m> {
}
/// Returns an object which contains the pp and stars.
pub fn calculate(self) -> PpResult {
pub fn calculate(self) -> PerformanceAttributes {
let stars = self
.stars
.unwrap_or_else(|| stars(self.map, self.mods, self.passed_objects).stars());
@@ -131,9 +131,11 @@ impl<'m> ManiaPP<'m> {
let pp = (strain_value.powf(1.1) + acc_value.powf(1.1)).powf(1.0 / 1.1) * multiplier;
PpResult {
PerformanceAttributes {
attributes: DifficultyAttributes { stars },
pp_acc: acc_value,
pp_strain: strain_value,
pp,
attributes: StarResult::Mania(DifficultyAttributes { stars }),
}
}
@@ -185,6 +187,13 @@ impl ManiaAttributeProvider for DifficultyAttributes {
}
}
impl ManiaAttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> Option<f32> {
Some(self.attributes.stars)
}
}
impl ManiaAttributeProvider for StarResult {
#[inline]
fn attributes(self) -> Option<f32> {
@@ -200,6 +209,11 @@ impl ManiaAttributeProvider for StarResult {
impl ManiaAttributeProvider for PpResult {
#[inline]
fn attributes(self) -> Option<f32> {
self.attributes.attributes()
#[allow(irrefutable_let_patterns)]
if let Self::Mania(attributes) = self {
Some(attributes.attributes.stars)
} else {
None
}
}
}
+1 -1
View File
@@ -65,7 +65,7 @@ impl Strain {
#[inline]
pub(crate) fn process(&mut self, current: &DifficultyHitObject) {
self.current_strain *= self.strain_decay(current.delta);
self.current_strain += self.strain_value_of(&current) * SKILL_MULTIPLIER;
self.current_strain += self.strain_value_of(current) * SKILL_MULTIPLIER;
self.current_section_peak = self.current_strain.max(self.current_section_peak);
self.prev_time.replace(current.start_time);
}
+25
View File
@@ -22,3 +22,28 @@ pub struct DifficultyAttributes {
pub stars: f32,
pub max_combo: usize,
}
/// Various data created through the pp calculation.
#[derive(Clone, Debug, Default)]
pub struct PerformanceAttributes {
pub attributes: DifficultyAttributes,
pub pp_acc: f32,
pub pp_aim: f32,
pub pp_flashlight: f32,
pub pp_speed: f32,
pub pp: f32,
}
impl PerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f32 {
self.attributes.stars
}
/// Return the performance point value.
#[inline]
pub fn pp(&self) -> f32 {
self.pp
}
}
+27 -10
View File
@@ -1,4 +1,4 @@
use super::DifficultyAttributes;
use super::{DifficultyAttributes, PerformanceAttributes};
use crate::{Beatmap, Mods, PpResult, StarResult};
/// Calculator for pp on osu!standard maps.
@@ -238,21 +238,21 @@ impl<'m> OsuPP<'m> {
/// Returns an object which contains the pp and [`DifficultyAttributes`](crate::osu::DifficultyAttributes)
/// containing stars and other attributes.
#[cfg(feature = "no_leniency")]
pub fn calculate(self) -> PpResult {
pub fn calculate(self) -> PerformanceAttributes {
self.calculate_with_func(super::no_leniency::stars)
}
/// Returns an object which contains the pp and [`DifficultyAttributes`](crate::osu::DifficultyAttributes)
/// containing stars and other attributes.
#[cfg(feature = "no_sliders_no_leniency")]
pub fn calculate(self) -> PpResult {
pub fn calculate(self) -> PerformanceAttributes {
self.calculate_with_func(super::no_sliders_no_leniency::stars)
}
/// Returns an object which contains the pp and [`DifficultyAttributes`](crate::osu::DifficultyAttributes)
/// containing stars and other attributes.
#[cfg(feature = "all_included")]
pub fn calculate(self) -> PpResult {
pub fn calculate(self) -> PerformanceAttributes {
self.calculate_with_func(super::all_included::stars)
}
@@ -262,14 +262,14 @@ impl<'m> OsuPP<'m> {
feature = "no_sliders_no_leniency",
feature = "all_included"
)))]
pub(crate) fn calculate(self) -> PpResult {
pub(crate) fn calculate(self) -> PerformanceAttributes {
unreachable!()
}
fn calculate_with_func(
mut self,
stars_func: impl FnOnce(&Beatmap, u32, Option<usize>) -> StarResult,
) -> PpResult {
) -> PerformanceAttributes {
if self.attributes.is_none() {
let attributes = stars_func(self.map, self.mods, self.passed_objects)
.attributes()
@@ -316,9 +316,14 @@ impl<'m> OsuPP<'m> {
.powf(1.0 / 1.1)
* multiplier;
let attributes = StarResult::Osu(self.attributes.unwrap());
PpResult { pp, attributes }
PerformanceAttributes {
attributes: self.attributes.unwrap(),
pp_acc: aim_value,
pp_aim: aim_value,
pp_flashlight: flashlight_value,
pp_speed: speed_value,
pp,
}
}
fn compute_aim_value(&self, total_hits: f32) -> f32 {
@@ -563,6 +568,13 @@ impl OsuAttributeProvider for DifficultyAttributes {
}
}
impl OsuAttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
Some(self.attributes)
}
}
impl OsuAttributeProvider for StarResult {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
@@ -578,7 +590,12 @@ impl OsuAttributeProvider for StarResult {
impl OsuAttributeProvider for PpResult {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
self.attributes.attributes()
#[allow(irrefutable_let_patterns)]
if let Self::Osu(attributes) = self {
Some(attributes.attributes)
} else {
None
}
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ enum OsuObjectKind {
}
impl OsuObject {
#[allow(clippy::clippy::too_many_arguments)]
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
h: &HitObject,
map: &Beatmap,
@@ -61,7 +61,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
let clock_rate = attributes.clock_rate;
let mut max_combo = 0;
let mut state = SliderState::new(&map);
let mut state = SliderState::new(map);
let mut hit_objects = map
.hit_objects
@@ -76,7 +76,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
HitObjectKind::Slider {
pixel_len, repeats, ..
} => {
max_combo += state.count_ticks(h.start_time, *pixel_len, *repeats, &map);
max_combo += state.count_ticks(h.start_time, *pixel_len, *repeats, map);
OsuObject::from(h, clock_rate)
}
+14 -5
View File
@@ -46,13 +46,13 @@ impl<'m> AnyPP<'m> {
pub fn calculate(self) -> PpResult {
match self {
#[cfg(feature = "fruits")]
Self::Fruits(f) => f.calculate(),
Self::Fruits(f) => PpResult::Fruits(f.calculate()),
#[cfg(feature = "mania")]
Self::Mania(m) => m.calculate(),
Self::Mania(m) => PpResult::Mania(m.calculate()),
#[cfg(feature = "osu")]
Self::Osu(o) => o.calculate(),
Self::Osu(o) => PpResult::Osu(o.calculate()),
#[cfg(feature = "taiko")]
Self::Taiko(t) => t.calculate(),
Self::Taiko(t) => PpResult::Taiko(t.calculate()),
}
}
@@ -272,6 +272,15 @@ impl AttributeProvider for StarResult {
impl AttributeProvider for PpResult {
#[inline]
fn attributes(self) -> StarResult {
self.attributes
match self {
#[cfg(feature = "fruits")]
Self::Fruits(f) => StarResult::Fruits(f.attributes),
#[cfg(feature = "mania")]
Self::Mania(m) => StarResult::Mania(m.attributes),
#[cfg(feature = "osu")]
Self::Osu(o) => StarResult::Osu(o.attributes),
#[cfg(feature = "taiko")]
Self::Taiko(t) => StarResult::Taiko(t.attributes),
}
}
}
+24 -1
View File
@@ -261,7 +261,30 @@ fn norm(p: f32, a: f32, b: f32, c: f32) -> f32 {
/// Various data created through the star calculation.
/// This data is necessary to calculate PP.
#[derive(Clone, Debug, Default)]
#[derive(Copy, Clone, Debug, Default)]
pub struct DifficultyAttributes {
pub stars: f32,
}
/// Various data created through the pp calculation.
#[derive(Copy, Clone, Debug, Default)]
pub struct PerformanceAttributes {
pub attributes: DifficultyAttributes,
pub pp: f32,
pub pp_acc: f32,
pub pp_strain: f32,
}
impl PerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f32 {
self.attributes.stars
}
/// Return the performance point value.
#[inline]
pub fn pp(&self) -> f32 {
self.pp
}
}
+20 -6
View File
@@ -1,4 +1,4 @@
use super::{stars, DifficultyAttributes};
use super::{stars, DifficultyAttributes, PerformanceAttributes};
use crate::{Beatmap, Mods, PpResult, StarResult};
/// Calculator for pp on osu!taiko maps.
@@ -136,7 +136,7 @@ impl<'m> TaikoPP<'m> {
}
/// Returns an object which contains the pp and stars.
pub fn calculate(mut self) -> PpResult {
pub fn calculate(mut self) -> PerformanceAttributes {
let stars = self
.stars
.unwrap_or_else(|| stars(self.map, self.mods, self.passed_objects).stars());
@@ -176,9 +176,11 @@ impl<'m> TaikoPP<'m> {
let pp = (strain_value.powf(1.1) + acc_value.powf(1.1)).powf(1.0 / 1.1) * multiplier;
PpResult {
PerformanceAttributes {
attributes: DifficultyAttributes { stars },
pp,
attributes: StarResult::Taiko(DifficultyAttributes { stars }),
pp_acc: acc_value,
pp_strain: strain_value,
}
}
@@ -253,11 +255,18 @@ impl TaikoAttributeProvider for DifficultyAttributes {
}
}
impl TaikoAttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> Option<f32> {
Some(self.attributes.stars)
}
}
impl TaikoAttributeProvider for StarResult {
#[inline]
fn attributes(self) -> Option<f32> {
#[allow(irrefutable_let_patterns)]
if let StarResult::Taiko(attributes) = self {
if let Self::Taiko(attributes) = self {
Some(attributes.stars)
} else {
None
@@ -268,6 +277,11 @@ impl TaikoAttributeProvider for StarResult {
impl TaikoAttributeProvider for PpResult {
#[inline]
fn attributes(self) -> Option<f32> {
self.attributes.attributes()
#[allow(irrefutable_let_patterns)]
if let Self::Taiko(attributes) = self {
Some(attributes.attributes.stars)
} else {
None
}
}
}
+1 -2
View File
@@ -50,8 +50,7 @@ impl Skill {
#[inline]
pub(crate) fn process(&mut self, current: &DifficultyObject, cheese: &[bool]) {
self.current_strain *= self.strain_decay(current.delta);
self.current_strain +=
self.kind.strain_value_of(&current, cheese) * self.skill_multiplier();
self.current_strain += self.kind.strain_value_of(current, cheese) * self.skill_multiplier();
self.current_section_peak = self.current_section_peak.max(self.current_strain);
self.prev_time.replace(current.start_time);
}