From 170333825e213e982baf615ca55ac99ee438700c Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <27200811+hazuki-san@users.noreply.github.com> Date: Thu, 25 Apr 2024 18:52:32 +0000 Subject: [PATCH] rina ify --- .gitignore | 4 +- CHANGELOG.md | 355 ------------------------------- Cargo.toml | 10 +- README.md | 34 +-- maps/stream map.osu | 328 ++++++++++++++++++++++++++++ src/any/difficulty/converted.rs | 4 +- src/any/difficulty/gradual.rs | 2 +- src/any/difficulty/mod.rs | 2 +- src/any/performance/gradual.rs | 2 +- src/catch/difficulty/gradual.rs | 4 +- src/catch/performance/gradual.rs | 4 +- src/lib.rs | 32 +-- src/mania/difficulty/gradual.rs | 4 +- src/mania/performance/gradual.rs | 4 +- src/osu/difficulty/gradual.rs | 4 +- src/osu/performance/gradual.rs | 4 +- src/osu/performance/mod.rs | 125 ++++++++--- src/taiko/difficulty/gradual.rs | 4 +- src/taiko/performance/gradual.rs | 4 +- src/util/sync.rs | 2 +- tests/decode.rs | 2 +- tests/difficulty.rs | 2 +- tests/performance.rs | 2 +- 23 files changed, 490 insertions(+), 448 deletions(-) delete mode 100644 CHANGELOG.md create mode 100644 maps/stream map.osu diff --git a/.gitignore b/.gitignore index 86f29123..5558ec6d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ Cargo.lock output* /.idea -/tests/custom.rs \ No newline at end of file +/tests/custom.rs +examples/ +.vscode \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 37612017..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,355 +0,0 @@ -# v1.0.0 (2024-04-02) - -The `rosu-pp` interface and internal structure has been rewritten completely. Fields have been -modified, builders work differently, function arguments have changed, and some types are no longer -exposed. - -Additionally, osu!catch has finally been updated to match osu!lazer as closely as possible, just -like the other modes already did. - -- Beatmap converts - - Each mode now has a [ZST], i.e. `Osu`, `Taiko`, `Catch`, and `Mania`, which are used to specify - a mode at compile-time. Most of their utility comes from the new `IGameMode` trait. - - Each mode's calculators now require `Converted` beatmaps to make sure they're valid for the - mode, e.g. the `TaikoPerformance` calculator no longer takes a simple `Beatmap` but a - `Converted<'_, Taiko>` (or its alias `TaikoBeatmap<'_>`). - - Conversion between `Beatmap` and `Converted<'_, M>` either - - is essentially a free operation if the map's mode already matches `M`, - - or performs the required conversion by modifying hitobjects & co, - - or indicates an error due to incompatible mode conversion, e.g. a mania map cannot be - converted to a taiko map -- Difficulty calculation - - The `Difficulty` type is the core of all difficulty calculations and acts as a builder to - specify various parameters. - - To calculate values for a specific mode, the method `Difficulty::with_mode` will produce a - builder to calculate values for that mode. - - Recycled attributes are only valid if *all* difficulty parameters match, i.e. it must be on - the same map, use the same mods, clock rate, custom beatmap attributes, and passed object count - (and hardrock offset for osu!catch). - - Clockrate is now always clamped between 0.01 and 100 and custom beatmap attributes are clamped - between -20 and 20. -- Performance calculation - - Each mode's performance calculator was renamed from `[Mode]PP` to `[Mode]Performance` and - `AnyPP` is now called `Performance`. - - Difficulty attributes now contain a few more values so that the beatmap is no longer necessary - for performance calculation as long as difficulty attributes are available. - - The functions `[Mode]Performance::new` now take an argument implementing the trait - `Into(Mode)Performance`, i.e. either a beatmap (as before), or attributes (difficulty or - performance). Since attributes speed up the calculation, they should be prefered whenever - available. However, be mindful that they have been calculated for the same map and difficulty - settings. Otherwise, the final attributes will be incorrect. -- Features - - The `tracing` feature has been added. Its sole functionality is that errors during beatmap - parsing will emit a `tracing` event on the `ERROR` level. If this features is not explicitely - enabled, parsing errors will be ignored entirely. - - The `gradual` features has been removed. Types for gradual calculation are now always available. - - The `sync` feature has been added. Taiko's gradual calculation types contain types that are not - thread-safe by default. Enabling this feature will add some performance penalty but use types - that *do* allow moving gradual calculation across threads. - - The `compact_strains` feature is now enabled by default and causes strain values during - difficulty calculation to be stored in a space-efficient way to prevent out-of-memory issues on - maliciously long maps. This comes at a small performance cost. -- Misc - - Async is no longer supported. Beatmap parsing now works through [rosu-map] - which does not support async since evidently it's generally slower than regular sequential code. - - Errors while *parsing* a beatmap will never be propagated. The only errors that will be - propagated are those occuring while *decoding*, e.g. a file couldn't be read or other IO errors. - Notably, this means that some content is now parsed successfully into a `Beatmap` whereas in - previous `rosu-pp` versions it would error, e.g. an empty file is now a valid `Beatmap`. - - Although new lazer mods such as `DifficultyAdjust` or `DoubleTime` with a custom clockrate are - essentially supported by providing methods to specify their parameters, mods themselves are still - specified by their bit value. This means: - - `Daycore` will not be considered unless its clockrate change is explicitly specified - - /!\\ `Blinds` will not be considered for osu! performance calculation /!\\ - - Most `usize` types are now `u32`, e.g. fields of `ScoreState` or `max_combo` in attributes. - - `n_misses` has generally been renamed to `misses` for both fields and methods. - - Types such as `NestedObjectKind`, `ManiaObject`, or `Mods` that were only used for internal - calculations are no longer publicly exposed. - -# v0.10.0 (2023-11-19) - -Essentially only adjustments to the API so bindings won't need an update. - -- __Additions:__ - - Added `From` impl for `GameMode` - - Added the method `AnyPP::hitresult_priority` - - Added the method `[Mode]PP::generate_state` which returns the score state that will be used for performance calculation ([#23]) - - The struct `SortedVec` has now an improved public interface so it can be constructed and pushed onto ([#22]) - -- __Breaking adjustments:__ - - Removed the method `HitObject::end_time` from the public api. ([#25]) - - The fields `control_points` and `edge_sounds` of `HitObjectKind::Slider` are now stored in a `Box` rather than a `Vec`. ([#26]) - - Overhauled gradual calculation. All relevant types are now gated behind the `gradual` feature which must be enabled. ([#24]) - - `*GradualDifficultyAttributes` has been renamed to `*GradualDifficulty` and `*GradualPerformanceAttributes` - has been renamed to `*GradualPerformance`. - - Types for gradual calculation that depend on a lifetime now have a counterpart without a lifetime that might clone - underlying data along the way. E.g. now there is `CatchOwnedGradualDifficulty` and `[Mode]OwnedGradualPerformance`. - - `OsuGradualDifficulty` and thus `GradualDifficulty` no longer implement `Clone`. - - Gradual performance calculators' method `process_next_object` has been renamed to `next` and `process_next_n_objects` - has been renamed to `nth`. They now also have the new method `last`. - - Similar to `Iterator::nth`, gradual performance calculators' method `nth` is now zero-indexed i.e. passing `n=0` - will process 1 object, `n=1` will process 2, and so on. - -## v0.9.5 (2023-09-06) - -- __Additions:__ - - Added the method `{AnyStars/AnyPP}::is_convert` which **needs** to be used if the map was manually converted beforehand - -- __Adjustments:__ - - Specified clock rates can go below 0.001 again - -- __Fixes:__ - - Fixed underflow for osu!std scores that were FCs but quit mid-slider - - Fixed panic on incorrect file headers ([#21]) - -## v0.9.4 (2023-02-09) - -- __Additions:__ - - Added the method `{TaikoPP/ManiaPP}::is_convert` which **needs** to be used if the map was manually converted beforehand - -- __Adjustments:__ - - Specified clock rates can no longer go below 0.001 to prevent crashing due to memory hogging. - - (technically breaking) The only reasons for parsing to fail are now IO errors or invalid file headers. All other `ParseError` variants have been removed and instead of throwing an error the line is just ignored. - -- __Fixes:__ - - The `Beatmap::bpm` method now works properly by considering the most common beat length instead of just the first one - -## v0.9.3 (2023-01-28) - -- __Additions:__ - - Added the method `ScoreState::total_hits` - - Added the trait methods `BeatmapExt::{mode}_hitobjects` which return a list of mode-specific processed `HitObject`s ([#20]) - -- __Fixes:__ - - Lines with invalid curve points are now ignored instead of throwing an error while parsing - - Fixed a niche capacity overflow in curve generation - -## v0.9.2 (2022-11-08) - -- __Adjustments:__ - - When passing an osu!std map to `TaikoGradualDifficultyAttributes` or `ManiaGradualDifficultyAttributes`, it now automatically converts the map internally. For osu!catch it was already happening trivially. - -- __Fixes:__ - - Fixed passed object count for taiko by ignoring non-circles - - Fixed a niche panic on UTF-16 encoded maps ([#18]) - - Fixed an occasional underflow when calculating accuracy pp - - Fixed an infinite loop on special ctb maps - -## v0.9.1 (2022-10-26) - -- __Adjustments:__ - - When passing an osu!std map to `TaikoPP` or `ManiaPP`, it now automatically converts the map internally. For osu!catch it was already happening trivially. - -- __Fixes:__ - - The fields `section_len` for all strain structs no longer depends on the clock rate. - -## v0.9.0 (2022-10-24) - -Big changes including the most recent [osu!](https://osu.ppy.sh/home/news/2022-09-30-changes-to-osu-sr-and-pp), [taiko](https://osu.ppy.sh/home/news/2022-09-28-changes-to-osu-taiko-sr-and-pp), and [mania](https://osu.ppy.sh/home/news/2022-10-09-changes-to-osu-mania-sr-and-pp) updates, aswell as various breaking changes. - -- __Breaking changes:__ - - `TimingPoint` and `DifficultyPoint` no longer contain a `kiai` field - - `DifficultyPoint` now has the additional fields `bpm_mult` and `generate_ticks` - - `Beatmap` now stores timing- and difficulty points in a `SortedVec` - - `Beatmap` now has the additional field `effect_points` - - For the performance calculators `OsuPP`, `TaikoPP`, `ManiaPP`, and `AnyPP` the method `misses` has been renamed to `n_misses` - - The accuracy method for `OsuPP`, `TaikoPP`, and `ManiaPP` is no longer required to be called last - - `ManiaPP` no longer has a `score` method. Instead it has `n320`, `n300`. `n200`, `n100`, `n50`, and `n_misses` methods, aswell as a `state` method - - Gradual performance calculation for mania now requires a `ManiaScoreState` instead of `score` - - `ManiaDifficultyAttributes` now have a `max_combo` field and method - - `OsuDifficultyAttributes` now have a `speed_note_count` field - - `OsuPerformanceAttributes` and `TaikoPerformanceAttributes` now have a `effective_miss_count` field - - `TaikoDifficultyAttributes` now have a `peak` and `hit_window` field - - Some other things I likely forgot about :S - -- __Additions:__ - - The performance calculators `OsuPP`, `TaikoPP`, `ManiaPP`, and `AnyPP` now have a `hitresult_priority` method to specify how hitresults should be generated - -- __Fixes:__ - - Fixed a bunch of fringe yet significant bugs for taiko and mania converts - - Fixed various floating point inaccuracies for osu!standard - - Fixed parsing difficulty points from .osu files - - Instead of throwing an error, invalid lines during parsing will just be ignored in some cases - - Fixed an unsafe transmute between incompatible types while parsing sliders - -## v0.8.0 (2022-08-02) - -- __Fixes:__ - - Fixed stack overflow bug when allocating ticks for some sliders on converted catch maps ([#14]) -- __Breaking changes:__ - - `Beatmap::attributes` now returns a new type `BeatmapAttributesBuilder` to allow for more - fine-grained calculations. `BeatmapAttributes` now contains expected values and also includes - a `BeatmapHitWindows` field containing the AR (preempt) and OD (great) hit windows in - milliseconds. ([#15]) - -## v0.7.1 (2022-07-12) - -- __Fixes:__ - - Parsing edge sounds is now mindful about overflowing a byte (ref. ranked map id 80799) - - Parsing the event section now attempts to read non-ASCII before eventually failing (ref. ranked map id 49374) - -## v0.7.0 (2022-07-06) - -- __Fixes:__ - - Slider velocity is now adjusted properly for taiko converts - - Fixed missing slider sounds for taiko converts -- __Breaking changes:__ - - Replaced the simple `Strains` struct with a new struct `{Mode}Strains` that contains more detail w.r.t. the mode. - - Renamed all `GameMode` variants to more idiomatic names - - Renamed `ParseError::IOError` to `ParseError::IoError` - -## v0.6.0 (2022-07-05) - -- __Additions__: - - Added the `ControlPoint` and `ControlPointerIter` types to the public interface - - `TimingPoint` and `DifficultyPoint` now implement `Default` - - Added new methods to `Beatmap`: - - `convert_mode`: Convert a map into another mode. (doesn't do anything if the starting map is not osu!standard) - - `control_points`: Return an iterator over all control points of a map - - `total_break_time`: Return the accumulated break time in milliseconds - - `timing_point_at`: Return the timing point for the given timestamp - - `difficulty_point_at`: Return the difficulty point for the given timestamp if available -- __Breaking changes:__ - - Moved some types to a different module. The following types can now be found in `rosu_pp::beatmap`: - - `Beatmap` - - `BeatmapAttributes` - - `ControlPoint` - - `ControlPointIter` - - `DifficultyPoint` - - `GameMode` - - `TimingPoint` - - Added a new field `kiai: bool` to both `TimingPoint` and `DifficultyPoint` to denote whether the current timing section is in kiai mode - - Added a new field `breaks: Vec` to `Beatmap` that contains all breaks throughout the map - - Added a new field `edge_sounds: Vec` to the `Slider` variant of `HitObjectKind` to denote the sample played on slider heads, ends, and repeats -- __Other:__ - - Small performance improvements for osu!taiko calculations - -## v0.5.2 (2022-06-14) - -- __Fixes:__ - - Fixed parsing non-UTF-8 encoded files and improved parse performance overall ([#9]) - - Handle missing approach rate properly this time - -## v0.5.1 (2022-03-21) - -- __Fixes:__ - - Performance calculation for taiko & mania now considers custom clock rates properly - -## v0.5.0 (2022-03-21) - -- __Fixes:__ - - Fixed panic on maps with 0 objects - - Fixed droplet timings on juicestreams with span count >1 - - Fixed timing point parsing on some (older) maps where "uninherited" value did not coincide with beat length - - Fixed handling .osu files with missing difficulty attributes - - Fixed huge memory allocations caused by incorrectly parsing .osu files -- __Breaking changes:__ - - The `stars` and `strains` functions for all modes were removed. Instead use the `{Mode}Stars` builder pattern which is similar to `{Mode}PP`. - - `BeatmapExt::stars`'s definition was adjusted to use the `AnyStars` builder struct - - Store `HitObject::sound` in `Beatmap::sounds` instead to reduce the struct size - - Removed the mode features `osu`, `fruits`, `taiko`, and `mania`. Now all modes are always supported. - - Renamed the `rosu_pp::fruits` module to `rosu_pp::catch`. Similarly, all structs `Fruits{Name}` were renamed to `Catch{Name}` and enums over the mode have their `Fruits` variant renamed to `Catch` - - Renamed `Mods`' method `speed` to `clock_rate` -- __Additions:__ - - Added `AttributeProvider` impl for `{Mode}PerformanceAttributes` - - Added the method `clock_rate` to `{Mode}PP` and `{Mode}Stars` to consider a custom clock rate instead of the one dictated by mods. - -## v0.4.0 (2021-11-25) - -- Fixed out of bounds panic on maps with single-control-point linear sliders -- Fixed incorrect attributes on maps with only 1 or 2 hit objects for all modes -- Added method `Beatmap::from_path` so the file does not have to be created manually for `Beatmap::parse`. -- Added a bunch of documentation. -- Added method `Beatmap::bpm` -- Added method `max_combo` for `DifficultyAttributes`, `PerformanceAttributes`, and all `{Mode}PerformanceAttributes` -- Added methods `TaikoDifficultyAttributes::max_combo` and `OsuDifficultyAttributes::max_combo` -- Added structs `{Mode}GradualDifficultyAttributes` to calculate a map's difficulty after every or every few objects instead of calling the mode's `stars` function over and over. -- Added structs `{Mode}GradualPerformanceAttributes` to calculate the performance on a map after every or every few objects instead of using `{Mode}PP` over and over. -- Added `BeatmapExt::gradual_difficulty` and `BeatmapExt::gradual_performance` to gradually calculate the difficulty or performance on maps of any mode, hit object by hit object. -- Added methods `{Mode}PP::state` that take a `{Mode}ScoreState` (same for `AnyPP` and `ScoreState`) to set all parameters at once. -- [BREAKING] Removed the `ParseError` variants `InvalidPathType` and `InvalidTimingSignature` and renamed `InvalidFloatingPoint` to `InvalidDecimalNumber`. -- [BREAKING] Removed the `last_control_point` field of `HitObjectKind::Slider` when neither the `osu` nor the `fruits` feature is enabled. -- [BREAKING] Added the field `TaikoDifficultyAttributes::max_combo` -- [BREAKING] Renamed the `attributes` field to `difficulty` for all `{Mode}PerformanceAttributes` structs -- [BREAKING] Replaced field `FruitsDifficultyAttributes::max_combo` by a method with the same name - -## v0.3.0 (2021-11-14) - -- [BREAKING] With the importance of sliders for osu!standard, the `no_sliders_no_leniency` feature became too inaccurate. Additionally, since considering sliders now inherently drags performance down a little more, the difference between `no_leniency` and `all_included` became too small. Hence, the three osu features `no_sliders_no_leniency`, `no_leniency`, and `all_included` were removed. When the `osu` feature is enabled, it will now essentially use `all_included` under the hood. - Additionally, instead of importing through `rosu_pp::osu::{version}`, you now have to import through `rosu_pp::osu`. -- [BREAKING] Instead of returning `PpResult`, performance calculations now return `{Mode}PerformanceAttributes` and `PpResult` has been renamed to `PerformanceAttributes`. -- [BREAKING] Instead of returning `StarResult`, difficulty calculations now return `{Mode}DifficultyAttributes` and `StarResult` has been renamed to `DifficultyAttributes`. -- [BREAKING] Various fields and methods now include `f64` instead of `f32` to stay true to osu!'s original code -- Added internal binary crate `pp-gen` to calculate difficulty & pp values via `PerformanceCalculator.dll` -- Added internal binary crate `pp-plot` to plot out differences between `pp-gen`'s output and `rosu-pp` values -- osu: Updated up to commit [9fb2402781ad91c197d51aeec716b0000f52c4d1](https://github.com/ppy/osu/commit/9fb2402781ad91c197d51aeec716b0000f52c4d1) (2021-11-12) - -## v0.2.3 (2021-08-09) - -- Reduced amount of required features of `async_std` and `async_tokio` -- Fixed a panic for some mania difficulty calculations on converts -- Updated the difficulty & pp changes from 21.07.27 -- Fixed dead loop when reading empty `.osu` files ([#2] - [@Pure-Peace]) -- Updated osu's clockrate bugfix for all modes - -## v0.2.2 (2021-05-05) - -- osu & fruits: - - Fixed specific slider patterns - - Optimized Bezier, Catmull, and other small things - - Benchmarking for osu!standard showed a 25%+ improvement for performance aswell as accuracy - -- fruits: - - Fixed tick timing for reverse sliders - -- taiko: - - Micro optimizations - -## v0.2.1 (2021-04-17) - -- parse & osu: - - Cleanup and tiny optimizations - -## v0.2.0 (2021-02-25) - -- Async beatmap parsing through features `async_tokio` or `async_std` ([#1] - [@Pure-Peace]) -- [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 (2021-02-15) - -- parse: - - Efficiently handle huge amounts of curvepoints - -- osu: - - Fixed panic on unwrapping unavailable hit results - - Fixed occasional underflow when calculating pp with passed_objects - -- taiko: - - Fixed missing flooring of hitwindow for pp calculation - -- fruits: - - Fixed passed objects in star calculation - -- mania: - - Fixed pp calculation on HR - -[@Pure-Peace]: https://github.com/Pure-Peace - -[#1]: https://github.com/MaxOhn/rosu-pp/pull/1 -[#2]: https://github.com/MaxOhn/rosu-pp/pull/2 -[#9]: https://github.com/MaxOhn/rosu-pp/pull/9 -[#14]: https://github.com/MaxOhn/rosu-pp/pull/14 -[#15]: https://github.com/MaxOhn/rosu-pp/pull/15 -[#18]: https://github.com/MaxOhn/rosu-pp/pull/18 -[#20]: https://github.com/MaxOhn/rosu-pp/pull/20 -[#21]: https://github.com/MaxOhn/rosu-pp/pull/21 -[#22]: https://github.com/MaxOhn/rosu-pp/pull/22 -[#23]: https://github.com/MaxOhn/rosu-pp/pull/23 -[#24]: https://github.com/MaxOhn/rosu-pp/pull/24 -[#25]: https://github.com/MaxOhn/rosu-pp/pull/25 -[#26]: https://github.com/MaxOhn/rosu-pp/pull/26 - -[ZST]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts -[rosu-map]: https://github.com/MaxOhn/rosu-map \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 732afb4e..9ff9680b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,11 @@ [package] -name = "rosu-pp" -version = "1.0.0" +name = "rina-pp" +version = "0.9.24" edition = "2021" -authors = ["MaxOhn "] +authors = ["MaxOhn ", "Simon G."] license = "MIT" readme = "README.md" -repository = "https://github.com/MaxOhn/rosu-pp" -documentation = "https://docs.rs/rosu-pp/" +repository = "https://github.com/osuthailand/rina-pp" description = "Difficulty and performance calculation for osu!" keywords = ["osu", "pp", "stars", "performance", "difficulty"] @@ -18,6 +17,7 @@ tracing = ["rosu-map/tracing"] [dependencies] rosu-map = { version = "0.1.1" } +mysql = "*" [dev-dependencies] proptest = "1.4.0" diff --git a/README.md b/README.md index 741aa511..0c5c0925 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -[![crates.io](https://img.shields.io/crates/v/rosu-pp.svg)](https://crates.io/crates/rosu-pp) [![docs](https://docs.rs/rosu-pp/badge.svg)](https://docs.rs/rosu-pp) +[![crates.io](https://img.shields.io/crates/v/rina-pp.svg)](https://crates.io/crates/rina-pp) [![docs](https://docs.rs/rina-pp/badge.svg)](https://docs.rs/rina-pp) -# rosu-pp +# rina-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 +A large part of `rina-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](#accuracy) while also providing a significant [boost in performance](#speed). @@ -24,17 +24,17 @@ News posts of the latest gamemode updates: ```rust // Decode the map -let map = rosu_pp::Beatmap::from_path("./resources/2785319.osu").unwrap(); +let map = rina_pp::Beatmap::from_path("./resources/2785319.osu").unwrap(); // Calculate difficulty attributes -let diff_attrs = rosu_pp::Difficulty::new() +let diff_attrs = rina_pp::Difficulty::new() .mods(8 + 16) // HDHR .calculate(&map); let stars = diff_attrs.stars(); // Calculate performance attributes -let perf_attrs = rosu_pp::Performance::new(diff_attrs) +let perf_attrs = rina_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! @@ -65,7 +65,7 @@ and for performance attributes there is `GradualPerformance` which requires the score state. ```rust -use rosu_pp::{Beatmap, GradualPerformance, Difficulty, any::ScoreState}; +use rina_pp::{Beatmap, GradualPerformance, Difficulty, any::ScoreState}; let map = Beatmap::from_path("./resources/1028484.osu").unwrap(); @@ -95,29 +95,29 @@ println!("PP: {}", attrs.pp()); ### Accuracy -`rosu-pp` was tested against all current beatmaps on multiple mod combinations and delivered +`rina-pp` was tested against all current beatmaps on multiple mod combinations 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 which can cascade into larger differences in the end. -With this in mind, `rosu-pp` is still as accurate as can be without targeting the +With this in mind, `rina-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 +An important factor for `rina-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: +Results of a rudimentary [benchmark] of osu!lazer and rina-pp: ```txt osu!lazer: Decoding maps: Median: 378.10ms | Mean: 381.47ms Calculating difficulties: Median: 588.89ms | Mean: 597.11ms Calculating performances: Median: 315.90µs | Mean: 310.60µs -rosu-pp: +rina-pp: Decoding maps: Median: 46.94ms | Mean: 47.21ms Calculating difficulties: Median: 72.90ms | Mean: 73.13ms Calculating performances: Median: 44.13µs | Mean: 45.53µs @@ -134,16 +134,16 @@ Calculating performances: Median: 44.13µs | Mean: 45.53µs ### Bindings -Using `rosu-pp` from other languages than Rust: -- JavaScript: [rosu-pp-js] -- Python: [rosu-pp-py] +Using `rina-pp` from other languages than Rust: +- JavaScript: [rina-pp-js] +- Python: [rina-pp-py] [osu!]: https://osu.ppy.sh/home [osu!lazer]: https://github.com/ppy/osu [osu!tools]: https://github.com/ppy/osu-tools [`tracing`]: https://docs.rs/tracing -[rosu-pp-js]: https://github.com/MaxOhn/rosu-pp-js -[rosu-pp-py]: https://github.com/MaxOhn/rosu-pp-py +[rina-pp-js]: https://github.com/MaxOhn/rina-pp-js +[rina-pp-py]: https://github.com/MaxOhn/rina-pp-py [benchmark]: https://gist.github.com/MaxOhn/625af10011f6d7e13a171b08ccf959ff diff --git a/maps/stream map.osu b/maps/stream map.osu new file mode 100644 index 00000000..67f1de2c --- /dev/null +++ b/maps/stream map.osu @@ -0,0 +1,328 @@ +osu file format v14 + +[General] +AudioFilename: audio.mp3 +AudioLeadIn: 0 +PreviewTime: 2122 +Countdown: 0 +SampleSet: Soft +StackLeniency: 0.3 +Mode: 0 +LetterboxInBreaks: 0 +WidescreenStoryboard: 1 + +[Editor] +Bookmarks: 23731,29133 +DistanceSpacing: 1.2 +BeatDivisor: 4 +GridSize: 4 +TimelineZoom: 2.3 + +[Metadata] +Title:TALALALALALALALALALALALAAAAA LALAAAAAAAAAAAAA +TitleUnicode:TALALALALALALALALALALALAAAAA LALAAAAAAAAAAAAA +Artist:Hoshimachi Suisei +ArtistUnicode:星街すいせい +Creator:KecHik445 +Version:Mahiru's Sounds Piercing The Heavenly Skies +Source: +Tags:Ceka Éclair eclair vtuber virtual youtuber hololive female vocal japanese jpop j-pop バーチャルyoutuber ホロライブ japanese pop anime meme れいるか hana87z Creatural gundam hathaway op opening 閃光 Senkou Alexandros NinjaWarotMC OnLoong KPMY _HaxOr_ iaport honigFNA actiol ezio Mahiru Shiina SEApodEErman bakery fujinn +BeatmapID:3632631 +BeatmapSetID:1742920 + +[Difficulty] +HPDrainRate:3.5 +CircleSize:4 +OverallDifficulty:10 +ApproachRate:9.7 +SliderMultiplier:1.8 +SliderTickRate:1 + +[Events] +//Background and Video events +0,0,"bg.png",0,0 +Video,880,"video.mp4" +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +106,288.115246098439,4,2,1,70,1,0 +1258,-133.333333333333,4,2,1,50,0,0 +1834,-95.2380952380952,4,2,1,70,0,0 +2410,-86.9565217391304,4,2,7,80,0,0 +10478,-86.9565217391304,4,2,8,80,0,0 +11198,-86.9565217391304,4,2,7,80,0,0 +15664,-80,4,2,7,80,0,0 +20850,-86.9565217391304,4,2,1,70,0,0 +21426,-86.9565217391304,4,2,1,80,0,0 +21714,-86.9565217391304,4,2,1,90,0,0 +22002,-86.9565217391304,4,2,1,85,0,0 +23155,-666.666666666667,4,2,1,70,0,0 +23731,-322.58064516129,4,2,1,70,0,0 + + +[Colours] +Combo1 : 63,120,254 +Combo2 : 192,192,192 +Combo3 : 162,206,247 + +[HitObjects] +48,336,1258,6,0,L|59:269,1,67.5000025749208,6|0,0:0|0:0,0:0:0:0: +145,285,1546,2,0,L|133:351,1,67.5000025749208,6|0,0:0|0:0,0:0:0:0: +40,96,1834,5,0,1:0:0:0: +86,92,1906,1,0,0:0:0:0: +129,111,1978,1,8,0:0:0:0: +158,148,2050,1,8,0:0:0:0: +187,184,2122,1,8,0:0:0:0: +229,203,2194,1,8,0:0:0:0: +276,200,2266,2,0,L|285:144,1,47.2499985580445,8|8,0:0|0:0,0:0:0:0: +352,68,2410,5,6,1:3:0:0: +385,43,2482,1,0,0:0:0:0: +426,40,2554,1,0,0:0:0:0: +464,57,2627,1,0,0:0:0:0: +487,91,2699,1,10,0:3:0:0: +491,132,2771,1,0,0:0:0:0: +474,169,2843,1,0,0:0:0:0: +440,194,2915,1,0,0:0:0:0: +399,197,2987,5,2,1:3:0:0: +362,161,3059,1,0,0:0:0:0: +312,149,3131,1,0,1:0:0:0: +263,165,3203,1,0,0:0:0:0: +229,203,3275,1,10,0:3:0:0: +195,241,3347,1,0,0:0:0:0: +146,257,3419,1,0,0:0:0:0: +96,245,3491,1,0,0:0:0:0: +59,209,3563,6,0,L|69:149,3,51.7500009870529,2|0|0|0,1:3|0:0|0:0|0:0,0:0:0:0: +168,76,3851,2,0,L|157:135,3,51.7500009870529,10|0|0|0,0:3|0:0|0:0|0:0,0:0:0:0: +120,344,4139,5,2,1:3:0:0: +171,363,4211,1,0,0:0:0:0: +227,356,4283,1,0,1:0:0:0: +275,324,4355,1,0,0:0:0:0: +301,274,4427,5,10,0:3:0:0: +327,224,4499,1,0,0:0:0:0: +375,192,4571,1,0,0:0:0:0: +431,185,4643,1,0,0:0:0:0: +482,204,4715,5,2,1:3:0:0: +474,254,4787,1,0,0:0:0:0: +446,297,4859,1,0,0:0:0:0: +402,323,4931,1,0,0:0:0:0: +351,328,5003,1,10,0:3:0:0: +303,310,5075,1,0,0:0:0:0: +267,273,5148,1,0,0:0:0:0: +241,229,5220,1,0,0:0:0:0: +192,216,5292,5,2,1:3:0:0: +134,226,5364,1,0,0:0:0:0: +79,204,5436,1,0,1:0:0:0: +45,157,5508,1,0,0:0:0:0: +42,98,5580,1,10,0:3:0:0: +70,47,5652,1,0,0:0:0:0: +122,19,5724,1,0,0:0:0:0: +180,23,5796,1,0,0:0:0:0: +227,57,5868,1,2,1:3:0:0: +249,103,5940,1,0,0:0:0:0: +289,134,6012,1,0,0:0:0:0: +339,145,6084,1,0,0:0:0:0: +389,131,6156,6,0,L|399:71,1,51.7500009870529,10|0,0:3|0:0,0:0:0:0: +474,254,6300,2,0,L|463:313,1,51.7500009870529,2|0,1:3|0:0,0:0:0:0: +294,361,6444,5,10,0:3:0:0: +294,361,6516,1,8,0:0:0:0: +294,361,6588,2,8,L|302:310,1,51.7500009870529 +172,121,6732,5,10,0:3:0:0: +172,121,6804,1,8,0:0:0:0: +172,121,6876,1,8,0:0:0:0: +172,121,6948,1,8,0:0:0:0: +172,121,7020,5,14,1:3:0:0: +115,119,7092,1,0,0:0:0:0: +68,149,7164,1,0,0:0:0:0: +46,201,7236,1,0,0:0:0:0: +56,256,7308,1,10,0:3:0:0: +95,296,7380,1,0,0:0:0:0: +150,308,7452,1,0,0:0:0:0: +202,287,7524,1,0,0:0:0:0: +233,241,7596,5,2,1:3:0:0: +264,195,7668,1,0,0:0:0:0: +316,174,7740,1,0,1:0:0:0: +371,186,7812,1,0,0:0:0:0: +410,226,7884,1,10,0:3:0:0: +420,281,7956,1,0,0:0:0:0: +398,333,8028,1,0,0:0:0:0: +351,363,8100,1,0,0:0:0:0: +294,361,8173,5,2,1:3:0:0: +300,325,8245,1,0,0:0:0:0: +306,289,8317,1,0,0:0:0:0: +312,253,8389,1,0,0:0:0:0: +319,218,8461,1,10,0:3:0:0: +325,182,8533,1,0,0:0:0:0: +331,146,8605,1,0,0:0:0:0: +338,111,8677,1,0,0:0:0:0: +344,75,8749,5,2,1:3:0:0: +400,97,8821,1,0,0:0:0:0: +433,148,8893,1,0,1:0:0:0: +428,209,8965,1,0,0:0:0:0: +388,255,9037,1,10,0:3:0:0: +328,268,9109,1,0,0:0:0:0: +273,243,9181,1,0,0:0:0:0: +243,189,9253,1,0,0:0:0:0: +216,132,9325,5,6,1:3:0:0: +179,96,9397,1,0,0:0:0:0: +129,83,9469,1,0,0:0:0:0: +80,95,9541,1,0,0:0:0:0: +43,131,9613,1,10,0:3:0:0: +29,180,9685,1,0,0:0:0:0: +40,230,9757,1,0,0:0:0:0: +75,268,9829,1,0,0:0:0:0: +124,283,9901,5,6,1:3:0:0: +150,258,9973,1,0,0:0:0:0: +182,242,10045,1,0,1:0:0:0: +218,235,10117,1,0,0:0:0:0: +254,239,10189,1,10,0:3:0:0: +287,252,10261,1,0,0:0:0:0: +316,274,10333,1,0,0:0:0:0: +337,304,10405,1,0,0:0:0:0: +349,338,10478,6,0,L|360:274,1,51.7500009870529,8|0,0:3|0:0,0:0:0:0: +400,72,10622,2,0,L|389:136,1,51.7500009870529,8|0,0:3|0:0,0:0:0:0: +440,352,10766,2,0,L|451:288,1,51.7500009870529,4|0,0:3|0:0,0:0:0:0: +491,86,10910,2,0,L|480:150,1,51.7500009870529,4|0,0:3|0:0,0:0:0:0: +376,204,11054,1,4,3:3:0:0: +76,21,11198,6,0,B|38:64|38:64|56:166,1,155.250002961159,12|0,0:2|0:0,0:0:0:0: +59,176,11486,6,0,L|68:227,1,51.7500009870529,8|0,0:0|0:0,0:0:0:0: +284,34,11630,5,6,1:3:0:0: +329,10,11702,1,0,0:0:0:0: +380,12,11774,1,0,0:0:0:0: +424,39,11846,1,0,0:0:0:0: +448,84,11918,1,14,0:3:0:0: +447,135,11990,1,0,0:0:0:0: +421,179,12062,1,0,0:0:0:0: +376,204,12134,1,0,0:0:0:0: +325,204,12206,5,2,1:3:0:0: +276,186,12278,1,0,0:0:0:0: +226,195,12350,1,0,1:0:0:0: +185,227,12422,1,0,0:0:0:0: +168,275,12494,1,10,0:3:0:0: +176,325,12566,1,0,0:0:0:0: +209,366,12638,1,0,0:0:0:0: +255,383,12710,1,0,0:0:0:0: +306,376,12783,5,2,1:3:0:0: +286,328,12855,1,0,0:0:0:0: +247,295,12927,1,0,1:0:0:0: +197,282,12999,1,0,0:0:0:0: +147,291,13071,1,10,0:3:0:0: +96,299,13143,1,0,0:0:0:0: +50,277,13215,1,0,0:0:0:0: +26,232,13287,1,0,0:0:0:0: +33,182,13359,5,2,1:3:0:0: +91,192,13431,1,0,0:0:0:0: +149,180,13503,1,0,0:0:0:0: +199,149,13575,1,0,0:0:0:0: +233,101,13647,1,10,0:3:0:0: +268,53,13719,1,0,0:0:0:0: +318,22,13791,1,0,0:0:0:0: +377,10,13863,1,0,0:0:0:0: +440,24,13935,5,2,1:3:0:0: +457,85,14007,1,0,0:0:0:0: +451,149,14079,1,0,0:0:0:0: +422,206,14151,1,0,0:0:0:0: +373,247,14223,1,10,0:3:0:0: +312,267,14295,1,0,0:0:0:0: +247,264,14367,1,0,0:0:0:0: +190,236,14439,1,0,0:0:0:0: +146,189,14511,5,2,1:3:0:0: +192,148,14583,1,0,0:0:0:0: +249,123,14655,1,0,1:0:0:0: +311,118,14727,1,0,0:0:0:0: +371,133,14799,1,10,0:3:0:0: +423,167,14871,1,0,0:0:0:0: +462,215,14943,1,0,0:0:0:0: +483,273,15015,1,0,0:0:0:0: +485,334,15087,6,0,L|476:384,3,51.7500009870529,2|0|0|0,1:3|0:0|0:0|0:0,0:0:0:0: +312,267,15376,2,0,L|320:216,3,51.7500009870529,10|0|0|0,0:3|0:0|0:0|0:0,0:0:0:0: +491,48,15664,5,2,1:3:0:0: +438,27,15736,1,0,0:0:0:0: +382,28,15808,1,0,1:0:0:0: +330,48,15880,1,0,0:0:0:0: +289,86,15952,1,10,0:3:0:0: +249,123,16024,1,0,0:0:0:0: +197,144,16096,1,0,0:0:0:0: +141,144,16168,1,0,0:0:0:0: +89,124,16240,5,6,1:3:0:0: +124,74,16312,1,0,0:0:0:0: +176,40,16384,1,0,0:0:0:0: +236,28,16456,1,0,0:0:0:0: +297,38,16528,1,10,0:3:0:0: +350,70,16600,1,0,0:0:0:0: +387,118,16672,1,0,0:0:0:0: +405,177,16744,1,0,0:0:0:0: +399,239,16816,5,2,1:3:0:0: +364,282,16888,1,0,0:0:0:0: +311,299,16960,1,0,1:0:0:0: +258,283,17032,1,0,0:0:0:0: +223,241,17104,1,10,0:3:0:0: +187,198,17176,1,0,0:0:0:0: +134,182,17248,1,4,1:3:0:0: +81,199,17320,1,0,0:0:0:0: +47,243,17392,5,0,0:0:0:0: +67,295,17464,1,0,0:0:0:0: +106,334,17536,1,0,1:0:0:0: +158,355,17608,1,0,0:0:0:0: +214,354,17680,1,10,0:3:0:0: +264,330,17752,1,0,0:0:0:0: +301,288,17824,1,0,0:0:0:0: +319,235,17896,1,0,0:0:0:0: +328,180,17969,6,0,L|339:116,3,56.25,2|0|0|0,1:3|0:0|1:0|0:0,0:0:0:0: +464,48,18257,2,0,L|452:111,3,56.25,10|0|0|0,0:3|0:0|0:0|0:0,0:0:0:0: +319,235,18545,5,6,1:3:0:0: +280,251,18617,1,0,0:0:0:0: +238,248,18689,1,0,0:0:0:0: +202,228,18761,1,0,0:0:0:0: +178,194,18833,1,10,0:3:0:0: +154,160,18905,1,0,0:0:0:0: +118,140,18977,1,0,0:0:0:0: +76,137,19049,1,0,0:0:0:0: +38,154,19121,6,0,L|28:209,3,56.25,2|0|0|0,1:3|0:0|1:0|0:0,0:0:0:0: +260,124,19409,2,0,L|269:68,1,56.25,10|0,0:3|0:0,0:0:0:0: +331,344,19553,5,4,1:3:0:0: +390,350,19625,1,0,0:0:0:0: +443,323,19697,1,0,0:0:0:0: +472,271,19769,1,0,0:0:0:0: +467,212,19841,1,0,1:0:0:0: +433,165,19913,1,0,0:0:0:0: +377,143,19985,5,10,0:3:0:0: +338,189,20057,1,0,0:0:0:0: +285,214,20129,1,0,0:0:0:0: +225,215,20201,1,0,0:0:0:0: +171,192,20274,1,2,1:3:0:0: +113,178,20346,1,0,0:0:0:0: +62,206,20418,1,0,1:0:0:0: +47,263,20490,1,0,0:0:0:0: +75,314,20562,5,10,0:3:0:0: +93,316,20634,1,0,0:0:0:0: +112,318,20706,1,0,0:0:0:0: +129,321,20778,1,0,0:0:0:0: +147,323,20850,5,8,1:2:0:0: +228,300,20922,1,0,0:0:0:0: +281,236,20994,1,8,0:0:0:0: +289,153,21066,1,0,0:0:0:0: +248,79,21138,1,8,0:0:0:0: +173,42,21210,1,0,0:0:0:0: +89,56,21282,1,8,0:0:0:0: +29,114,21354,1,0,0:0:0:0: +12,196,21426,5,8,0:0:0:0: +75,251,21498,1,8,0:0:0:0: +159,268,21570,1,8,0:0:0:0: +241,240,21642,1,8,0:0:0:0: +297,178,21714,5,8,0:0:0:0: +367,120,21786,1,8,0:0:0:0: +457,132,21858,1,8,0:0:0:0: +508,208,21930,1,8,0:0:0:0: +488,296,22002,5,12,0:0:0:0: +320,48,22146,1,12,0:0:0:0: +260,380,22578,5,12,0:0:0:0: +508,208,22722,1,12,0:0:0:0: +0,352,23155,6,0,L|47:344,1,47.2499985580445,2|0,0:0|0:0,0:0:0:0: +128,328,23731,6,0,B|192:229|192:229|311:253|311:253|234:167|234:167|303:78|303:78|188:114|188:114|122:25|122:25|124:141|124:141|20:182|20:182|129:202|126:201,1,1032.29995904572,12|0,0:0|0:0,0:0:0:0: +256,192,29133,12,0,29781,0:0:0:0: diff --git a/src/any/difficulty/converted.rs b/src/any/difficulty/converted.rs index b6d64f23..fdedec00 100644 --- a/src/any/difficulty/converted.rs +++ b/src/any/difficulty/converted.rs @@ -14,8 +14,8 @@ use crate::{ /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, Difficulty}; -/// use rosu_pp::catch::{Catch, CatchDifficultyAttributes}; +/// use rina_pp::{Beatmap, Difficulty}; +/// use rina_pp::catch::{Catch, CatchDifficultyAttributes}; /// /// let converted = Beatmap::from_path("./resources/2118524.osu") /// .unwrap() diff --git a/src/any/difficulty/gradual.rs b/src/any/difficulty/gradual.rs index 2857588d..3c746af7 100644 --- a/src/any/difficulty/gradual.rs +++ b/src/any/difficulty/gradual.rs @@ -23,7 +23,7 @@ use crate::{ /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, GradualDifficulty, Difficulty}; +/// use rina_pp::{Beatmap, GradualDifficulty, Difficulty}; /// /// let map = Beatmap::from_path("./resources/2785319.osu").unwrap(); /// let difficulty = Difficulty::new().mods(64); // DT diff --git a/src/any/difficulty/mod.rs b/src/any/difficulty/mod.rs index 51575a58..6818a391 100644 --- a/src/any/difficulty/mod.rs +++ b/src/any/difficulty/mod.rs @@ -32,7 +32,7 @@ use crate::{model::mode::IGameMode, util::mods::Mods}; /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, Difficulty, any::DifficultyAttributes}; +/// use rina_pp::{Beatmap, Difficulty, any::DifficultyAttributes}; /// /// let map = Beatmap::from_path("./resources/2118524.osu").unwrap(); /// diff --git a/src/any/performance/gradual.rs b/src/any/performance/gradual.rs index d0d401e4..062f7c13 100644 --- a/src/any/performance/gradual.rs +++ b/src/any/performance/gradual.rs @@ -32,7 +32,7 @@ use crate::{ /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, GradualPerformance, Difficulty, any::ScoreState}; +/// use rina_pp::{Beatmap, GradualPerformance, Difficulty, any::ScoreState}; /// /// let map = Beatmap::from_path("./resources/2785319.osu").unwrap(); /// let difficulty = Difficulty::new().mods(64); // DT diff --git a/src/catch/difficulty/gradual.rs b/src/catch/difficulty/gradual.rs index de0ad44e..006a8934 100644 --- a/src/catch/difficulty/gradual.rs +++ b/src/catch/difficulty/gradual.rs @@ -31,8 +31,8 @@ use super::{ /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, Difficulty}; -/// use rosu_pp::catch::{Catch, CatchGradualDifficulty}; +/// use rina_pp::{Beatmap, Difficulty}; +/// use rina_pp::catch::{Catch, CatchGradualDifficulty}; /// /// let converted = Beatmap::from_path("./resources/2118524.osu") /// .unwrap() diff --git a/src/catch/performance/gradual.rs b/src/catch/performance/gradual.rs index 3484f0db..85535b67 100644 --- a/src/catch/performance/gradual.rs +++ b/src/catch/performance/gradual.rs @@ -21,8 +21,8 @@ use crate::{ /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, Difficulty}; -/// use rosu_pp::catch::{Catch, CatchGradualPerformance, CatchScoreState}; +/// use rina_pp::{Beatmap, Difficulty}; +/// use rina_pp::catch::{Catch, CatchGradualPerformance, CatchScoreState}; /// /// let converted = Beatmap::from_path("./resources/2118524.osu") /// .unwrap() diff --git a/src/lib.rs b/src/lib.rs index 2388faf7..9560212b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! 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 +//! A large part of `rina-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](#accuracy) //! while also providing a significant [boost in performance](#speed). //! @@ -18,17 +18,17 @@ //! //! ``` //! // Decode the map -//! let map = rosu_pp::Beatmap::from_path("./resources/2785319.osu").unwrap(); +//! let map = rina_pp::Beatmap::from_path("./resources/2785319.osu").unwrap(); //! //! // Calculate difficulty attributes -//! let diff_attrs = rosu_pp::Difficulty::new() +//! let diff_attrs = rina_pp::Difficulty::new() //! .mods(8 + 16) // HDHR //! .calculate(&map); //! //! let stars = diff_attrs.stars(); //! //! // Calculate performance attributes -//! let perf_attrs = rosu_pp::Performance::new(diff_attrs) +//! let perf_attrs = rina_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! @@ -59,7 +59,7 @@ //! score state. //! //! ``` -//! use rosu_pp::{Beatmap, GradualPerformance, Difficulty, any::ScoreState}; +//! use rina_pp::{Beatmap, GradualPerformance, Difficulty, any::ScoreState}; //! //! let map = Beatmap::from_path("./resources/1028484.osu").unwrap(); //! @@ -91,29 +91,29 @@ //! //! ## Accuracy //! -//! `rosu-pp` was tested against all current beatmaps on multiple mod combinations and delivered +//! `rina-pp` was tested against all current beatmaps on multiple mod combinations 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 which can cascade into larger differences in the end. -//! With this in mind, `rosu-pp` is still as accurate as can be without targeting the +//! With this in mind, `rina-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 +//! An important factor for `rina-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: +//! Results of a rudimentary [benchmark] of osu!lazer and rina-pp: //! ```txt //! osu!lazer: //! Decoding maps: Median: 378.10ms | Mean: 381.47ms //! Calculating difficulties: Median: 588.89ms | Mean: 597.11ms //! Calculating performances: Median: 315.90µs | Mean: 310.60µs //! -//! rosu-pp: +//! rina-pp: //! Decoding maps: Median: 46.94ms | Mean: 47.21ms //! Calculating difficulties: Median: 72.90ms | Mean: 73.13ms //! Calculating performances: Median: 44.13µs | Mean: 45.53µs @@ -130,16 +130,16 @@ //! //! ## Bindings //! -//! Using `rosu-pp` from other languages than Rust: -//! - JavaScript: [rosu-pp-js] -//! - Python: [rosu-pp-py] +//! Using `rina-pp` from other languages than Rust: +//! - JavaScript: [rina-pp-js] +//! - Python: [rina-pp-py] //! //! [osu!]: https://osu.ppy.sh/home //! [osu!lazer]: https://github.com/ppy/osu //! [osu!tools]: https://github.com/ppy/osu-tools //! [`tracing`]: https://docs.rs/tracing -//! [rosu-pp-js]: https://github.com/MaxOhn/rosu-pp-js -//! [rosu-pp-py]: https://github.com/MaxOhn/rosu-pp-py +//! [rina-pp-js]: https://github.com/MaxOhn/rina-pp-js +//! [rina-pp-py]: https://github.com/MaxOhn/rina-pp-py //! [benchmark]: https://gist.github.com/MaxOhn/625af10011f6d7e13a171b08ccf959ff //! [`GradualDifficulty`]: crate::any::GradualDifficulty //! [`GradualPerformance`]: crate::any::GradualPerformance @@ -160,6 +160,8 @@ clippy::cast_possible_wrap )] +#![feature(round_ties_even, precise_pointer_size_matching)] + #[doc(inline)] pub use self::{ any::{Difficulty, GradualDifficulty, GradualPerformance, Performance}, diff --git a/src/mania/difficulty/gradual.rs b/src/mania/difficulty/gradual.rs index 6af40416..dc8a1ab3 100644 --- a/src/mania/difficulty/gradual.rs +++ b/src/mania/difficulty/gradual.rs @@ -24,8 +24,8 @@ use super::{ /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, Difficulty}; -/// use rosu_pp::mania::ManiaGradualDifficulty; +/// use rina_pp::{Beatmap, Difficulty}; +/// use rina_pp::mania::ManiaGradualDifficulty; /// /// let converted = Beatmap::from_path("./resources/1638954.osu") /// .unwrap() diff --git a/src/mania/performance/gradual.rs b/src/mania/performance/gradual.rs index 55db847a..9ee42cc3 100644 --- a/src/mania/performance/gradual.rs +++ b/src/mania/performance/gradual.rs @@ -20,8 +20,8 @@ use super::{ManiaPerformanceAttributes, ManiaScoreState}; /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, Difficulty}; -/// use rosu_pp::mania::{Mania, ManiaGradualPerformance, ManiaScoreState}; +/// use rina_pp::{Beatmap, Difficulty}; +/// use rina_pp::mania::{Mania, ManiaGradualPerformance, ManiaScoreState}; /// /// let converted = Beatmap::from_path("./resources/1638954.osu") /// .unwrap() diff --git a/src/osu/difficulty/gradual.rs b/src/osu/difficulty/gradual.rs index 507f949b..777ba7a6 100644 --- a/src/osu/difficulty/gradual.rs +++ b/src/osu/difficulty/gradual.rs @@ -31,8 +31,8 @@ use super::{ /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, Difficulty}; -/// use rosu_pp::osu::{Osu, OsuGradualDifficulty}; +/// use rina_pp::{Beatmap, Difficulty}; +/// use rina_pp::osu::{Osu, OsuGradualDifficulty}; /// /// let converted = Beatmap::from_path("./resources/2785319.osu") /// .unwrap() diff --git a/src/osu/performance/gradual.rs b/src/osu/performance/gradual.rs index 749ef5d2..474cacfa 100644 --- a/src/osu/performance/gradual.rs +++ b/src/osu/performance/gradual.rs @@ -20,8 +20,8 @@ use super::{OsuPerformanceAttributes, OsuScoreState}; /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, Difficulty}; -/// use rosu_pp::osu::{Osu, OsuGradualPerformance, OsuScoreState}; +/// use rina_pp::{Beatmap, Difficulty}; +/// use rina_pp::osu::{Osu, OsuGradualPerformance, OsuScoreState}; /// /// let converted = Beatmap::from_path("./resources/2785319.osu") /// .unwrap() diff --git a/src/osu/performance/mod.rs b/src/osu/performance/mod.rs index d1c26721..9e641e85 100644 --- a/src/osu/performance/mod.rs +++ b/src/osu/performance/mod.rs @@ -553,39 +553,24 @@ impl OsuPerformanceInner { multiplier *= 1.0 - (f64::from(self.attrs.n_spinners) / total_hits).powf(0.85); } - if self.mods.rx() { - // * https://www.desmos.com/calculator/bc9eybdthb - // * we use OD13.3 as maximum since it's the value at which great hitwidow becomes 0 - // * this is well beyond currently maximum achievable OD which is 12.17 (DTx2 + DA with OD11) - let (n100_mult, n50_mult) = if self.attrs.od > 0.0 { - ( - 1.0 - (self.attrs.od / 13.33).powf(1.8), - 1.0 - (self.attrs.od / 13.33).powf(5.0), - ) - } else { - (1.0, 1.0) - }; - - // * As we're adding Oks and Mehs to an approximated number of combo breaks the result can be - // * higher than total hits in specific scenarios (which breaks some calculations) so we need to clamp it. - self.effective_miss_count = (self.effective_miss_count - + f64::from(self.state.n100) - + n100_mult - + f64::from(self.state.n50) * n50_mult) - .min(total_hits); - } - let aim_value = self.compute_aim_value(); let speed_value = self.compute_speed_value(); let acc_value = self.compute_accuracy_value(); let flashlight_value = self.compute_flashlight_value(); - let pp = (aim_value.powf(1.1) + let pp = if self.mods.rx() { + (aim_value.powf(1.1) + + speed_value.powf(0.9) + + acc_value.powf(1.1) + + flashlight_value.powf(1.1)) + .powf(1.0 / 1.1) * multiplier + } else { + (aim_value.powf(1.1) + speed_value.powf(1.1) + acc_value.powf(1.1) + flashlight_value.powf(1.1)) - .powf(1.0 / 1.1) - * multiplier; + .powf(1.0 / 1.1) * multiplier + }; OsuPerformanceAttributes { difficulty: self.attrs, @@ -612,9 +597,15 @@ impl OsuPerformanceInner { // * Penalize misses by assessing # of misses relative to the total # of objects. // * Default a 3% reduction for any # of misses. if self.effective_miss_count > 0.0 { - aim_value *= 0.97 - * (1.0 - (self.effective_miss_count / total_hits).powf(0.775)) - .powf(self.effective_miss_count); + if self.mods.rx() { + aim_value *= 0.97 + * (0.9 - (self.effective_miss_count / total_hits).powf(1.1)) + .powf(self.effective_miss_count); + } else { + aim_value *= 0.97 + * (1.0 - (self.effective_miss_count / total_hits).powf(0.775)) + .powf(self.effective_miss_count); + } } aim_value *= self.get_combo_scaling_factor(); @@ -630,7 +621,11 @@ impl OsuPerformanceInner { }; // * Buff for longer maps with high AR. - aim_value *= 1.0 + ar_factor * len_bonus; + if self.mods.rx() { + aim_value *= 1.0 + ar_factor; + } else { + aim_value *= 1.0 + ar_factor * len_bonus + } if self.mods.hd() { // * We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. @@ -657,6 +652,27 @@ impl OsuPerformanceInner { // * It is important to consider accuracy difficulty when scaling with accuracy. aim_value *= 0.98 + self.attrs.od.powf(2.0) / 2500.0; + if self.mods.rx() { + let diff_ratio = self.get_distance_duration_ratio(); + let mut length = self.attrs.hit_length(); + + // dt cuts length + if self.mods.dt() { + length /= 1.5; + } + + if self.mods.dt() { + aim_value *= 2.31_f64.powf(1.0 + diff_ratio / 11.0) * 0.4; + } else { + aim_value *= 2.31_f64.powf(1.1 + diff_ratio / 4.0) * 0.4; + }; + + // nerf short maps + if length <= 65.0_f64 { + aim_value *= (length / 489.0).sqrt() * 1.4 + 0.49 + } + } + aim_value } @@ -810,7 +826,7 @@ impl OsuPerformanceInner { } fn get_combo_scaling_factor(&self) -> f64 { - if self.attrs.max_combo == 0 { + if self.attrs.max_combo == 0 || self.mods.rx() { 1.0 } else { (f64::from(self.state.max_combo).powf(0.8) / f64::from(self.attrs.max_combo).powf(0.8)) @@ -821,6 +837,55 @@ impl OsuPerformanceInner { const fn total_hits(&self) -> f64 { self.state.total_hits() as f64 } + + fn get_distance_duration_ratio(&self) -> f64 { + let mut pos = 0; + let mut ratios: Vec = vec![]; + + let mut map_cs = self.attrs.cs; + + if self.mods.hr() { + map_cs = self.attrs.cs * 0.3; + } else if self.mods.ez() { + map_cs = self.attrs.cs * 2.0; + } + + while pos + 1 < self.attrs.hit_objects.len() { + let obj = &self.attrs.hit_objects[pos]; + let next_obj = &self.attrs.hit_objects[pos + 1]; + + if !obj.is_circle() || !next_obj.is_circle() { + pos += 1; + continue; + } + + let _dist = (next_obj.pos.x - obj.pos.x).powi(2) + (next_obj.pos.y - obj.pos.y).powi(2); + let mut dist = _dist.sqrt(); + + // calculate the circle radius of the 2 circles and subtract it from distance. + let r = 54.4 - 4.48 * map_cs; + dist -= r * 2.0; + + let mut duration = next_obj.start_time - obj.end_time(); + if self.mods.dt() { + duration /= 1.44; // should be 1.5, but it buffs maps too much + } + + let mut ratio = 0.0; + + if duration != 0.0 { + ratio = dist as f64 / duration; + } + + ratios.push(ratio); + + pos += 1; + } + + let sum: f64 = ratios.iter().sum(); + let count = ratios.len() as f64; + sum / count + } } fn calculate_effective_misses(attrs: &OsuDifficultyAttributes, state: &OsuScoreState) -> f64 { diff --git a/src/taiko/difficulty/gradual.rs b/src/taiko/difficulty/gradual.rs index 9cff07d5..2f4a4438 100644 --- a/src/taiko/difficulty/gradual.rs +++ b/src/taiko/difficulty/gradual.rs @@ -25,8 +25,8 @@ use super::{ /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, Difficulty}; -/// use rosu_pp::taiko::{Taiko, TaikoGradualDifficulty}; +/// use rina_pp::{Beatmap, Difficulty}; +/// use rina_pp::taiko::{Taiko, TaikoGradualDifficulty}; /// /// let converted = Beatmap::from_path("./resources/1028484.osu") /// .unwrap() diff --git a/src/taiko/performance/gradual.rs b/src/taiko/performance/gradual.rs index 263d389c..779d564c 100644 --- a/src/taiko/performance/gradual.rs +++ b/src/taiko/performance/gradual.rs @@ -20,8 +20,8 @@ use super::TaikoPerformanceAttributes; /// # Example /// /// ``` -/// use rosu_pp::{Beatmap, Difficulty}; -/// use rosu_pp::taiko::{Taiko, TaikoGradualPerformance, TaikoScoreState}; +/// use rina_pp::{Beatmap, Difficulty}; +/// use rina_pp::taiko::{Taiko, TaikoGradualPerformance, TaikoScoreState}; /// /// let converted = Beatmap::from_path("./resources/1028484.osu") /// .unwrap() diff --git a/src/util/sync.rs b/src/util/sync.rs index ad5db40d..5360a4ac 100644 --- a/src/util/sync.rs +++ b/src/util/sync.rs @@ -133,7 +133,7 @@ impl fmt::Debug for Weak { } /// ```compile_fail -/// use rosu_pp::{taiko::TaikoGradualDifficulty, Beatmap, Difficulty}; +/// use rina_pp::{taiko::TaikoGradualDifficulty, Beatmap, Difficulty}; /// /// let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted(); /// let difficulty = Difficulty::new(); diff --git a/tests/decode.rs b/tests/decode.rs index c37459c9..9d2d1c7b 100644 --- a/tests/decode.rs +++ b/tests/decode.rs @@ -1,4 +1,4 @@ -use rosu_pp::{catch::Catch, mania::Mania, model::mode::GameMode, osu::Osu, taiko::Taiko, Beatmap}; +use rina_pp::{catch::Catch, mania::Mania, model::mode::GameMode, osu::Osu, taiko::Taiko, Beatmap}; use crate::common::assert_eq_float; diff --git a/tests/difficulty.rs b/tests/difficulty.rs index 6c607b09..a6cc1a3a 100644 --- a/tests/difficulty.rs +++ b/tests/difficulty.rs @@ -1,6 +1,6 @@ use std::panic::{self, UnwindSafe}; -use rosu_pp::{ +use rina_pp::{ catch::{Catch, CatchDifficultyAttributes}, mania::{Mania, ManiaDifficultyAttributes}, osu::{Osu, OsuDifficultyAttributes}, diff --git a/tests/performance.rs b/tests/performance.rs index da78d413..5cdc6228 100644 --- a/tests/performance.rs +++ b/tests/performance.rs @@ -1,6 +1,6 @@ use std::panic::{self, UnwindSafe}; -use rosu_pp::{ +use rina_pp::{ catch::{CatchPerformance, CatchPerformanceAttributes}, mania::{ManiaPerformance, ManiaPerformanceAttributes}, osu::{OsuPerformance, OsuPerformanceAttributes},