MaxOhn / rosu-pp

PP and star calculation for all osu! gamemodes
MIT License
79 stars 40 forks source link

PP calculation from cached attributes not working in v0.10.0 #28

Closed Minoxs closed 3 months ago

Minoxs commented 9 months ago

Problem

I have been using rosu-pp to calculate scores in a project of mine, where I planned to calculate difficulty attributes once and reuse them constantly. This was working great up until v0.10.0.

pub fn get_pp(attr: DifficultyAttributes, score: CScore) -> f64 {
    Beatmap::default()
        .pp()
        .attributes(attr)
        .combo(score.combo)
        .n300(score.n300)
        .n100(score.n100)
        .n50(score.n050)
        .n_misses(score.misses)
        .calculate()
        .pp()
}

It always returns 0 now.

The only way to fix this is loading the beatmap file and calculating PP from there... It'd be great to have the option to calculate PP straight from the difficulty attributes again.

Difficulty

Difficulty was being calculated this way:

pub fn get_difficulty(bytes: &[u8], mods: u32, mode: GameMode) -> Result<DifficultyAttributes, String> {
    let parse = Beatmap::from_bytes(bytes);
    return match parse {
        Ok(map) => Ok(map.stars().mods(mods).mode(mode).calculate()),
        Err(why) => Err(why.to_string())
    };
}

Workaround

Changing it so that the pp calculation is done on the beatmap itself works just fine, only problem is that can't really cache the attributes and discard the map for future PP calculations.

pub extern "C" fn GetPPFromMap(bytes: &[u8], mods: u32, mode: GameMode, score: CScore) -> f64 {
    let parse = Beatmap::from_bytes(bytes);
    return match parse {
        Ok(map) => {
            map.pp()
               .mode(mode)
               .mods(mods)
               .combo(score.combo)
               .n300(score.n300)
               .n100(score.n100)
               .n50(score.n050)
               .n_misses(score.misses)
               .calculate()
               .pp()
        }
        Err(_) => { 0.0 }
    };
}

p.s: Example code has been slightly adapted from the real thing, as seen here https://github.com/Minoxs/crosu-pp

MaxOhn commented 9 months ago

Oh interesting, I've never considered calculating performance attributes only from difficulty attributes without the actual map, am kinda surprised it even worked 😄 It does definitely seem useful, will check on it.

MaxOhn commented 9 months ago

After some quick checkup I noticed that

So your initial approach should be good to use again if you also specify the amount of passed objects by calculating it via the difficulty attributes, for osu!standard that is.

The use-case is appealing so even though hitobject count isn't strictly needed in mania difficulty attributes, adding it is probably justified so that the map is no longer necessary for mania performance calc, and thus not needed for any mode as long as attributes and passed objects are specified.

Will work on it 👍

Minoxs commented 9 months ago

Awesome! This will be super useful 😄

MaxOhn commented 6 months ago

Finally finished the v1.0.0 release which properly supports performance calculation through either beatmap or attributes. 🎉

pub fn get_pp(attr: DifficultyAttributes, score: CScore) -> f64 {
    rosu_pp::Performance::new(attr) // `new` method accepts both, attributes or a beatmap
        .combo(score.combo)
        .n300(score.n300)
        .n100(score.n100)
        .n50(score.n050)
        .misses(score.misses) // `n_misses` -> `misses`
        .calculate()
        .pp()
}