Skip to content

osu!standard parity for 2026q2 pp update - #77

Open
myssto wants to merge 27 commits into
MaxOhn:pp-updatefrom
myssto:pp-update-2026q2
Open

osu!standard parity for 2026q2 pp update#77
myssto wants to merge 27 commits into
MaxOhn:pp-updatefrom
myssto:pp-update-2026q2

Conversation

@myssto

@myssto myssto commented Aug 24, 2026

Copy link
Copy Markdown

Difficulty and performance tests are now passing (on unix, see related bullet) for standard. Here is my shortlist of things I think deserve a bit more attention / more experienced eye.

  • Skill "base classes" and pseudo-inheritance macro
    • There were a lot of changes to the base skill classes so I chose to implement alongside what was existing already so as to not break other rulesets in the process. Design choices were meant to follow lazer's inheritance as closely as possible, but looking back to it now I think the use of todo! with the intention of shadowing methods is probably not great and hard to follow.
  • Gradual calculation tests failing
    • The concept of gradual calculation makes perfect sense to me, but for the life of me I could not figure out why they were failing for me. Unsure if it's a rounding error or not.
  • Adjusting test case epsilon for unix
    • As previously discussed in the discord, there are inherit floating point discrepancies on unix platforms so for testing purposes I arbitrarily set the epsilon value for floating point comparisons to a higher value than it would normally be.
    • The above seemed to work fine, save for one case where the AR for a map was seemingly being truncated? As a stopgap I casted the values to f32, but I still have no idea what was causing that.

Outside of these points and general organization, I'm happy with the rest of it :)

@tsunyoku

tsunyoku commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

skills_new and everything associated with it feels quite messy in the name of avoiding some refactor to make it work for the other rulesets. I'm not suggesting that you have to implement all of the diff calc changes for other rulesets in this PR but it'd make this code significantly better if you at least made the API the same between them all, allowing actual updates to be PR'd separately.

I agree with mirroring lazer's API as much as possible but using pre-existing code as a guide for how much is "too much" would probably help too. Seeing unused code intentionally added just to say "matching lazer's API" doesn't feel particularly helpful - if it was matching, then it wouldn't be unused. Same goes for the other places that are doing it to stay in-sync with the "old" skills - drift is expected, imo. I haven't done a full read through this PR yet so I'm not sure why you made those choices but the skill API alone just reads weird.

@tsunyoku tsunyoku left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial thoughts, haven't done a proper comparison to lazer yet.

Comment on lines +89 to +90
if curr.idx > 2
&& let Some(osu_last_last_obj) = curr.previous(1, diff_objects)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's much of an issue with just keeping the condition to let Some(osu_last_last_obj) = curr.previous(1, diff_objects). They achieve the same thing even if the semantics are different.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, although technically as written this would short circuit the call to curr.previous() but like you said it's just semantics at that point.


// * Final velocity is being raised to a power because flow difficulty scales harder with
// * both high distance and time, and we want to account for that.
flow_diff = flow_diff.powf(1.45);

@tsunyoku tsunyoku Aug 24, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some sort of preferred style from @MaxOhn would probably help but maybe this should be f64::powf(flow_diff, 1.45) instead. Same for any other usages if there are any, just noticed it's not consistent.

@MaxOhn MaxOhn Aug 24, 2026

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I prefer the full function syntax for these kinds of calls because it makes it slightly more similar to C#'s Math.Pow(...) syntax. Am not aware of a way to enforce this notation programmatically unfortunately. Clippy can disallow specific methods but not the syntax you call it with.

@myssto myssto Aug 24, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can go through and change the f64_foo.func(f64_bar) calls to be f64::func(f64_foo, f64_bar) in that case, no problem. I tried to go off of the existing style as much as I could and since they both seemed to be used in different places throughout the codebase I wasn't sure about it.

Comment thread src/osu/difficulty/evaluators/reading.rs
Comment thread src/osu/difficulty/skills/aim.rs
Comment on lines +11 to +30
fn process_internal<'a>(
&mut self,
curr: &Self::DifficultyObject<'a>,
objects: &Self::DifficultyObjects<'a>,
) -> f64;

#[expect(dead_code, reason = "used by process_internal")]
fn strain_value_at<'a>(
&mut self,
curr: &Self::DifficultyObject<'a>,
objects: &Self::DifficultyObjects<'a>,
) -> f64;

fn backfill_peaks<'a>(
&mut self,
curr: &Self::DifficultyObject<'a>,
objects: &Self::DifficultyObjects<'a>,
);

fn save_current_peak(&mut self, section_length: f64);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is implementing these in the trait not an option? Relying on the implementation inside the macros being noticed is kinda meh, and I almost left a comment asking where the code disappeared to.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried my best to base these off of the existing skill traits where presumably the design philosophy was "if it needs to mutate stateful variables on the struct that are filled in by the macro, implement in the macro instead of the trait".

@tsunyoku

Copy link
Copy Markdown
Contributor

Regarding gradual calculation failures, depending on when you started copying changes over you may need one or both of these fixes:

@myssto

myssto commented Aug 25, 2026

Copy link
Copy Markdown
Author

skills_new and everything associated with it feels quite messy in the name of avoiding some refactor to make it work for the other rulesets. I'm not suggesting that you have to implement all of the diff calc changes for other rulesets in this PR but it'd make this code significantly better if you at least made the API the same between them all, allowing actual updates to be PR'd separately.

I agree with mirroring lazer's API as much as possible but using pre-existing code as a guide for how much is "too much" would probably help too. Seeing unused code intentionally added just to say "matching lazer's API" doesn't feel particularly helpful - if it was matching, then it wouldn't be unused. Same goes for the other places that are doing it to stay in-sync with the "old" skills - drift is expected, imo. I haven't done a full read through this PR yet so I'm not sure why you made those choices but the skill API alone just reads weird.

This is all pretty fair and valid and I would agree. Obviously everything in skills_new and macros_new were meant to be a stopgap until the rest of the rulesets were able to be looked into as well, but yeah they could probably be replaced as-is with a little extra work to make the APIs match up.

Like I mentioned to Max previously, rust isn't entirely my strong suit and when it comes to more involved things like the existing macros for the skill traits it was a bit of a learning experience to try and figure out what I was looking at haha. Clearly it was much easier before when the inheritance chain from skills was simply Skill -> StrainSkill -> StrainDecaySkill, so I think it was a little difficult for me to compare to the existing setup with things seeming so different. I do have some ideas on ways it can be improved so I'll definitely work on it a bit more.

@MaxOhn

MaxOhn commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Just wanna say that a lot of rosu-pp's code is very much non-idiomatic Rust just so that it can achieve easier parity with osu, hope you don't take away any antipatterns from working with this codebase 😄
The artificial inheritance via macro is very complex, I'll try to give refactoring of your addition a shot eventually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants