MCPcopy Index your code
hub / github.com/MaxOhn/rosu-pp

github.com/MaxOhn/rosu-pp @v4.0.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v4.0.1 ↗ · + Follow
1,409 symbols 4,155 edges 189 files 342 documented · 24%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

crates.io docs

rosu-pp

Library to calculate difficulty and performance attributes for all osu! gamemodes.

A large part of rosu-pp is a port of osu!lazer's difficulty and performance calculation with emphasis on a precise translation to Rust for the most accurate results while also providing a significant boost in performance.

Last commits of the ported code: - osu!lazer : 28c846b4d9366484792e27f4729cd1afa2cdeb66 (2025-10-13) - osu!tools : ab97b64f60901952926b2121ddffb8976d7f8775 (2025-10-16)

News posts of the latest updates: https://osu.ppy.sh/home/news/2025-10-29-performance-points-star-rating-updates

Usage

// Decode the map
let map = rosu_pp::Beatmap::from_path("./resources/2785319.osu").unwrap();

// Whereas osu! simply times out on malicious maps, rosu-pp does not. To
// prevent potential performance/memory issues, it is recommended to check
// beforehand whether a map is too suspicious for further calculation.
// Alternatively, using e.g. `checked_calculate` instead of `calculate`
// performs the same suspicion check before calculating.
if let Err(sus) = map.check_suspicion() {
    panic!("{sus:?}");
}

// Calculate difficulty attributes
let diff_attrs = rosu_pp::Difficulty::new()
    .mods(8 + 16) // HDHR
    .calculate(&map); // or `checked_calculate`

let stars = diff_attrs.stars();

// Calculate performance attributes
let perf_attrs = rosu_pp::Performance::new(diff_attrs)
    // To speed up the calculation, we used the previous attributes.
    // **Note** that this should only be done if the map and all difficulty
    // settings stay the same, otherwise the final attributes will be incorrect!
    .mods(24) // HDHR, must be the same as before
    .combo(789)
    .accuracy(99.2)
    .misses(2)
    .calculate();

let pp = perf_attrs.pp();

// Again, we re-use the previous attributes for maximum efficiency.
let max_pp = perf_attrs.performance()
    .mods(24) // Still the same
    .calculate()
    .pp();

println!("Stars: {stars} | PP: {pp}/{max_pp}");

Gradual calculation

Gradually calculating attributes provides an efficient way to process each hitobject separately and calculate the attributes only up to that point.

For difficulty attributes, there is GradualDifficulty which implements Iterator and for performance attributes there is GradualPerformance which requires the current score state.

use rosu_pp::{Beatmap, GradualPerformance, Difficulty, any::ScoreState};

let map = Beatmap::from_path("./resources/1028484.osu").unwrap();

let mut gradual = Difficulty::new()
    .mods(16 + 64) // HRDT
    .clock_rate(1.2)
    .gradual_performance(&map);

let mut state = ScoreState::new(); // empty state, everything is on 0.

// The first 10 hitresults are 300s
for _ in 0..10 {
    state.n300 += 1;
    state.max_combo += 1;
    let attrs = gradual.next(state.clone()).unwrap();
    println!("PP: {}", attrs.pp());
}

// Fast-forward to the end
state.max_combo = ...
state.n300 = ...
state.n_katu = ...
...
let attrs = gradual.last(state).unwrap();
println!("PP: {}", attrs.pp());

Accuracy

rosu-pp was tested against millions of real scores and delivered values that matched osu!lazer perfectly down to the last decimal place.

However, there is one small caveat: the values are only this precise on debug mode. On release mode, Rust's compiler performs optimizations that produce the tiniest discrepancies due to floating point inaccuracies. With this in mind, rosu-pp is still as accurate as can be without targeting the .NET compiler itself. Realistically, the inaccuracies in release mode are negligibly small.

Speed

An important factor for rosu-pp is the calculation speed. Optimizations and an accurate translation unfortunately don't always go hand-in-hand. Nonetheless, performance improvements are still snuck in wherever possible, providing a significantly faster runtime than the native C# code.

Results of a rudimentary benchmark of osu!lazer and rosu-pp:

osu!lazer:
Decoding maps:            Median: 325.18ms | Mean: 325.50ms
Calculating difficulties: Median: 568.63ms | Mean: 575.97ms
Calculating performances: Median: 256.00µs | Mean: 240.40µs

rosu-pp:
Decoding maps:            Median: 46.03ms | Mean: 47.13ms
Calculating difficulties: Median: 82.11ms | Mean: 84.27ms
Calculating performances: Median: 40.57µs | Mean: 43.41µs

Features

Flag Description Dependencies
default No features enabled
sync Some gradual calculation types can only be shared across threads if this feature is enabled. This feature adds a small performance penalty.
tracing Any error encountered during beatmap decoding will be logged through tracing::error. If this feature is not enabled, errors will be ignored. [tracing]

Bindings

Using rosu-pp from other languages than Rust: - JavaScript: rosu-pp-js - Python: rosu-pp-py

Extension points exported contracts — how you extend this code

HitResultGenerator (Interface)
Provides flexible hitresult generation based on mode and whether it should focus on performance, accuracy, or other fact [12 …
src/any/hitresult_generator.rs
IGameMode (Interface)
A way to specify a gamemode at compile-time. Notably, this is implemented for the marker types [`Osu`], [`Taiko`], [`Ca [4 …
src/model/mode.rs
AssertEq (Interface)
(no doc) [8 implementers]
tests/common.rs
IEnumerable (Interface)
Mimics the C# `IEnumerable ` interface. [1 implementers]
src/util/traits.rs
HasInterval (Interface)
(no doc) [2 implementers]
src/taiko/difficulty/utils/has_interval.rs
OsuStrainSkill (Interface)
(no doc) [2 implementers]
src/osu/difficulty/skills/strain.rs
IntoPerformance (Interface)
Turning a type into a performance calculator of any mode. [4 implementers]
src/any/performance/into.rs
Pending (Interface)
(no doc) [3 implementers]
src/model/beatmap/decode.rs

Core symbols most depended-on inside this repo

clone
called by 167
src/util/sync.rs
new
called by 128
src/osu/difficulty/gradual.rs
new
called by 120
src/mania/difficulty/gradual.rs
iter
called by 103
src/taiko/difficulty/object.rs
new
called by 100
src/taiko/difficulty/gradual.rs
get
called by 98
src/taiko/difficulty/object.rs
assert_eq_float
called by 75
tests/common.rs
new
called by 70
src/catch/difficulty/gradual.rs

Shape

Method 858
Function 346
Class 157
Enum 30
Interface 18

Languages

Rust100%

Modules by API surface

src/osu/performance/mod.rs45 symbols
src/any/performance/mod.rs37 symbols
src/mania/performance/mod.rs35 symbols
src/catch/performance/mod.rs35 symbols
src/any/difficulty/mod.rs34 symbols
src/taiko/performance/mod.rs32 symbols
src/model/beatmap/decode.rs31 symbols
src/osu/object.rs26 symbols
src/mania/performance/hitresult_generator/closest.rs23 symbols
src/osu/performance/hitresult_generator/ignore_acc.rs21 symbols
src/model/beatmap/attributes/mod.rs21 symbols
src/osu/performance/hitresult_generator/fast.rs20 symbols

For agents

$ claude mcp add rosu-pp \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact