This PR makes front matter items with comments able to be parsed
correctly by the client, for example:
```markdown
---
tags:
- keyboard
- tap
- hybrid
- play style
needs_cleanup: true # https://github.com/ppy/osu-wiki/issues/9919
---
# Hybrid
...
```
The front matter YAML used by osu!wiki not complicated, so simply
splitting with `#` is possible.
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
Before:
https://github.com/user-attachments/assets/d87bd7e3-37f8-4634-9e6a-5859d5bade57
After:
https://github.com/user-attachments/assets/4de940af-1e30-4266-9aac-5ccd12f38742
---
The title is convoluted but basically I'm angling to close
https://github.com/ppy/osu/issues/36705 with this.
The point is that on current `master`, the keyboard-hotkey-based toggles
on the left of the screen get disabled if you select a range of objects
which contains no addition samples. The report linked above finds this
annoying because it means you basically always need to add an addition
sound *first* and *then* pick a bank.
This is not necessary, and this commit changes the behaviour such that
the bank selection toggles are no longer blocked when you select a range
of objects without additions. Choosing an addition bank when there are
no additions still does nothing to the selected object, *but* adding a
sound *after* that bank preselection will use the preselected bank
rather than auto.
Closes https://github.com/ppy/osu/issues/36830.
This is a regression from https://github.com/ppy/osu/pull/36681.
Due to the aforementioned pull request's changes, rotating an object
that could not scale on the X or Y axis (due to having that dimension
zero) would trigger `CanScale{X,Y}` to change as said rotated object's
width or height became not zero. This in turn would cause `SelectionBox`
to *fully recreate* all of its handles and buttons, *including* the
rotation handle that initiated the rotation operation, therefore
dropping the ongoing rotation operation completely and leaving the
editor in a half-broken state.
The suggested solution here is to recreate handles more granularly to
prevent this from happening. (I've probably not improved it as much as I
could have, but this is as far as I'm willing to go for now unless
review finds it unpalatable.)
[It still doesn't
work.](https://github.com/ppy/osu/actions/runs/22759488243/job/66012293202)
Looking at the [job
output](https://github.com/ppy/osu/actions/runs/22759488243/job/66012293202#step:1:21)
it appears that the permissions of the `GITHUB_TOKEN` are
*automatically* constrained to `read` even if you request more scopes.
Would be nice if that was *actually documented* somewhere!
Also given supply-chain attacks that people are running on github [via
*issue titles* these
days](https://grith.ai/blog/clinejection-when-your-ai-tool-installs-another)
I'm not sure we want any automation near where it can reach code. Sure,
much of the fault in the aforementioned attack was the fault of meatbags
trusting clankers *WAY* too much, which is a mistake we *would not* do,
but given everpresent software degradation *from unknown sources and for
unknown reasons* let's not ~~COPILOT~~ *ahem* tempt fate...
It was taking up way too much vertical space previously.
I'm using the same font specs that are in the new dropdown header, which
seem to work quite well. Negative padding because without it everything
is way too vertically sparse.
Why? I was looking at [this
discussion](https://github.com/ppy/osu/discussions/36708) and like "we
need to have SV visible here" but there's really not much room with all
this text in the way.
Closes#13513
Matches stable behavior where hat only plays when the slider tick rate
is a multiple of 2.
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
Closes#36246
This PR overrides `HandlePositionalInput` to reject all positional input
when `Item` is null.
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
Before:
https://github.com/user-attachments/assets/95b19bdb-12c5-4ee7-b7e2-f9098aecd2fa
After:
https://github.com/user-attachments/assets/937081c5-92cb-4bea-a774-e53afbdbb1fb
---
This is a subjective diff and can be closed on the spot with no
discussion if deemed incorrect. I mostly just want to get it off my list
of unresolved forum threads.
The catalyst for this change is [this
thread](https://osu.ppy.sh/community/forums/topics/2171493?n=1), wherein
the complaint is that you can get jumpscared by a slider repeat in a
combo because the slider repeats do not fade in with the rest of the
combo. Which means that you don't immediately know whether a slider will
need to be repeated or not and you have to watch out for potential
slider repeats fading in, which could happen at an *arbitrary* time
because it's still dependent on the map's original AR.
This seems pretty anti-user so I made it fade in with the rest of the
combo. The original comment cited "broken layering" which I guess refers
to the fact that sliders can repeat *more* than once and the second
repeat will show *under the slider head*. It's pretty broken, sure, but
I don't think it's *that* bad, and I *do* think that it's better than
the current behaviour.
The reason it shows only the first two repeats is that there's no point
in showing more for reasons that are hopefully obvious.
This commit rearranges the contents of `ShearedButtons` to be more
independent of each other in regards to sizing. Thanks to that, the
custom logic related to enabling autosizing is no longer necessary.
Witdh and height are no longer set via the constructor, and can be
freely configured using the initializer syntax.
Additionally, this allows the button to use relative sizing without
having to resort to any hackery with `Size` (this will come in handy for
me when implementing the new footer on multiplayer screens).
Given that most of the `ShearedButton`s currently in use set their width
explicitly, I did not set `AutoSizeAxes = Axes.X` like it would be by
default previously. Instead it is set on the only two such buttons (show
converts/selected mods on ssv2). I suppose it might be a good idea to
have it set that by default if no `Width` is specified, as right now
it'll just not show anything.
Also I've set the margin on the text field by default in all cases
instead of only when autosizing like how it was previously, since
otherwise it would be a pain to set that on each button instance when
needed. I've checked all affected components and could not find any text
overflowing issues that this could cause.
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
RFC. Closes https://github.com/ppy/osu/issues/36815 I guess.
Song select V1 completely disabled the ability to view a score outside
of solo play in ways that are very easy to let fly under the radar:
5fc836d1f0/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs (L26)46db3ad96d/osu.Game/Screens/Select/PlaySongSelect.cs (L47-L53)
therefore the issue this is trying to close would never even manifest
there.
The direct cause of the issue is that the results screen is pushed to
the relevant online screen's *substack* of screens rather than the
game-global parent stack. That means that when the back button is
pressed, the following logic fires:
6fa4a7152f/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs (L174-L189)
This logic fires *on the parent screen* even though *the child screen*
is the one the user is attempting to back out of. And none of the
exemptions for the screen substack inside of the above method fire
because the subscreen is not an `IOnlinePlaySubScreen` (it's
`SoloResultsScreen` in this case).
Now the direct cause here is probably fixable, although possibly not
without some significant pulling of footer-shaped teeth. *However*, I
kind of question as to why viewing scores should be permitted on online
song selects in the first place - it kind of distracts from the primary
purpose of the screens which is to *just pick a map already*.
The reproduction scenario for the subscription leak is as follows:
1. Switch to a scrolling ruleset (anything but osu! from the standard
ones).
2. Select a beatmap to edit.
3. Load the composer.
4. Go to timing tab.
5. Change a timing point.
6. Go back to the composer.
At this point, `EditorChangeHandler.OnStateChange` will have multiple of
the same delegate in the invocation list.
<img width="691" height="311" alt="Screenshot 2026-03-05 at 11 15 55"
src="https://github.com/user-attachments/assets/57788341-9573-48f1-b360-f21036891081"
/>
That in turn is caused by the fact that changing a timing point *does*
incur a full reload of the composer via the following flow:
15b6e28ebe/osu.Game/Rulesets/Edit/ScrollingHitObjectComposer.cs (L145)64a29313a8/osu.Game/Screens/Edit/Editor.cs (L1137-L1145)
This flow is my "fault"; see https://github.com/ppy/osu/pull/28444. The
reason why a full composer reload is used is not clear to my
recollection at this time, but it is likely because it's just the least
likely to fail. A smarter solution that wouldn't require a full reload
would also entail checking that there exists a safe insertion point that
allows replacing timing points in a way that will reflect everywhere it
must. Including all of the `IScrollingAlgorithm` machinery and such.
In general it is not uncommon in the codebase to not bother to clean up
some event callbacks if it is implicitly or explicitly guaranteed that
both objects bound by the callback will always get disposed in tandem at
the same time. This *was* true with this particular flow to a point,
which was until that full composer reload was implemented.
<details>
<summary>To address the elephant in the room</summary>
Someone will inevitably notice https://github.com/ppy/osu/pull/36824
which was a clanked pull request pointing out this leak. And then
someone will inevitably call this "AI discrimination"! *Gasp!*
So first of all, let me stop you right there. Yes, as far as I am
_personally_ concerned, it is "AI discrimination". I invoke the full
force of the Butlerian Jihad.
The clank army's goal is to eradicate my job and make me work in an
Amazon warehouse instead. Or, if not that, at least my job is to be rid
of all remnants of fun I still get from it and for me to be reduced to
that one guy from the meme "i guess we're doin circles now". You know
the one.
I resent this. You attack me directly. I do not perceive the need to
meet you halfway or be civil.
That said, I have too much respect for the users of this software to
leave reports of potentially real issues unchecked. So I did check, and
it was real. And you know what? Good job to the clanker. It did what it
was designed to do: it parsed a code file, recognised a hole in a
pattern it was designed to recognise, and invoked forms of language
given to it to communicate this to the meatbag that opened that PR.
And here's the thing: my primary issue is with that meatbag that opened
that PR. That meatbag served no functional purpose in any of this. The
meatbag took a hose that spews 90% water and 10% raw sewage at random
intervals and pointed it at my house directly, claiming that they just
want to clean it. At no point did the meatbag appear to have the common
decency to pull out a container, pour some magic liquid out, check if
there's sewage in it, and filter it out if there is any. But no, that
would take *effort* and *thought*, would it not? The *effort* and
*thought* that is required of *me* to *review* the clanker's work?
The PR had no reproduction scenario, and had testing checkboxes that
were presumably meant for *me* to check off. Why is it *my* job to
figure all of this out rather than the submitter meatbag's?
I do *not* have obligations towards spew-hose-pointing meatbags. Point
that hose at your own backyard at your peril.
If you *actually manage* to get the clanker to filter out *all* of the
spew without fail itself, my only win condition is gone. But it is not
yet that time. So at least have the decency to check for the spew
yourself, rather than telling the clanker to put checkboxes in the PR
descriptions telling *me* to check for it.
</details>
Closes https://github.com/ppy/osu/issues/36453.
My omission was in assuming that web was going to start filtering out
the tags below the threshold from API responses, which is not the case.
Whether or not the consumers want or not to display tags below threshold
is subjective. I figured that the matchmaking card tooltip might want to
display below threshold but I dunno.
Regressed in 60d9c358b8.
In general an `APIBeatmap`'s `BeatmapSet` need not be present. In the
usage site of `osu.Game.Overlays.BeatmapSet.Info`, `Beatmap` and
`BeatmapSet` were actually two separate bindables. Moving the logic to a
helper, and therefore implicitly moving `BeatmapSet` from tracking said
separate bindable to instead refer to `Beatmap.BeatmapSet` broke this.
Resolves#36099
This PR fixes keyboard navigation in the beatmap select carousel for
lazer by implementing page-wise traversal with the Page Up and Page Down
keys and changing it from only scrolling to actually selecting items.
**Changes:**
- Added handling for `TraversalType.Page` in the keyboard traversal
switch.
- Implemented `traverseKeyboardPage(int direction)` method to move the
selection by approximately one "page" of visible items, accounting for
partially obscured items like the search bar. Also it does not wrap
around (like the current PageUp/Down functionality).
- Added new key bindings:
- `PageUp` → SelectPreviousPage
- `PageDown` → SelectNextPage
The code may be very explicit for the scroll logic with the page keys,
so I would appreciate some feedback when the PR is reviewed.
The naming of the keybinds may need to be adjusted. `Next page` and
`previous page` may be somewhat misleading.
**Behavior after the change:**
- Pressing Page Up/Down now moves the selection by a page of items.
- After navigating, pressing Left/Right selects the navigated song
instead of moving relative to the previous position.
**See:**
https://www.youtube.com/watch?v=JXmKAhhKiCc
---------
Signed-off-by: Linus Genz <linuslinuxgenz@gmail.com>
Co-authored-by: Dean Herbert <pe@ppy.sh>
Part of the `ScreenFooter` refactor, which intends to move the footer
content handling to `OsuScreen`. This commit updates the `ScreenFooter`
test to operate on entire `OsuScreen`s, in order to better test the
entire flow of pushing a screen, and having it create and add its own
content to the footer.
This should be 80-90% identical to the original test case structure
wise, just that instead of manually manipulating the footer with
`SetButtons()`, various screens with the appropriate buttons are being
moved around the screen stack.
Additionally this adds some more tests handling common use cases, as
well as removes `TestLoadOverlayAfterFooterIsDisplayed()`, since as far
as I understand the behaviour described in it doesn't actually happen in
production code. From what I can see, Screens instantiate their overlays
in `load()`, and then register them in `LoadComplete()`. There seems to
be no case where a `ShearedOverlayContainer` is created in the middle of
a screen's lifecycle.
Part of the screen footer refactor.
Once footer content is being managed by `OsuScreen`, the current tests
which simply create the tested overlay and `ScreenFooter` in a container
will no longer work.
This PR refactors them to use `ScreenTestScene` with the setup being
creating a dedicated testing `OsuScreen` which does the bare minimum to
create the tested overlay and necessary components (eg.
`FooterButtonFreeModsV2` for `TestSceneFreeModsOverlay`).
Most of the changes here can be described as
`%s/<...>Overlay/screen.Overlay/g`, with some minor touchups as
necessary, given that we're now testing a more complete flow which
checks more things that were previously not handled by the tests.
## [Move footer to front in
ScreenTestScene](f8740e0403)
Self-explanatory. Without it the footer would show below the actual
overlay, breaking tests depending on manual input. For the sake of tests
not breaking in CI, both #36718 and this have this included - would
prefer the former to be merged first since it was already reviewed
there.
## `TestSceneModSelectOverlay`
There were a few tests (`TestColumnHidingOnIsValidChange`,
`TestColumnHidingOnTextFilterChange`, and
`TestHidingOverlayClearsTextSearch`) that would create a custom overlay
instance instead of the globally provided one. I've tested both and the
tests run fine with the default overlay, so they're now using that
instead.
## `TestSceneFreeModSelectOverlay`
Updated to use footer v2.
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
Rebase of https://github.com/smoogipoo/osu/pull/193
Going forward, the client will have to know the type of pool being
invited to so that it can enter the appropriate screen when clicking the
notification.
Unfortunately, SignalR does not support overloading methods, or even
adding parameters to them, so this PR deprecates the
`MatchmakingRoomInvited` event and adds its replacement
`MatchmakingRoomInvitedWithParams` with a complex `invitation` parameter
that we _can_ extend in the future if required (such as potentially
adding the name of the pool).
This also prepares the notification by extracting some code to a
`Complete` method receiving said `invitation` parameter. This part of
code will be further modified to enter the correct screen:
0a4018045b/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/QueueController.cs (L200)
In particular, I have tested that new clients continue to work with the
old server (dev.ppy.sh) in quick play
| | Old Server | New Server |
| ------------- |:-------------:| :-----:|
| Old Client | 🟢 | 🟢 |
| New Client | 🟢 | 🟢 |
- [x] Depends on https://github.com/ppy/osu/pull/36741 for merge
conflict avoidance
RFC, cc @OliBomby
## [Adjust behaviour of automatic bank assignment during
placement](547f55e9b3)
Diatribe time!
This is fallout of the discussion about auto bank in
https://github.com/ppy/osu/issues/36705.
Auto bank in lazer as written before this commit is confused. On stable,
auto bank is closer to "no bank", as in "go look up the current sample
timing point, get the bank of that, and use that". lazer has no timing
points anymore, but people still want auto bank. So what do?
Auto bank for normal samples is somewhat sane still. It only works
during placement, and will just copy the normal bank of the previous
object - if one exists. That said, one *might not* exist, but the
resulting object will still have its normal sample created with
`editorAutoBank: true`. That is largely cosmetic and without
consequences, but this commit fixes that.
Auto bank for *addition* samples, however... Hoo boy.
- For placed objects, auto bank means "take the normal sample, read its
bank, and use that". Simple enough, right?
- Hoooooowever. During placement, auto bank before this commit used to
mean "look at the *previous object*, check if it has an addition sound
and then use its bank, if not use *the previous object's* normal sample
and then use its bank" which is a completely different thing with its
own implications. Like, say, what happens if the previous object uses
the auto addition bank too? What should be copied over? Should it be the
notion of "auto bank" in that the addition bank should match the normal
bank, or should it be the literal bank that the previous object is
using?
This change attempts to define this unambiguously. "Auto additions bank"
means "the same bank as the normal bank of this object", full stop.
## [Do not touch sample toggle state if there are no selected
objects](052cde5987)
Fixes issue described in
https://github.com/ppy/osu/issues/36705#issuecomment-3953917163 wherein
opening a sample popover will disable addition bank toggles and toggle
off all addition samples.
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
- Closes https://github.com/ppy/osu/issues/30293
- Fixes https://osu.ppy.sh/community/forums/topics/2179339?n=1
Aside from fixing the off-by-one error that I mentioned in
https://github.com/ppy/osu/issues/30293#issuecomment-2413801663, this
also:
- Brings back the behaviour wherein if timing points are arranged very
weird and nightcore would play e.g. two first beats in a timing point
back-to-back, the second timing point is silent.
- Brings back the behaviour wherein the finish sample only plays if
`OmitFirstBarLine` on the timing point is disabled.
However:
- This does not bring back the behaviour wherein hat samples only play
if the slider tick rate is even because that only kind of makes sense in
common time, and if common time is mixed with waltz time or other time
signatures, it just gets weird.
- Also stable has zero attempt for compensating for waltz time anyway,
lazer's behaviour is bespoke, so that is not going to match any way you
cut it.
My testing procedure essentially consisted of getting stable to log when
it was playing nightcore samples and cross-checking the first 30sec or
so of https://osu.ppy.sh/beatmapsets/534385#osu/1131956 (check out the
timing of that beatmap, for something ranked it is DEEPLY messed up).
I guess I can add test cases if deemed required but I already wasted
much more time than I would have liked here...
- The mods button should not be visible because it is not hooked up
properly to actually work with selecting user mods. As written it
would show (and feign the ability to modify) required mods on the
playlist item.
You could probably adjust the logic to make user mods controllable via
this button but this is simpler for now.
- The random and options buttons could perhaps remain, but they are of
very limited use on this screen anyway.
The previous iteration disabled all footer buttons altogether, so I'm
just following precedent here mostly.
Of note, this test scene still doesn't use the new footer properly.
Should these even be here? Maybe it's better to leave these real world
flows to a `Navigation` style test so we're not dealing with weird test
setups that kinda do something but don't... or maybe do it in three
different ways (two footers present, three back buttons etc.)
Dunno. But this kind of covers what I want (except it doesn't cover the
new footer usage).
In stable, there was support for mixed fonts (some coming from the
user's skin, with fallback missing characters generated from TTF
backend).
We don't really have support for this so this is a simple in-between for
the time being.
Closes#33395
Copies the bookmarks from `referenceWorkingBeatmap` while creating a new
difficulty from scratch. I adapted the tests in
`TestSceneEditorBeatmapCreation` to include the bookmark checks.
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
- Related to https://github.com/ppy/osu-server-spectator/issues/406
Adding this field to this model has several vague reasons that I can't
fully formulate yet, but I can't really see myself going forward
*without* this.
- People were very excited about having referees displayed on the room
participants' list, and so adding the referees as real
`MultiplayerRoomUser`s helps this. Having the role could even be used
client-side to show a special icon or other status on the participants
list. (Which isn't done yet, could be as an aesthetic follow-up after
the basics are in place.)
- Server-side, having this field is convenient for things like
permission checks or just plain logic, as with two hubs you just need to
do different *stuff* on a `MultiplayerRoomUser`.
Part of #32584.
Very much inspired by the respective component for displaying profile
pictures on the user overlay
* allow disabling interactivity/tooltips
* add option to show placeholder on null team instead of hiding
component entirely
* move setting corner radius out to respective parent components to
allow for easier overriding
Adds a `DamageInfo` property to `RankedPlayDamageInfo` to be used by the
result screen.
The issue this is trying to solve is that once the result screen
initializes, the HP value of each player has already been updated in the
room state so the previous values are no longer accessible. Doing this
without the state exposing it would require some kinda setup to keep the
previous MatchState's HP values around on the client which would
introduce a lot of unnecessary weirdness.
- Closes https://github.com/ppy/osu/issues/36584
The last two commits could be either fixes to the issue above, but in a
code quality perspective, the scheduler in `setLink()` seems unnecessary
as the other set methods don't have it (other than making it run last)
and the other commit is self explanatory.
## [Specify `Accept` header in registration
request](28edb788f7)
The lack of it meant that in specific scenarios web would respond with a
chunk of HTML instead of JSON.
## [Allow showing registration error message even if no redirect is
given](6ad49941ff)
There are scenarios where this can happen, and if it did, previously the
strict requirement to have both would cause the specific message to be
discarded and replaced with the generic "something happened" one.
- Closes https://github.com/ppy/osu/issues/35389
Same as:
2efe0c95e6/osu.Game/Screens/Edit/Editor.cs (L1173-L1180)
There's also seeking hit objects and sample points, but the seeks are
relatively close to each other and probably useless when playing(?). If
we want to make those cases not stuck at the same point in time, I
believe the leniency should be lower than 1000 ms.
With the above, that is why I just copy-pasted the code, as we may want
to have different leniencies.
Edit: forgot the automated label thing, will not label next time
- Adds sorting and display styles.
- Saves sort/display modes to the config.
- Improves performance, especially on the 2nd+ time opening the overlay.
https://github.com/user-attachments/assets/e32b50d0-58a1-4eef-b18c-988fb497e545
---
Coming off some recent feedback in
https://github.com/ppy/osu/discussions/33426#discussioncomment-13431275,
I decided to take a bit of a detour and get a little bit more
functionality in.
Sorting by rank, although it should technically work, doesn't work right
now. This is because the osu!web API doesn't return user rank on
`/user/` lookups - it's only returned for the friends request. I'm
leaving this open as a discussion topic.
- We can make osu!web return the rank and osu! will require no further
changes to work correctly, or
- We can try to implement additional paths through
`osu-server-spectator` which would blow this PR out of proportion and is
best left for a task of its own.
For simplicity, I've re-implemented this display mostly as its own
component for now, lifting code from `FriendDisplay` which was recently
overhauled. These implementations should eventually be combined somehow
but that's dependent on:
1. Figuring out the styling - friends can display offline users for
which it makes no sense to display the "spectate" button.
2. Figuring out how to handle the different users/presence pathways.
It's mostly a code complexity issue.
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
- Exiting while queueing will now background the search.
- The "queue in background" button changed to "stop queueing".
Exiting while in the "pending accept"/"waiting for players" states will
exit from the queue. There is also a small period during the in-room
state (where the matchmaking screen hasn't been pushed yet but "good
luck" is displaying) where the user cannot exit from the screen. I've
removed the exit confirmation dialogs to streamline the process and
align with this.
https://github.com/user-attachments/assets/8c172502-0624-42cd-ae0c-bb710068267c
After testing out the icon on device for a bit, we decided the osu! logo
was a little too large.
This PR replaces the app icons with a new version where the sizing of
the logo itself is aligned to Apple's recommended boundary.
<img width="198" height="169" alt="Screenshot 2026-01-30 at 2 33 24 PM"
src="https://github.com/user-attachments/assets/c66fb016-9c19-4ff5-814a-6f302ec459ca"
/>
Regressed in d6bf4fd90d.
One very visible instance of this regression is the login form.
https://github.com/user-attachments/assets/5ba10ac5-4cb1-49af-b55c-89cf58ca0b44
The `CommentEditor` usage was discovered with one of my favourite tricks
which is doing
```diff
diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs
index fefe776b01..c17cca726b 100644
--- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs
@@ -42,6 +42,12 @@ public partial class OsuTextBox : BasicTextBox
Margin = new MarginPadding { Left = 2 },
};
+ public new bool Masking
+ {
+ get => base.Masking;
+ set => base.Masking = value;
+ }
+
protected bool DrawBorder { get; init; } = true;
private OsuCaret? caret;
```
and then looking for usages of the setter. That's all due diligence
you're getting here, I'm not auditing every single text box in the game.
And yes, the `CommentEditor` usage is OMEGA dodgy but the change applied
here is the only one that preserves its visual appearance. I'm not
putting in time to fix it.
Probably closes https://github.com/ppy/osu/issues/36492.
This is dumb but it's in large part my stupidity.
To begin with, the immediate direct offending call that causes the
observed symptoms is
a401c7d5e9/osu.Game/Screens/Edit/Components/FormSampleSet.cs (L296)
The reason why this "invalidation" affects sample volume is that in the
framework implementation, the call [removes the relevant sample factory
from the sample store which is an audio
component](5b716dcbef/osu.Framework/Audio/Sample/SampleStore.cs (L65-L72)).
In the process it also [unbinds audio
adjustments](5b716dcbef/osu.Framework/Audio/AudioCollectionManager.cs (L37-L38)),
which *would* have the effect of resetting the sample volume to 100%,
effectively (and I've pretty much confirmed that that's what happens).
Now for why this call sometimes does the right thing and sometimes
doesn't: Sometimes the call is made in response to an *actual* change to
the beatmap skin, which is correct and expected, if very indirect, but
sometimes it is made *over-eagerly* when there is no reason to recycle
anything yet.
One such circumstance is entering the setup screen, which will still
"invalidate" (read: remove) the samples, but the compose tab hasn't seen
any change to the beatmap skin, so when it is returned to, it has no
reason to retrieve the sample again, and as such it will try to play
samples which are for better or worse in a completely undefined state
because they're not supposed to be *in use* anymore.
Therefore, the right thing here would seem to be to take the
responsibility of invalidation from a random component, and move it to a
place that's *actually* correlated to every other component needing to
recycle samples, e.g. `EditorBeatmapSkin` responding to changes in the
beatmap resources via raising `BeatmapSkinChanged`.
Unfortunately, because of the structure of everything, this recycle
needs to go from targeted to individual samples, to nuking the entire
store. The reason for this is that `RealmBackedResourceStore` does not
provide information as to *what* resources changed, it just says that
*the set of them* did.
For the recycle to be smarter, `EditorBeatmapSkin` would need to know
not only which samples were added or replaced, but also which ones were
*removed*, so that users don't hear phantom samples that no longer exist
in the editor later. That would however be a lot of hassle for nothing,
so I just recycle everything here and hope it won't matter.
As to why I could only reproduce this on this one beatmap - I'm not
super sure. The failure does not seem to be specific to beatmaps, but it
may be exacerbated by certain patterns of accessing samples which means
that beatmaps with high BPM like the one I managed to reproduce this on
may just be more susceptible to this.
As a final note, I do realise that this is not fundamentally improving
the surrounding systems and it's still a pretty rickety thing to do.
It's still on the consumers to know and respond to the sample store
recycle and this is likely to fail if a consumer ever doesn't. That
said, I have no brighter ideas at this point in time that won't involve
me spending a week refactoring audio.
Closes https://github.com/ppy/osu/issues/36490.
While I'm out here already taking heat for deleting mod combinations let
me do more of that.
The main problem with the mod combination here, again, is that the
application of the mods does not commute.
- The current behaviour is that TP is applied first, then DA. This is,
again, "enforced" by the mod select overlay implicitly enforcing order
of the mod instances in the global mod bindable to match the display
order of mods inside it.
Even this doesn't "work" correctly as is, because as the bug reporter
points out, if they throw on DA with no changes expecting the map's
default AR to be applied, it still gets halved. This is because DA works
in a way wherein if you don't touch the AR slider, DA does not touch AR.
Which means that the DA slider should *really* be at *half* of the map's
base AR by default in this case because TP is active. How do you program
this?
- The *alternative* behaviour would be that DA is applied first, then
TP. This in turn would mean that the effective range of AR adjustment
offered by DA when TP is active would be halved to [0, 5] (or [-5, 5.5]
with extended ranges). How do you program this?
The above is just client-side concerns, while leaving out the other
giant concern, which is "how do you get every single place in the game
that may want to apply mods to a beatmap to apply them *in the same
order*?". Then extend that to server-side components, then extend that
to every external library that may want to re-implement SR/PP
calculations, etc. etc.
One additional remark:
What the bug reporter *did not* say however, but I am saying, is that
there's an elephant in the room, and that is the Easy mod, which *also*
changes AR, but also *happens* to apply commutatively with Target
Practice simply because both mods are implemented to halve the AR, which
means that the order of application doesn't matter. If I were *really*
bent on being a bad guy and just deleting mod combinations
indiscriminately, I'd delete that one as well. But I'm not doing that.
Effectively closing https://github.com/ppy/osu/issues/21471 as a
wontfix.
The issue is demonstrated by the following test case:
<details>
<summary>patch</summary>
```diff
diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs
index 31498295da..36b4fe5122 100644
--- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs
@@ -55,5 +55,49 @@ public void TestSkipToFirstSpinnerNotSuppressed()
PassCondition = () => Player.GameplayClockContainer.GameplayStartTime > 0
});
}
+
+ [Test]
+ public void TestFreezeFrameAppliedBeforeHidden()
+ {
+ CreateModTest(new ModTestData
+ {
+ Mods =
+ [
+ new OsuModFreezeFrame(),
+ new OsuModHidden(),
+ ],
+ CreateBeatmap = () => new OsuBeatmap
+ {
+ HitObjects =
+ {
+ new HitCircle { StartTime = 3000, Position = OsuPlayfield.BASE_SIZE / 2, NewCombo = true },
+ new HitCircle { StartTime = 5000, Position = OsuPlayfield.BASE_SIZE / 2 },
+ }
+ },
+ PassCondition = () => ((HitCircle)Player.GameplayState.Beatmap.HitObjects[1]).TimeFadeIn == 480
+ });
+ }
+
+ [Test]
+ public void TestFreezeFrameAppliedAfterHidden()
+ {
+ CreateModTest(new ModTestData
+ {
+ Mods =
+ [
+ new OsuModHidden(),
+ new OsuModFreezeFrame(),
+ ],
+ CreateBeatmap = () => new OsuBeatmap
+ {
+ HitObjects =
+ {
+ new HitCircle { StartTime = 3000, Position = OsuPlayfield.BASE_SIZE / 2, NewCombo = true },
+ new HitCircle { StartTime = 5000, Position = OsuPlayfield.BASE_SIZE / 2 },
+ }
+ },
+ PassCondition = () => ((HitCircle)Player.GameplayState.Beatmap.HitObjects[1]).TimeFadeIn == 480
+ });
+ }
}
}
```
</details>
The reason that this is happening is a data dependency. Freeze Frame
modifies `TimePreempt` of hitobjects:
54c0b2c20c/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs (L53)
while Hidden uses `TimePreempt` to set `TimeFadeIn`:
54c0b2c20c/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs (L45)
Therefore the final value of `TimeFadeIn` with these two mods active
depends on the order of application.
The reason why I'm bothering to do this is that I was pinged from the PP
development server about this again in the context of some ongoing
'rework' and I wish to get ahead of this before it becomes a bigger
problem than it already is.
The current order of application of these mods as done in `Player` is
constant, but essentially undefined. I'm not even sure what even
enforces the current order. It's currently Hidden, then Freeze Frame. If
I were to guess, the thing "enforcing" (insert 400 tonne solid lead air
quotes) it is probably the mod select overlay with its "I need to own
all of the mod instances in the global bindable" substitution logic.
I'm already getting pushback for this from the PP server crowd who are
attempting to justify the current "behaviour" by saying that the player
base wants this or by saying that it's already broken so it should
remain that way forever. I am however not willing to accept
[stable-taiko-tier stupidity
again](https://github.com/ppy/osu/pull/27136#issuecomment-1957055402)
and am not willing to accept the complexity this would invite everywhere
else.
I do not see any other easy way of fixing this problem at this point in
time. I had hoped that I could inline the `TimePreempt` read in
`OsuModHidden`, but it's not that easy because it's set in multiple
places.
Existing HDFF scores would probably need to be delisted from the
leaderboards. I'm not even sure I can get myself to pretend to care.
Removes the previous `AppIcon.appiconset` bundle and replaces it with
all of the necessary asset slices to enable all of the Liquid Glass
variants of the app icon on iOS 26, while also preserving backwards
compatibility with older iOS versions.
<img width="1164" height="388" alt="LiquidGlass"
src="https://github.com/user-attachments/assets/7d6e7a90-3fe5-4853-9c63-35144359f49e"
/>
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
This removes the disabled sound, but I think that's fine. If we want
that, it should be handled by `HoverClickSounds` (which I'm currently
intentionally not using because it can be a bit noisy).
Closes#36503.
This was attempted to be fixed by frenzibyte using some hack workaround
logic, but this is the true fix.
Things were never matching due to `UpdateSize` spamming `Resize`
transforms every frame, causing the fade out to complete before
transforms have reached a final state.
TC is a mod that always increases difficulty and is quite similar to HD.
Given we even have diffcalc/pp considerations for it it's time to move
it to the category it belongs.
This doesn't cover any other mods that might need reshuffling too
because TC is the only one that has actual impact on difficulty-based
leaderboards (as in pp) and some people are actively playing it for the
difficulty increase and not just as a fun gimmick.
After a quick search turns out it was difficulty increasing from the
start but was moved to fun in review
https://github.com/ppy/osu/pull/3569#discussion_r300085523 but I don't
really agree with that.
Also as far as I know multimods don't do anything anymore?.. I've put it
into HD multimod for consistency, but can move it to be separate if you
want
Co-authored-by: Dan Balasescu <smoogipoo@smgi.me>
See inline commentary. I don't really have any energy left to provide
anything else, other than maybe a demonstration of how this dies in
framework:
diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneScreenStackUnbindOnExit.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneScreenStackUnbindOnExit.cs
new file mode 100644
index 000000000..c74ce6636
--- /dev/null
+++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneScreenStackUnbindOnExit.cs
@@ -0,0 +1,59 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Screens;
+
+namespace osu.Framework.Tests.Visual.UserInterface
+{
+ public partial class TestSceneScreenStackUnbindOnExit : FrameworkTestScene
+ {
+ [Cached]
+ private ScreenStack screenStack = new ScreenStack();
+
+ [Test]
+ public void TestScreenExitUnbindDoesNotInterruptLoadComplete()
+ {
+ AddStep("set up the scenario", () =>
+ {
+ Child = screenStack;
+ screenStack.Push(new Screen());
+ screenStack.Push(new BrokenScreen());
+ });
+ AddUntilStep("wait to get to target screen", () => screenStack.CurrentScreen, Is.InstanceOf<Screen>);
+ }
+
+ private partial class BrokenSlider : BasicSliderBar<float>
+ {
+ [Resolved]
+ private ScreenStack screenStack { get; set; } = null!;
+
+ protected override void LoadComplete()
+ {
+ // exiting the current screen provokes the behaviour of unbinding all bindables in the screen's subtree
+ screenStack.CurrentScreen.Exit();
+
+ // ...but the following calls should still take correct effect inside `SliderBar`
+ // (namely one consisting of propagating `{Min,Max}Value` into `currentNumberInstantaneous`)
+ // so that it doesn't have its internal invariants violated
+ CurrentNumber.MinValue = -10;
+ CurrentNumber.MaxValue = 10;
+
+ // this notably calls `Scheduler.AddOnce(updateValue)` inside, which will happen *in the imminent future, in the same frame as `LoadComplete()` here.
+ // if the above mutations of `{Min,Max}Value` don't correctly propagate inside the slider bar due to an overly eager unbind, this will cause a crash.
+ base.LoadComplete();
+ }
+ }
+
+ private partial class BrokenScreen : Screen
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ InternalChild = new BrokenSlider();
+ }
+ }
+ }
+}
I attempted to address what I perceive to be the root issue here which
is that `ScreenStack` is allowed to arbitrarily unbind bindables under
drawables which are by all means still in the scene graph. The attempt
consisted of scheduling the unbind until after children of the screen
stack, but that caused 150 game-side tests to fail, seemingly on
something relevant to bindable leases, so I give up.
To explain on the wordy and probably incoherent title: The patient zero
for this was the following discord report:
https://discord.com/channels/188630481301012481/1097318920991559880/1463029073998774375
After information extraction, it turned out that the user manually used
the "Edit externally..." feature to rename a sample to
`normal-hitnormal1.mp3`, thinking (logically, to be fair), that the 1
bank works the same way that all the others do.
It doesn't, samples in the 1 bank do not have a numerical suffix in the
filename - but the logic retrieving the sample sets to display in the
setup tab was not checking that, leading to confusion wherein a sample
would only "work" in the setup tab but not in the actual editor
composer.
Can be tested with [the beatmap provided by the
user](https://discord.com/channels/188630481301012481/1097318920991559880/1463110516573470774).
This ends up with a bit of an undefined behaviour, but it's already a
bit of an edge case (files missing in the `files` folder that are
references in the database).
First and foremost, let's stop the exception. If we allow it to throw,
it's impossible to exit the skin editor in this state.
Closes https://github.com/ppy/osu/issues/36135.
Reported [on reddit of all
places](https://www.reddit.com/r/osugame/comments/1qhnfdn/every_time_i_make_an_adjustment_to_the_hitcircle/).
The reason that this is happening is as follows:
- Changing a combo colour raises `BeatmapSkinChanged`
- `BeatmapSkinChanged` getting raised triggers the sample chooser
dropdown to repopulate its items (as intended and correctly)
- Setting items of a dropdown can also change its `Current.Value`,
because the relevant logic attempts to ensure that `Current.Value` is
valid in line with the new items
- Therefore the `Current.Value` of the dropdown changes from `null` to
sample set `-1` which corresponds to the "Add new..." item as it's the
only item in the dropdown...
- which is indistinguishable from the sequence of events that happens if
the user manually opens the dropdown and clicks the "Add new..." item.
This change sidesteps this entire car crash by just ensuring that even
beatmaps without custom samples have at least set number 1 initialised
(even if it's empty and has no samples). This means that the initial
value of the dropdown is never `null`, and that every time that the
value changes to the set `-1` it actually is due to user action.
Should have known better than to play these dumb games.
It doesn't work in practice, because the skill baseline of an incoming
"contributor" is so wildly variant that any issue that isn't explicitly
a one-liner is automatically not suitable for *some* non-trivial subset
of "new contributor".
The label is also largely used by incoming contributors whose interests
are not necessarily aligned with the project but instead appear to have
other ulterior motives.
Just a couple of things I noticed in passing:
- When changing the configuration setting, things were not reset.
Likewise, if the setting was off the queues would still be added to but
never flushed.
- When the setting is toggled, a stale next notification time was still
present due to the `??=` and lack of resetting. This should no longer be
the case.
The property used here is listed in SignalR documentation:
https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-10.0#bearer-token-authentication
While in practice this probably has zero bearing on anything,
theoretically the way proposed in this commit is more correct.
As the documentation states, with some transports the token may need to
be renewed if it expires, which providing it via a header as done
previously would not achieve, while going through `API.AccessToken`
every time will perform a token refresh if one is needed.
This is the counterpart to
https://github.com/ppy/osu-server-spectator/pull/413. The goal is to
log the value which is seemingly failing to work correctly client-side
as well.
The reason for doing that is two-fold:
- To eliminate possibility of freakazoid issues wherein some computers
miscalculate the version hash somehow
- To eliminate possibility of the version hash somehow getting lost in
transit (e.g. present client-side but no longer present server-side).
- Implement IProvideCursor in SongSelect to hide cursor when background is revealed
- Cursor reappears on mouse movement and hides again after 1 second of inactivity
- Fix MenuCursorContainer to preserve drag rotation state during hide/show cycles
The important one is the required mod selection, whose overlay contents
overlap with the add button. The free mod selection would otherwise be
fine, if not for consistency.
* Fix play button starting wrong beatmap before selection loads
When clicking the osu! cookie (play button) before a newly selected beatmap finishes loading, the previous beatmap would be played instead of the currently selected one.
This was caused by the cookie reading from the global beatmap state which is debounced by 150ms, while the Enter key correctly used the carousel's current selection.
The fix makes the cookie use the same beatmap source as Enter - the carousel's current selection - which is always up-to-date regardless of debounce timing.
Closes#36074
* Use ensureGlobalBeatmapValid() for logo and Enter key actions
* Add test for beatmap selection timing bug
Tests the fix for issue #36074 where clicking the play button immediately after selecting a different difficulty would start the wrong beatmap due to the 150ms selection debounce.
Additionally to fix the options button, I could either cache the
interface in PlaylistsSongSelectV2 or make the interface cache itself. I
went with the latter option.
Previously, there if two consecutive hitobjects were 50ms apart both mechanisms to make sure that the input buttons are alternated would fail.
This produced a replay frame which lifts the current button, followed by a frame which presses the same button again, at the same time as it was lifted.
The key-up frame would always get skipped without frame-stability, leading to hitsounds and hit animations not getting played in the editor.
This is invariably a test fix but also a valid change to make in
isolation.
In short, the problem is thus: Now that the beatmap skin is hooked up to
realm to receive notifications about changed files (which is required
for correctly handling custom samples), tests started failing, because
of the following sequence of events
- Saving a brand new `.osu` to a beatmap set causes
`RealmBackedResourceStore` to fire subscription callbacks because a
new file was added to the set
- This fires `RealmBackedResourceStore.CacheInvalidated`
- Which fires `EditorBeatmapSkin.BeatmapSkinChanged`
- Which would previously fire `EditorBeatmap.SaveState()` and as such
mark the beatmap dirty / modified.
In this scenario this is gratuitous. There's no need to be raising save
states here, a new `.osu` was added to the set that is in a consistent
saved state and nothing actually changed in the beatmap.
However it does not appear sane to attempt to circumvent this with
conditional guards or something, because in cases where files are
added/removed from the set, *there isn't really any reason to take save
states anyway*. The change handler only deals with the `.osu`, any
modifications to any of the other files cannot be undone anyway.
Therefore, only keep the state save to the one change to beatmap skin
that *can* actually be sanely undone which is changing combo colours.
Among others, this features a scary-looking change wherein
`WorkingBeatmap` now exposes a `RealmAccess` via
`IStorageResourceProvider`. This is necessary to make
`RealmBackedResourceStore` actually start firing callbacks when the
edited beatmap's skin is modified by adding new samples to it.
This reverts to the way stable does things. Smooth seeking is nice and
all, but slows things down and doesn't give the instant reponse that
mappers are used to.
This should fix some performance issues regarding seeking as it no
longer tries to render large portions of the map during the seek
operation.
Note that I've also forced the summary timeline to always non-smooth
seek. It was bugging out in weird ways when doing smooth seeks and I
don't want to attempt to fix it.
I found this way too high in the editor, not giving enough feedback when
seeking.
To recap, this was put in place to avoid glitchy sounding audio. Such
glitchiness only occurs with *very* fast seeks. This is still well
within sane range.
`dotnet format` expects me to put a space between the spread `..` and
the expression after which is UTTERLY STUPID AND UGLY AND WRONG AND
CANNOT BE `.editorconfig`'d BECAUSE [INSERT EXCESSIVELY LONG BLEEP].
God I hate when good features are kneecapped by this sort of stupidity.
Resolves https://github.com/ppy/osu/discussions/36107.
The replay linked in the aforementioned discussion happens to contain
the magic sequence for ZIP headers in the binary stream (50 4b 05 06)
which led to it getting classified as a ZIP with no entries and
completely bogus everything in the header at
c8b18acd4d/osu.Game/Database/ImportTask.cs (L51-L56)
and then finally dying of
2025-12-23 07:26:45 [error]: [?????] Model creation of replay-osu_2040232_4828629841.osr failed.
2025-12-23 07:26:45 [error]: System.InvalidOperationException: Sequence contains no matching element
2025-12-23 07:26:45 [error]: at System.Linq.ThrowHelper.ThrowNoMatchException()
2025-12-23 07:26:45 [error]: at System.Linq.Enumerable.First[TSource](IEnumerable`1 source, Func`2 predicate)
2025-12-23 07:26:45 [error]: at osu.Game.Scoring.ScoreImporter.CreateModel(ArchiveReader archive, ImportParameters parameters)
2025-12-23 07:26:45 [error]: at osu.Game.Database.RealmArchiveModelImporter`1.importFromArchive(ArchiveReader archive, ImportParameters parameters, CancellationToken cancellationToken)
at
554961036e/osu.Game/Scoring/ScoreImporter.cs (L46)
The test scene doesn't exercise the custom sample playback, but I hope I
can be forgiven for this as setting up a custom editor beatmap just for
this to work is rather cumbersome.
Even without the ID resetting logic before `Populate()` getting broken
and annotated with a TODO, this was kinda stupid. Why was purging logic
allowed to run *using a not-yet-completely-validated online ID of the
set*?
This is the other part of hopefully fixing scenarios like
https://osu.ppy.sh/community/forums/topics/2162457?n=8 (hopefully with
no babies poured out in the mean time, but only time will tell).
The end of `Populate()` is too early to do any validation of online IDs,
as population happens in `PostImport()` via `ProcessBeatmap`.
Additionally, this modifies the check to also include the set's ID. This
is one part of curtailing issues like
https://osu.ppy.sh/community/forums/topics/2162457?n=8 - the featured
artist template beatmap in question has a single difficulty with a bogus
positive beatmap set ID and a beatmap ID of 0, and my opinion is that
this sort of situation should for all intents and purposes cause the
beatmap set's ID to be reset.
A few facts of life:
- Guest difficulties are at this point a staple of mapping.
- People are very much used to flinging `.osu`s around (because there's
no better alternative).
- Currently there are two ways to get an `.osu` out of lazer. You can:
- Export the beatmap as "compatibility" to an `.osz`, then
transmogrify the `.osz` to a `.zip`, then extract the `.zip`, then
pluck out the `.osu`. This is the "correct" way to make sure stable
works, but is also stupidly arcane.
- Use "edit externally" to mount the beatmap files to disk, then
copy-paste out the `.osu`. This is the *wrong* way to make sure
stable works, because the mounting process exposes the raw "for
editing" format with features stable doesn't support, but it the
actual easy one.
- Reports about guest difficulties exported from lazer "working wrong on
stable" are prevalent. Probably mostly because of the preceding point.
What this PR does is introduce a *third* method to export an `.osu`,
which is designed to be both the easiest one yet *and* correct. I am
hoping this will curb the complaints until support for direct submission
of guest difficulties is added - which I still hope to see, but it will
be a significant effort *client-side* (the server side has been ready
for years now).
And yes, you will notice that much of the code added in
`LegacyBeatmapExporter` related to manipulation of the path is
copy-pasted from `LegacyExporter`. I don't care enough to invent
protected / abstract / whatever else OOP faff for something that may not
survive review and is mostly a weird semi-temporary wart.
In order of severity:
- You could actually click on the textbox portion of a disabled textbox,
focus it, select text, input stuff, and commit, which would die
on the spot.
- The slider part had no visual indication that it's not interactable
anymore.
Some are not immediately relevant to the stacking issue because they
fail both before and after it, just less so after the stacking issue
(half-)fix, and as such have been commented out for the time being.
The new ones added in this change don't play the UI sounds, likely
because they make it nigh impossible to actually hear what the gameplay
samples will sound like.
This removes the same sounds from the existing buttons to match.
Closes https://github.com/ppy/osu/issues/36052.
Not much more to say here. Until now the `PreEmpt` typing discrepancy
has likely gone unnoticed since it's usually only used in visual usages.
First part of https://github.com/ppy/osu/issues/36039, see also
https://github.com/ppy/osu/discussions/33784.
The goal is to add this back to the set panels too, but that one is more
complicated than this if you can believe it (because it requires being
able to tell which other difficulties of the set are filtered out to
fade them out). I also foresee the display logic to vary significantly
there.
Of note, for this to work in beatmaps-split-apart mode, this requires a
change of behaviour in the filtering logic. Old song select when given a
"selected beatmap set" would *include* that in results regardless of the
filter, but that doesn't work for beatmaps-split-apart for reasons that
are hopefully obvious, so this changes the behaviour to *entirely
bypass* the filter and just show the set. Unsure how angry will people
be with that.
Also of note, when in the scoped mode, altering any filter criteria will
dismiss the scope.
The previous multiplier was supposed to account for seeking backwards
being "slower" because the track is playing forwards, but it really
didn't work amazingly.
Rather than trying to pull off some magic, let's just ensure that seeks
in both directions feel correct, even if that means that time gradually
moves forwards.
Closes https://github.com/ppy/osu/issues/35998 (mostly).
Of note, this still doesn't match stable completely. I attempted to
implement the full stable seeking algorithm but it's objectively worse
in other scenarios, so I'd rather just tweak what we have until the
majority of users are happy.
This didn't end up feeling as good as I hoped. Will revise at a later
stage, adding beat sync in another way that isn't jank transforms on the
progress bars.
Also closes https://github.com/ppy/osu/issues/35972.
* Fix(Matchmaking): Prevent mod track adjustments from applying BGM speed in queue
* replaced modification of MusicController directly with a property change.
* formatted
Addresses https://github.com/ppy/osu/discussions/36004.
Not adding localisation because the previous implementation was
`.ToString()`ing anyway.
Would have made the abuse e-mail a link but `mailto:` doesn't work with
`MessageFormatter` and I don't want to go into that right now.
The message *almost* matches stable. The "almost" is because it doesn't
mention the `/ignore` chat command. I was just going to implement the
command, but I went to check what it does, and backed away slowly
because it has like weird scoping to chat, highlights, and PMs, so
`nope.avi`.
Closes https://github.com/ppy/osu/issues/36003.
The duplicated `RulesetSkinProvidingContainer` is unfortunate but it's
either this or I start doing proxy shenanigans.
"Closes" https://github.com/ppy/osu/issues/35920.
The button can't easily work anyway since it's not guaranteed that the
spectating user has all of the frames of the replay (think entering
spectate midway through a play).
This matches the results screen in spectator too.
* Add failing test for copy->paste not being idempotent
* Ensure all elements on default skins use fixed anchors
`UsesFixedAnchor` defaults to false, i.e. closest anchors. Combined with
manual anchor / origin specs on some drawables, this would get default
skins into impossible states wherein a drawable would use "closest
anchor" but also explicitly specify anchor / origin that aren't closest,
which horribly fails on attempting to copy and paste.
Frankly shocked this has gone unnoticed for this long, and I regret not
vetoing this "feature" more every time I see its tentacles spread to
produce breakage of levels yet unseen.
Does this commit contain major levels of suck? For sure. Do I have any
better ideas that wouldn't consist of a multi-day rewrite or deletion of
this "feature"? No.
* Fix skin editor always applying closest anchor / origin on paste regardless of whether the component uses fixed anchor
Self-explanatory. Should close https://github.com/ppy/osu/issues/29111
along with previous commit.
More or less covers the first page of client sentry issues sorted by
volume, all of which is pretty much useless for anything because it's
client-specific-failure noise.
More or less covers the first page of client sentry issues sorted by
volume, all of which is pretty much useless for anything because it's
client-specific-failure noise.
Unsure about this one, but I find the preceding commit to be very
lacking in explaining to the user why the editor don't work. Shining
some things red may help aid understanding.
Idk why, but in my first commit here I just commented out the lines
restricting DB schema suffixes to debug builds only, and before all that
mess there was a "TODO: fix".
I'm only doing this for sake of tools like BeatmapExporter and to not
clog up disk space when newer schema versions arrive.
This should work well with existing installations (hopefully)
Closes https://github.com/ppy/osu/issues/35807.
The reason this closes the aforementioned issue is as follows:
Taking https://osu.ppy.sh/beatmapsets/1236180#osu/4650477 as the
example, we have:
```
minBeatLength = 342.857142857143
maxBeatLength = 419.58041958042003
mostCommonBeatLength = 342.85700000000003
```
Note that `mostCommonBeatLength < minBeatLength` here.
Taking the inverse of that to compute BPM, we get
```
minBpm = 174.99999999999991
maxBpm = 142.99999999999986
mostCommonBpm = 175.00007291669704
```
which without DT present doesn't do anything bad, but when DT is
engaged (and thus BPM is multiplied by 1.5), midpoint rounding causes
the min BPM to become 262, and the most common BPM to become 263.
* Fix mania editor timestamp generation being culture-dependent
Mostly closes https://github.com/ppy/osu/issues/35809.
* Add failing test for notes with fractions
* Round note time when copying out timestamp & apply half-millisecond tolerance when parsing
Closes the rest of https://github.com/ppy/osu/issues/35809.
One issue here was that while the timestamp generation would allow
fractional object timestamps to be output, the parsing (via
`selection_regex`) would *reject* fractional timestamps, therefore
making lazer incompatible even with itself.
The other is that rounding is probably fine to do anyway for
interoperability with stable. I'd hope nobody actually *needs*
sub-millisecond precision but I'm ready to be proven wrong by some
aspire jokester.
* Specify invariant culture when writing out combo indices to editor timestamp in other rulesets
Pretty sure this is just a much-of-muchness because it's integers but
might as well if I'm spending time here already.
Addresses https://github.com/ppy/osu/discussions/35811 I guess.
Will only work for local leaderboards for now but maybe good enough for
what is essentially a 5 minute job?
Can be made to work with online leaderboards too I guess if need be.
This is dodgy as hell but `ShortName` is completely derived from
`OnlineID` anyway so there should be no valid reason to ever attempt to
serialise it anyway.
Intended to match the rest of the UI which is less rounded these days.
See inline comment for reason for not matching `FormControl` corner
radius just yet.
Closes https://github.com/ppy/osu/issues/35721.
I worry that straight up removing the nuke and not adding any channel
leave calls in exchange is going to leave tourney client users
with the *inverse* problem of being joined into a gorillion channels
from multiplayer matches they broadcasted, so this attempts to strike a
reasonable balance.
* Load all beatmaps in bulk for SubScreenBeatmapSelect
* Fix tests no longer working due to drawable changes
* Remove test that no longer makes sense
* Split matchmaking panel into subclasses for each panel type
* Adjust tests to match new structure
* Add `ConfigureAwait`
* Display loading spinner while beatmaps are being fetched
* Fix test failure
* Load playlist items directly in `LoadComplete`
* Convert `MatchmakingSelectPanel` card content classes into nested classes
* Wait for panels to be loaded before operating on them
* Add ConfigureAwait()
---------
Co-authored-by: Dan Balasescu <smoogipoo@smgi.me>
* Add failing test coverage for layered hit samples not playing in mania when beatmap is converted
Adding the `osu.Game.Rulesets.Osu` reference to the mania test project
is required so that `HitObjectSampleTest` base logic doesn't die on
f0aeeeea96/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs (L88-L91)
* Fix layered hit sounds not playing on converted beatmaps in mania
Compare
f9e58b4864/osu!/GameplayElements/HitObjects/HitObject.cs#L476-L477.
In case of converted beatmaps, the last condition there
(`BeatmapManager.Current.PlayMode != PlayModes.OsuMania`) fails,
and thus layered hitsounds are allowed to play.
* Add failing test coverage for mania beatmap conversion assigning wrong samples to spinners
* Fix mania beatmap conversion assigning wrong samples to spinners
A spinner is never `IHasRepeats`. It was a dead condition, leading to
the hitobject generating fallback `NodeSamples`, which in particular
feature a silent tail which stable doesn't do.
Noticeably, stable also appears to force the head of the generated hold
note to have no addition sounds:
f9e58b4864/osu!/GameplayElements/HitObjects/Mania/SpinnerMania.cs#L86-L89
* Add failing test coverage for file hit sample not falling back to plain samples if file missing
* Allow `FileHitSampleInfo` to fall back to standard samples if the file is not found (or not allowed to be looked up)
I'm honestly not 100% as to how closely this matches stable because I
reached the point wherein I'd rather not look at stable code anymore, so
as long as this passes tests I'm fine to wait for someone else to report
new breakage.
* Use alternative workaround for lack of osu! ruleset assembly in mania test project
* Fix encode stability test failures
Probably closes https://github.com/ppy/osu/issues/35650.
Realm slow, episode 23894. I can't reproduce freezes as big as the video
in the issue is showing but 'realm slow' is 99% the culprit, because
affected user's database is not small.
Because I just wasted 30 minutes trying to debug why a skin provided by
a user in an issue thread was failing to deserialise, only to realise
halfway through that the deserialisation error I was seeing was *from
the fallback path and thus a complete red herring*.
Closes https://github.com/ppy/osu/issues/35651.
The reproduction steps provided in the issue are too complex even. In my
testing all you need to do is go into editor, replace the background via
external editing, and exit out to song select; you'll immediately see
loss of selection on the carousel, the set panel still using the old
background, and eventually a crash when you attempt to re-select any of
the difficulties of the edited set.
`HandleItemsChanged()` - an optimisation aiming to reduce the number
of redundant re-filters due to minor changes to realm models that aren't
visible to the user anyway - ignoring changes to `BeatmapInfo.ID` after
re-entering song select post-external edit meant that song select would
retain stale beatmap models that no longer existed in the realm
database, thus failing refetch attempts via `GetWorkingBeatmap()` or
8f6f859c15/osu.Game/Screens/SelectV2/FooterButtonOptions.cs (L56-L57)
RFC. Written to address
https://osu.ppy.sh/community/forums/topics/2150023.
Few other things we might want to happen here:
- pause the track when starting the drag
- figure out what to do when a drag is held while the track changes in
the background (which was impossible to happen before this)
but I want to see the reaction to this first.
Clicking the button opens the browser, on the "new topic" page inside
the help forum. Web can now correctly read the build number of the
client since https://github.com/ppy/osu-web/pull/12478 so I see
no reason not to.
Minimal effort implementation. Stemmed from discussion in
https://discord.com/channels/90072389919997952/299846395031060480/1437368033734561792.
Not really interested in putting more effort into this at this point, if
this is not considered acceptable then just close the PR and this can be
revisited more properly at a later date.
* Add failing test coverage for blocking users not removing their messages from public channels
* Fix messages from blocked users being visible in public channels
Closes https://github.com/ppy/osu/issues/35633.
It appears that the expectation from web here is that messages from
blocked users should be excised client-side. Compare:
12dd504255/resources/js/chat/conversation-view.tsx (L104)
This implementation won't *restore* the messages after a block and
unblock, but I kind of... don't care if I'm honest with you? Making that
happen will result in a bunch of complications for no reason, so I'm
fine waiting for anyone to complain about it.
RFC, lowest effort solution for https://github.com/ppy/osu/issues/34979.
The `SkinImporter` conditional *is* hella ugly, but anything less ugly
will require taking a hammer to structures. Maybe passing version via
the import flow, maybe even trying to make the `EnsureMutableSkin()`
flow somehow attempt to read the `skin.ini` that's in resources. No
idea.
Properties from `skin.ini` that were defaults or that lazer can't
(won't ever?) understand snipped.
`VerificationFailureResponse.RequiredSessionVerificationMethod` not
being nullable means that if it was missing in the verification
response, it would not be `null` but default to `TimedOneTimePassword`
instead, therefore showing TOTP-related error messages to users that
never enabled it rather than the user-facing message they were supposed
to.
Most easily tested on a local full-stack environment with
```diff
diff --git a/app/Libraries/SessionVerification/MailState.php b/app/Libraries/SessionVerification/MailState.php
index 305a2794ec0..3c2d15f335b 100644
--- a/app/Libraries/SessionVerification/MailState.php
+++ b/app/Libraries/SessionVerification/MailState.php
@@ -14,7 +14,7 @@ use Carbon\CarbonImmutable;
class MailState
{
- private const KEY_VALID_DURATION = 600;
+ private const KEY_VALID_DURATION = 10;
public readonly CarbonImmutable $expiresAt;
public readonly string $key;
```
applied so that you don't have to wait 10 minutes to trigger the
failure.
Issue was bisected to [this commit](6f1664f0a6)
This change in the commit outlined is what caused the issue:
```diff
BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
{
Clock = DrawableRuleset.FrameStableClock,
ProcessCustomClock = false,
- Breaks = working.Beatmap.Breaks
+ BreakTracker = breakTracker,
},
```
`BreakTracker` always initializes breaks as `new Period(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION);` leaving room at the end to account for the fade before resuming gameplay.
Because of this, changing the `BreakOverlay` to use a `BreakTracker` instead of the original beatmap breaks caused each break to be `BREAK_FADE_DURATION` shorter than it was originally - which in this case is 325ms - leading to the discrepancy between the background fadeout and the overlay fadeout.
Since the current behavior is 'correct', aligning the overlay with the rest of the beatmap such as background fadeout, I changed the timing to account for the shorter duration instead of revert the overlay initialization.
* Add new API property backing for tiered rank
* Slightly refactor `ProfileValueDisplay` for direct access to things that will need direct access
* Extract separate component for global rank display
* Add tiered colours for global rank
* Add Github link button to wiki overlay header
* Localize jump link string
* Mark ILinkHandler dependency as nullable
* Make the button actually look like it does on the website
* Use existing web string instead of inventing a new one
* Bind value change callback more reliably
---------
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
This was primarily written to fix
https://github.com/ppy/osu/issues/35538, but also incidentally targets
some other scenarios, such as:
- When switching from artist filtering to title filtering, selection
sometimes would stay at the group under which the selection's artist
was filed, rather than moving to the group under which the selection's
title is filed (in other words, the group that *the selection is
currently under*).
- When simply assigning a beatmap to a collection such that it would
be moved out of the current group, the selection will now follow to
the new collection's group rather than staying at its previous
position.
Whether this is desired is highly likely to be extremely situational,
but I don't want to introduce complications unless it's absolutely
necessary.
This has a significant performance overhead because
`CheckModelEquality()` isn't free, but it doesn't seem horrible in
profiling.
Calling `HandleItemActivated()` rather than its intended 'parent method'
of `Activate()` meant that selection state was not correctly
invalidated:
819da1bc38/osu.Game/Graphics/Carousel/Carousel.cs (L157)
which in turn meant that carousel item Y positions would not be
recalculated correctly after the group was expanded, which meant that
the items would become
- visible,
- stuck to the bottom of the expanded group,
- one on top of another.
Which is not something that's going to perform well.
Certified OOP moment.
Fell out when attempting
https://github.com/ppy/osu-server-spectator/pull/346.
Functionally, if a true non-`HubException` is produced via an invocation
of a spectator server hub method, this doesn't really do much - the
error will still log as 'unobserved' due to the default handler, it will
still show up on sentry, etc. The only difference is that it'll get
handled via the continuation installed in `FireAndForget()` rather than
the `TaskScheduler.UnobservedTaskException` event.
The only real case where this is relevant is when the server throws
`HubException`s, which will now instead bubble up to a more
human-readable form. Which is relevant to the aforementioned PR because
that one makes any hub method potentially throw a `HubException` if the
client version is too old.
Obviously this does nothing for the existing old clients.
* Apply some renames & drawable names for visualiser
Optional but really helps me make heads of tails as to what anything is
here.
Like really, multiple variations of `footerContent` inside a
`ScreenFooter` class, with zero elaboration that it's really content to
do with *overlays*...
* Fix screen footer overlay content being pushed to right during fade-out
- Closes https://github.com/ppy/osu/issues/35203
- Supersedes / closes https://github.com/ppy/osu/pull/35468
Closes https://github.com/ppy/osu/issues/35003.
Bit dodgy to use `CurrentSelectionItem` for this. Ideally I would use
the global `Beatmap.IsDefault`, but I kind of don't want to violate the
rule that `BeatmapCarousel` shouldn't have direct access to the global
beatmap. And this seems to work, so... maybe fine to use until it
doesn't?
- Below 20 custom sample sets, they are shown as ternary buttons.
- Above 20 custom sample sets, they are shown in a dropdown (yes there
are actual cases of this as I've been informed by the NAT; one example
being https://osu.ppy.sh/beatmapsets/1018061#osu/2197383)
As a bonus, to make determining what the heck is actually changing when
adjusting these controls, the full set of applicable sounds now plays on
adding/removing additions, changing their banks, as well as changing the
custom set (if any).
For now there are no user-facing controls to add the samples to the map
yourself, you have to know how to name the `.wav`s and edit-externally
them in yourself. *For now.*
This is actually possible in current usages if you e.g. toggle "use
original metadata" on/off which will change the width of the underlying
sprite texts. Or by setting window size. Pick your poison.
Instead of truncating.
Addresses https://github.com/ppy/osu/discussions/35404.
The one "tiny" problem is that the "click to search" functionality of
these texts is maybe a bit worse now, because the clickable target is
now the full width of the wedge rather than autosized to the text.
Salvaging this is *maybe* possible, but *definitely* annoying, so I'd
rather not frontload it.
Is this lazy? Sure it is. Friends and blocks do the same thing, though,
and I'm not overthinking this any more than I already have.
Being smarter here would likely mean being more invasive with respect to
listening in on all outgoing API requests and silently updating
favourites on that basis. Which is "smart" but also complicated.
Something I've asked to be done for a long time. Relevant because I've
complained about this on every addition of a new piece of user-local
state: friends, blocks, and now favourite beatmaps.
It's just so messy managing all this inside `APIAccess` next to
everything else, IMO.
The "partial" leaderboard logic in `SoloGameplayLeaderboardProvider`
always assumed the online fetch would request 50 scores, which is no
longer the case after https://github.com/ppy/osu/pull/33100.
This is a set of model changes which is supposed to facilitate support
for custom sample sets to the beatmap editor that is on par with stable.
It is the minimal set of changes. Because of this, it can probably be
considered "ugly" or however else you want to put it - but before you
say that, I want to try and pre-empt that criticism by explaining where
the problems lie.
Problem #1: duality in sample models
---
There is currently a weird duality of what a `HitObject`'s samples will
be.
- If an object has just been placed in the editor, and not saved /
decoded yet, it will use `HitSampleInfo`.
- If an object has already been encoded to the beatmap at least once, it
will use `ConvertHitObjectParser.LegacyHitSampleInfo`.
As long as that state of affairs remains, `HitSampleInfo` must be able
to represent anything that `LegacyHitSampleInfo` can, if feature parity
is to be achieved.
Problem 2: The 0 & 1 sample banks
---
Custom sample banks of 2 and above are a pretty clean affair. They map to
a suffix on the sample filename, and said samples are allowed to be
looked up from the beatmap skin. `Suffix` already exists in
`HitSampleInfo`.
However, the 1 custom sample bank is evil. It uses *non-suffixed*
samples, *allows lookups from the beatmap skins*, contrary to no bank /
bank 0, which *also* uses non-suffixed samples, but *doesn't* allow them
to be looked up from the beatmap skin.
This is why `HitSampleInfo.UseBeatmapSamples` has been called to
existence - without it there is no way to represent the ability of using
or not using the beatmap skin assets.
As has been stated previously in discussions about this feature, it's
both a *mapping* and a *skinning* concern.
There are many things you could do about either of these problems, but I
am pretty sure tackling either one is going to take *many* more lines of
code than this commit does. Which is why this is the starting point of
negotiation.
The equality check that was supposed to replace the read of
`CurrentSelectionItem` showed up as a hotspot in profiling.
The selection updating code looks a little stupid after this, but all in
the name of performance...
Beatmap panels can be visible for very brief instants.
`PanelSetBackground` has a backstop to prevent expensive background
loads which is based on the position of the panel relative to centre of
screen.
However, retrieving the working beatmap that *precedes* any of that
expensive background load logic, is *also* expensive, and *always* runs
even if a panel is visible on screen for only a brief second. Therefore,
by moving some of that background load delay towards delaying retrieving
the working beatmap, we can save on doing even more work, which has
beneficial implications for performance.
I had previously made it invariant in
https://github.com/ppy/osu/pull/32837, and in another instance of past
me being an asshole, I can't actually find the reasoning for this at
this time.
That said, you'd be excused for thinking "why does this matter"? Well,
this will fix https://github.com/ppy/osu/issues/35381, because that
failure only occurs when the user's culture is set to one that doesn't
use a decimal point (.) but rather a decimal comma (,). This messes with
framework, which uses the *current* culture to check for decimal
separator rather than invariant:
d3226a7842/osu.Framework/Graphics/UserInterface/TextBox.cs (L106-L111)
An alternative would be to change framework instead to always accept the
invariant decimal separator.
God I hate this culture crap.
* SSV2 : Replace Mark as Played with Remove from Played if already played
* Remove checks of BeatmapInfo.LastPlayed for DateTimeOffset.MinValue
* Make FooterButtonOptions use a RealmLive<BeatmapInfo> and act on review comments
* FIXUP: Detach BeatmapInfo before passing it to FooterButtonOptions.Popover
---------
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
* Remove unnecessary information from matchmaking beatmap panel
* Move avatar overlay inside card for better layout
* Allow higher jumping when jumping in succession
* Exclude player panel avatars from masking
* Adjust player panel animations a bit further
* Add avatar-only display mode
* Fix round warmup test not working
* Remove dead test scenes
* Fix edge case where users are added to not-yet-loaded card
* Decouple `PlayerPanel` from `UserPanel`
* Fix remaining test failure (and rename test to match new naming)
From local testing on release build (such that online beatmaps are
accessible) with a large database it seems that maybe this'll help with
recurrent complaints of 'stutters'.
Co-authored-by: Dean Herbert <pe@ppy.sh>
I'm not exactly sure on the reproduction scenario here, but I have had
switching ruleset with converts disabled crash on me a few times
today. It appears to happen sometimes when after the switch the expanded
group no longer exists in the set mapping, because a filter just ran and
removed that group from set of possible groups because there'd be no
beatmaps under it.
I tried to manufacture a quick test but it's not a quick one to test
because filtering intereference is required to reproduce, I think.
I will try again on request but I mostly just want to get this fix out
ASAP before I finish up for the day.
Because the detached store exists and has a chance to actually
semi-reliably intercept a beatmap update operation, I decided to try
this. It still uses a bit of a heuristic in that it checks for
transactions that delete and insert one beatmap each, but probably the
best effort thus far?
Notably old song select that was already doing the same thing locally to
itself got a bit broken by this, but with some tweaking that *looks* to
be more or less harmless I managed to get it unbroken. I'm not too
concerned about old song select, mind, mostly just want to keep it
*vaguely* working if I can help it.
Addresses https://github.com/ppy/osu/discussions/35182.
The source as it is would have you believe that this is correct and
intentional but I'm not so sure about that. For one thing, I
cross-checked stable, and sure, missing tiny droplets does change the
state to fail over there. For another, the guard in master at
5e642cbce7/osu.Game.Rulesets.Catch/UI/Catcher.cs (L250)
is very suspicious, given that it is dead in cause of tiny droplets
because of a preceding guard:
5e642cbce7/osu.Game.Rulesets.Catch/UI/Catcher.cs (L227-L228)
Looking into blame, the tiny droplet guard originates at
e7f1f0f38b, wherein it looks to have been
aimed at handling *hyperdash* state specifically. Later, the logic has
been moved around and around like five times; at
7069cef9ce, the catcher animation logic
was added *below* the hyperdash-aimed guard without the comment being
updated in any way; 5a5c956ced moved the
logic from `CatcherArea` to `Catcher`, while simultaneously changing
the inline comment to no longer mention hyperdashing; and finally,
1d669cf65e added specific testing of tiny
droplets not changing catcher animation state, which I wager to be
back-engineered from the implementation as-it-was rather than
supported by any actual ground truth.
For additional reference:
- catcher animation logic in stable is at 67795dba3c/osu!/GameModes/Play/Rulesets/Fruits/RulesetFruits.cs#L411-L463
- hyperdash application logic in stable is at 67795dba3c/osu!/GameModes/Play/Rulesets/Fruits/RulesetFruits.cs#L165-L171
Addresses https://github.com/ppy/osu/issues/33443, maybe.
I considered adding tests but they'd likely be janky and take a long
time to write, so decided against until there's a demand for it.
Closes https://github.com/ppy/osu/issues/34062.
The root cause of the issue is that `OnEntering()` calls
`onArrivingAtScreen()`, which calls `ensureGlobalBeatmapValid()`, which
would call `checkBeatmapValidForSelection()` with a `FilterCriteria`
instance retrieved from the `FilterControl`.
The problem with that is at the time that this call chain is happening,
`FilterControl` is not yet loaded, which means in particular that it has
not bound itself to the config bindable, as that happens on
`LoadComplete()`:
bff07010d1/osu.Game/Screens/SelectV2/FilterControl.cs (L198)
To resolve this, retrieve the bindable in `SongSelect` itself, which
ensures it is valid for reading at the time the above call chain
happens.
Because it is 99% sure that doing so will fail and spam the user with
"this beatmap doesn't match the online version" notifications, and
because the map status is "locally modified", they should be pretty
aware of that already. This fixes the primary mode of the failure that
https://github.com/ppy/osu/pull/35173 was attempting to hack around.
This will have regressed somewhere around the time that BSS went live,
because at that point the editor stopped resetting online IDs for
beatmaps that got locally modified, making the `beatmapId <= 0` guards
no longer prevent attempts of submission.
A relatively recent regression. It's maybe not a huge one, in that it
probably doesn't matter all that much, but it is somewhat important to
keep the "locally modified" status of the set for as long as possible.
One reason for that is that keeping the "locally modified" status will
pull up a dialog when the user attempts to update the beatmap, warning
them that they will lose their local changes - this dialog will not show
if the online lookup flow is allowed to overwrite the map status with
something else.
Closes https://github.com/ppy/osu/issues/34810.
The reason why I touched it in this direction and not the other is only
because the standalone panel positioning of the button was touched last
in 92ed964627, thus I changed the set
panel to match that.
Closes https://github.com/ppy/osu/issues/35113.
Regressed in dfed564bda - setting
`Carousel.CurrentSelection` was not all that
`requestRecommendedSelection()` was doing there...
A potential point of discussion is whether this global beatmap switch
should be debounced or instant. I'm not sure I have a particularly
well-formed opinion on that. One argument in favour of not debouncing is
that if you look closely at the left side of the screen while the
debounce is in progress, you can still sort of see the broken behaviour
happen - it just doesn't stay there forever.
Thankfully `ensureGlobalBeatmapValid()` being called in every scenario
on screen suspension prevented this bug from being any worse than it is
right now.
Closes https://github.com/ppy/osu/issues/35010.
The issue here does not reproduce consistently, and is more or less
random in presentation. That said, using a large enough realm database
more or less ensures that the issue will present itself (in testing on a
large realm db, the failure rate is around ~50%).
This actually regressed in https://github.com/ppy/osu/pull/34842. The
core failure in this case is here:
fd412618db/osu.Game/Screens/SelectV2/BeatmapCarousel.cs (L161)
The `CheckModelEquality()` call above is comparing two `BeatmapInfo`s,
but a84c364e44 changed the
`BeatmapInfo`-comparing path of `CheckModelEquality()` to use
`GroupedBeatmap` instead. Due to this, `CheckModelEquality()` falls back
to reference equality comparison for `BeatmapInfo`s. When that reference
comparison fails, the carousel stops detecting that the current
selection was deleted from under it correctly, and therefore the
proximity-based selection logic never runs.
Due to the human-obvious mechanism of failure and relatively easy
manual reproduction I've decided not to try and add tests for this,
as they are likely to take a long time to write due to the mechanism
of failure being incorrect use of reference equality specifically. That
said, I can try on request.
Probably closes https://github.com/ppy/osu/issues/35138. I'm not sure. I
only got the issue to reproduce once, on dev, using a very large
archive that was uploading really slowly, and then never again.
The working theory is that basically handling of `progressSampleChannel`
is quite dodgy and it could possibly, in circumstances unknown, be
allowed to play forevermore after transitioning to failed / canceled
state. Success state does not get this treatment because it has special
logic to set progress to 1.
11 is excessive. There can ever be at most 9 of these panels, ever,
because there are at most 9 possible letter grades at this time (F, D,
C, B, A, S, S+, SS, SS+).
Easiest way to make this work without rewriting the layout logic.
I think it makes sense to have the button still exist there but not be
usable on certain screens.
* Change maximum UR estimation + buff rhythm
* Penalty for classic ezhd
* Buff mono bonus to counterbalance logic fix
* New miss penalty + slightly nerf length bonus
* Adjust rhythm values
* Adjust penalty and buff high SR acc
* Exclude HDFL from hidden reading penalties
* Make comment a lil nicer
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
Closes https://github.com/ppy/osu/issues/34959.
`ArgonCounterTextComponent` is pretty terrible and prevents being able
to fix the issue easily. The core issue is that this is the first
instance of the component's usage where the label text can be longer
than the counter in the X dimension, so the total width of any counter
is equal to max(label width, counter width), and the label will be
aligned to the left of that width, while the counter will be aligned to
the right of that width.
The fix sort of relies on the fact that I don't expect *any* consumer of
`ArgonCounterTextComponent` that meaningfully uses the wireframe digits
to want the non-wireframe digits to be aligned to the *left* rather than
the right. It's not what I'd expect any segmented display to work.
(There are usages that specify `TopLeft` anchor, but they usually
display the same number of wireframe and non-wireframe digits, so for
them it doesn't really matter if the digits are left-aligned to the
wireframes or not.)
I'm doing this silently to see if any users complain without being told
about the change. Without this, when holding left arrow for short
bursts, precisely *one* beatmap change happens before actual key repeat
kicks in, which feels really weird (updates the leaderboard / background
unexpectedly).
This is the simplest way to resolve the issue, so if users aren't
offended by it I think we should commit to it.
Personally it's still fast enough to not annoy me at all.
I can't see how this can happen in a normal flow, so just doing it as a
safety measure. Pointed out in https://github.com/ppy/osu/issues/34940
but likely due to a third party fuck being loaded.
Regressed with https://github.com/ppy/osu-server-spectator/pull/311.
As it turns out, the method not just resets ready states but also the
beatmap availabilities. So we have to send it again here.
I have a feeling this is also broken in standard multiplayer in some way
or another.
d4b357dfa0 contains a sneaky regression.
The previous code read:
if (CurrentSelection != null && CheckModelEquality(beatmap, CurrentSelection))
RequestSelection(matchingNewBeatmap);
and the new one reads:
if (CurrentSelection is GroupedBeatmap currentBeatmapUnderGrouping)
{
var candidateSelection = currentBeatmapUnderGrouping with { Beatmap = beatmap };
if (CheckModelEquality(candidateSelection, CurrentSelection))
RequestSelection(candidateSelection);
}
The point is that we want to reselect `matchingNewBeatmap` here, not the
old selection. The `CheckModelEquality()` check's purpose is to check
whether *the current selection needs updating*.
I'm not sure why tests just wonderfully passed despite this, but my
suspicion is that it was because of accidental copying of realm guids
that obscured this problem.
This should better account for scenarios where user FPS is below 60 fps.
Previously the debounce would unexpectedly be longer than usual for low
FPS scenarios.
* Prevent Taiko difficulty crash if a map only contains 0-strains
* Add second check for safety
This is accessing a different array of strains. I'd rather be safe than sorry.
* Add guard in PP too
* Make `MarginOfError` a const
This isn't a super common mod compared to every other one on the list, it's probably not worth the storage (and memory in case of stable) implications. We can look at revisiting this once we have actual spinner difficulty considerations
This is probably where things get a little controversial.
There are some song select flows wherein song select just wants to
ensure sanity by authoritatively setting the global beatmap. The goal is
to change the beatmap immediately and instantly. Therefore it should
kind of be the carousel's job to figure out its grouping complications.
To that end, `CurrentSelection` is made virtual, and overridden in
`BeatmapCarousel` to perform a sort of reconciliation logic. If an
external component sets `CurrentSelection` to a `BeatmapInfo`, one of
the two following things happen:
- Nothing, if the current `GroupedBeatmap` is already a copy of the
beatmap that needs to be selected, or
- The carousel looks at its items, finds any first copy which matches
the beatmap that the external consumer wanted selected, and changes
selection to that instead.
Basically, `BeatmapCarousel.CurrentSelection`, which is
magic-object-typed, can no longer use `BeatmapInfo` directly, it now
must also use `GroupedBeatmap`.
This spills out all the way into song select because of beatmap
selection flows that require hookup from song select.
This bypasses the immediate first issue of not being able to display
multiple instances of a beatmap on the carousel because of model
equality being baked into the structure. It inevitably poses a bunch of
*other* problems, but it's a start.
Previously CompositeDrawable.CheckChildrenLife() would be run before lifetimeManager.Update() which lead to the new drawables being inserted into the container but not being made alive immediately, leading to the drawable not becoming visibile until the next update loop.
This stems from me looking into `TestSceneFailAnimation` failures
(https://github.com/ppy/osu/runs/48663953318). As it turns out, I should
not have been mad by CI, and rather should have been mad at myself for
failing to read.
`FailedAtJudgement` in fact does not mean "this judgement, and only this
judgement, triggered failure". If any further judgements occur
post-fail, they will also have `FailedAtJudgement` set to true. It is
essentially a *dump* of the state of `HealthProcessor.Failed` prior to
applying the judgement.
ec21685c25/osu.Game/Rulesets/Scoring/HealthProcessor.cs (L49-L57)
Because of this, reverting several judgements which occur post-fail
could lead to failed state reverting earlier than intended, and thus
potentially trigger a second fail, thus tripping the `Player` assertion.
The `BeatmapDifficultyCache` handles mod changes, so handling locally is
unnecessary. By handling locally, it creates a visual issue when
adjusting mods often. Test using Ctrl +/- at song select and observing
that without this change, the star rating will flicker back to the
default due to the local re-fetch.
* Test theory crafting
* Place in more appropriate place
* fix a bit better
* Move things around
* Reduce diff
---------
Co-authored-by: StanR <hi@stanr.info>
* New acc curve
* Penalise rhythm difficulty based on unstable rate
* Rename mono acc stuff for more clarity
* Fix nullable
* Rename stuff
* Get actual estimation for SS unstable rate
* Double space my bad
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
* Calculate consistency factor from object strains
* Use `totalDifficultHits` in performance calc
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
* Rebalance aim and speed
* Rebalance star rating
* Attempt further speed balancing
* More balancing
* More balancing
* Buff aim a bit
* More speed balancing
* Global rebalance
* Speed balancing
* Global rebalancing
* More speed balancing
* Buff aim
* MORE BALANCING
* Revert "Rebalance star rating"
This reverts commit f48c7445e12174c65b74edfef863cb3ae3cc29ff.
* Decouple velocity change bonus from wide angle bonus
* Replace sin with smoothstep
* Set multiplier back to 0.75
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
* Change pp summing and adjust multipliers
* Add back convert consideration for hidden
* And the other one whoops
---------
Co-authored-by: StanR <hi@stanr.info>
* Reduce combo scaling for osu!catch
This is a conservative reduction, a middle point between the current
scaling and the CSR proposals.
* Reduce osu!catch combo scaling further
0.45 makes little difference so let's reduce it a bit more.
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
* initial commit
* changed HD curve
* removed AR variable
* update for new rework
* nerf HD acc bonus for AR>10
* add another HD nerf for AR>10
* Update OsuDifficultyCalculator.cs
* fix speed part being missing
* Update OsuDifficultyCalculator.cs
* rework to difficulty-based high AR nerf
* move TC back to perfcalc
* fix nvicka
* fix comment
* use utils function instead of manual one
* Clean up
* Use "visibility" term instead
* Store `mechanicalDifficultyRating` field
* Rename `isFullyHidden` to `isAlwaysPartiallyVisible` and clarify intent
* Remove redundant comment
* Add `calculateDifficultyRating` method
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
* Buff precision difficulty rating in osu!
* Fix position repetition calculation
* Fix aim evaluator crashing, move small circle bonus calculation, adjust the curve slightly
* Refactor
* Fix code quality
* Semicolon
* Apply small circle bonus to speed too
* Fix formatting
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
* implement stuff
* fix basic issues
* rework calculations
* sanity check
* don't use score based misscount if no scorev1 present
* Update OsuPerformanceCalculator.cs
* update misscount diff attribute names
* add raw score misscount attribute
* introduce more reasonable high bound for misscount
* code quality changes
* Fix osu!catch SR buzz slider detection (#32412)
* Use `normalized_hitobject_radius` during osu!catch buzz slider detection
Currently the algorithm considers some buzz sliders as standstills when
in reality they require movement. This happens because `HalfCatcherWidth`
isn't normalized while `exactDistanceMoved` is, leading to an inaccurate
comparison.
`normalized_hitobject_radius` is the normalized value of `HalfCatcherWidth`
and replacing one with the other fixes the problem.
* Rename `normalized_hitobject_radius` to `normalized_half_catcher_width`
The current name is confusing because hit objects have no radius in the
context of osu!catch difficulty calculation. The new name conveys the
actual purpose of the value.
* Only set `normalized_half_catcher_width` in `CatchDifficultyHitObject`
Prevents potential bugs if the value were to be changed in one of the
classes but not in both.
* Use `CatchDifficultyHitObject.NORMALIZED_HALF_CATCHER_WIDTH` directly
Requested during code review.
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
* Move osu!catch movement diffcalc to an evaluator (#32655)
* Move osu!catch movement state into `CatchDifficultyHitObject`
In order to port `Movement` to an evaluator, the state has to be either
moved elsewhere or calculated inside the evaluator. The latter requires
backtracking for every hit object, which in the worst case is continued
until the beginning of the map is reached. Limiting backtracking can
lead to difficulty value changes.
Thus, the first option was chosen for its simplicity.
* Move osu!catch movement difficulty calculation to an evaluator
Makes the code more in line with the other game modes.
* Add documentation for `CatchDifficultyHitObject` fields
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
* Move all score-independent bonuses into star rating (#31351)
* basis refactor to allow for more complex SR calculations
* move all possible bonuses into star rating
* decrease star rating scaling to account for overall gains
* add extra FL guard for safety
* move star rating multiplier into a constant
* Reorganise some things
* Add HD and SO to difficulty adjustment mods
* Move non-legacy mod multipliers back to PP
* Some merge fixes
* Fix application of flashlight rating multiplier
* Fix Hidden bonuses being applied when Blinds mod is in use
* Move part of speed OD scaling into difficulty
* Move length bonus back to PP
* Remove blinds special case
* Revert star rating multiplier decrease
* More balancing
---------
Co-authored-by: StanR <hi@stanr.info>
* Add diffcalc considerations for Magnetised mod (#33004)
* Add diffcalc considerations for Magnetised mod
* Make speed reduction scale with power too
* cleaning up
* Update OsuPerformanceCalculator.cs
* Update OsuPerformanceCalculator.cs
* add new check to avoid overestimation
* fix code style
* fix nvicka
* add database attributes
* Refactor
* Rename `Working` to `WorkingBeatmap`
* Remove redundant condition
* Remove useless variable
* Remove `get` wording
* Rename `calculateScoreAtCombo`
* Remove redundant operator
* Add comments to explain how score-based miss count derivations work
* Remove redundant `decimal` calculations
* use static method to improve performance
* move stuff around for readability
* move logic into helper class
* fix the bug
* Delete OsuLegacyScoreProcessor.cs
* Delete ILegacyScoreProcessor.cs
* revert static method for multiplier
* use only basic combo score attribute
* Clean-up
* Remove unused param
* Update osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
Co-authored-by: StanR <castl@inbox.ru>
* rename variables
* Add `LegacyScoreUtils`
* Add fail safe
* Move `countMiss`
* Better explain `CalculateRelevantScoreComboPerObject`
* Add `OsuLegacyScoreMissCalculator`
* Move `CalculateScoreAtCombo` and `CalculateRelevantScoreComboPerObject`
* Remove unused variables
* Move `GetLegacyScoreMultiplier`
* Add `estimated` wording
---------
Co-authored-by: wulpine <wulpine@proton.me>
Co-authored-by: James Wilson <tsunyoku@gmail.com>
Co-authored-by: StanR <hi@stanr.info>
Co-authored-by: StanR <castl@inbox.ru>
* scale misscount by proportion of difficult sliders
* cap sliderbreak count at count100 + count50
* use countMiss instead of effectiveMissCount as the base for sliderbreaks
* make code inspector happy + cleanup
* refactor to remove unnecesary calculation and need for new tuple
* scale sliderbreaks with combo
* use aimNoSliders for sliderbreak factor
* code cleanup
* make inspect code happy
* use diffcalcutils
* fix errors (oops)
* scaling changes
* fix div by zeros
* Fix compilation error
* Add online attributes for new difficulty attributes
* Formatting
* Rebase fixes
* Make `CountTopWeightedSliders` to remove weird protected `SliderStrains` list
* Prevent top weighted slider factor from being Infinity
---------
Co-authored-by: tsunyoku <tsunyoku@gmail.com>
* basis refactor to allow for more complex SR calculations
* move all possible bonuses into star rating
* decrease star rating scaling to account for overall gains
* add extra FL guard for safety
* move star rating multiplier into a constant
* Reorganise some things
* Add HD and SO to difficulty adjustment mods
* Move non-legacy mod multipliers back to PP
* Some merge fixes
* Fix application of flashlight rating multiplier
* Fix Hidden bonuses being applied when Blinds mod is in use
* Move part of speed OD scaling into difficulty
* Move length bonus back to PP
* Remove blinds special case
* Revert star rating multiplier decrease
* More balancing
---------
Co-authored-by: StanR <hi@stanr.info>
* Move osu!catch movement state into `CatchDifficultyHitObject`
In order to port `Movement` to an evaluator, the state has to be either
moved elsewhere or calculated inside the evaluator. The latter requires
backtracking for every hit object, which in the worst case is continued
until the beginning of the map is reached. Limiting backtracking can
lead to difficulty value changes.
Thus, the first option was chosen for its simplicity.
* Move osu!catch movement difficulty calculation to an evaluator
Makes the code more in line with the other game modes.
* Add documentation for `CatchDifficultyHitObject` fields
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
* Move difficulty calculation fields from `Slider` to `OsuDifficultyHitObject`
* Remove redundant check
* Use `LastObject` where possible
* Update tests
* Make `LazyTravelDistance` `double`
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
* Use `normalized_hitobject_radius` during osu!catch buzz slider detection
Currently the algorithm considers some buzz sliders as standstills when
in reality they require movement. This happens because `HalfCatcherWidth`
isn't normalized while `exactDistanceMoved` is, leading to an inaccurate
comparison.
`normalized_hitobject_radius` is the normalized value of `HalfCatcherWidth`
and replacing one with the other fixes the problem.
* Rename `normalized_hitobject_radius` to `normalized_half_catcher_width`
The current name is confusing because hit objects have no radius in the
context of osu!catch difficulty calculation. The new name conveys the
actual purpose of the value.
* Only set `normalized_half_catcher_width` in `CatchDifficultyHitObject`
Prevents potential bugs if the value were to be changed in one of the
classes but not in both.
* Use `CatchDifficultyHitObject.NORMALIZED_HALF_CATCHER_WIDTH` directly
Requested during code review.
---------
Co-authored-by: James Wilson <tsunyoku@gmail.com>
# temporary workaround for https://youtrack.jetbrains.com/issue/RIDER-130051/Cannot-resolve-symbol-inspections-incorrectly-firing-for-xmldoc-protected-member-references
# temporary workaround for https://youtrack.jetbrains.com/issue/RIDER-130381/Rider-does-not-respect-propagated-NoWarn-CS1591?backToIssues=false
dotnet_diagnostic.CS1591.severity=none
#license header
#license header
file_header_template=Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.\nSee the LICENCE file in the repository root for full licence text.
file_header_template=Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.\nSee the LICENCE file in the repository root for full licence text.
about:Got something you think should change or be added? Search for or start a new discussion!
- name:osu!stable issues
url:https://github.com/ppy/osu-stable-issues
about:For osu!(stable) - ie. the current "live" game version, check out the dedicated repository. Note that this is for serious bug reports only, not tech support.
@@ -55,9 +55,7 @@ When in doubt, it's probably best to start with a discussion first. We will esca
While pull requests from unaffiliated contributors are welcome, please note that due to significant community interest and limited review throughput, the core team's primary focus is on the issues which are currently [on the roadmap](https://github.com/orgs/ppy/projects/7/views/6). Reviewing PRs that fall outside of the scope of the roadmap is done on a best-effort basis, so please be aware that it may take a while before a core maintainer gets around to review your change.
While pull requests from unaffiliated contributors are welcome, please note that due to significant community interest and limited review throughput, the core team's primary focus is on the issues which are currently [on the roadmap](https://github.com/orgs/ppy/projects/7/views/6). Reviewing PRs that fall outside of the scope of the roadmap is done on a best-effort basis, so please be aware that it may take a while before a core maintainer gets around to review your change.
The [issue tracker](https://github.com/ppy/osu/issues) should provide plenty of issues to start with. We also have a [`good first issue`](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label, although from experience it is not used very often, as it is relatively rare that we can spot an issue that will definitively be a good first issue for a new contributor regardless of their programming experience.
The [issue tracker](https://github.com/ppy/osu/issues) should provide plenty of issues to start with. In the case of simple issues, a direct PR is okay. However, if you decide to work on an existing issue which doesn't seem trivial, **please ask us first**. This way we can try to estimate if it is a good fit for you and provide the correct direction on how to address it. In addition, note that while we do not rule out external contributors from working on roadmapped issues, we will generally prefer to handle them ourselves unless they're not very time sensitive.
In the case of simple issues, a direct PR is okay. However, if you decide to work on an existing issue which doesn't seem trivial, **please ask us first**. This way we can try to estimate if it is a good fit for you and provide the correct direction on how to address it. In addition, note that while we do not rule out external contributors from working on roadmapped issues, we will generally prefer to handle them ourselves unless they're not very time sensitive.
If you'd like to propose a subjective change to one of the visual aspects of the game, or there is a bigger task you'd like to work on, but there is no corresponding issue or discussion thread yet for it, **please open a discussion or issue first** to avoid wasted effort. This in particular applies if you want to work on [one of the available designs from the osu! Figma master library](https://www.figma.com/file/VIkXMYNPMtQem2RJg9k2iQ/Master-Library).
If you'd like to propose a subjective change to one of the visual aspects of the game, or there is a bigger task you'd like to work on, but there is no corresponding issue or discussion thread yet for it, **please open a discussion or issue first** to avoid wasted effort. This in particular applies if you want to work on [one of the available designs from the osu! Figma master library](https://www.figma.com/file/VIkXMYNPMtQem2RJg9k2iQ/Master-Library).
@@ -73,6 +71,9 @@ Aside from the above, below is a brief checklist of things to watch out when you
After you're done with your changes and you wish to open the PR, please observe the following recommendations:
After you're done with your changes and you wish to open the PR, please observe the following recommendations:
- Please submit the pull request from a [topic branch](https://git-scm.com/book/en/v2/Git-Branching-Branching-Workflows#_topic_branch) (not `master`), and keep the *Allow edits from maintainers* check box selected, so that we can push fixes to your PR if necessary.
- Please submit the pull request from a [topic branch](https://git-scm.com/book/en/v2/Git-Branching-Branching-Workflows#_topic_branch) (not `master`), and keep the *Allow edits from maintainers* check box selected, so that we can push fixes to your PR if necessary.
- Please pick the following target branch for your pull request:
-`pp-dev`, if the change impacts star rating or performance points calculations for any of the rulesets,
-`master`, otherwise.
- Please avoid pushing untested or incomplete code.
- Please avoid pushing untested or incomplete code.
- Please do not force-push or rebase unless we ask you to.
- Please do not force-push or rebase unless we ask you to.
- Please do not merge `master` continually if there are no conflicts to resolve. We will do this for you when the change is ready for merge.
- Please do not merge `master` continually if there are no conflicts to resolve. We will do this for you when the change is ready for merge.
A free-to-win rhythm game. Rhythm is just a *click* away!
*osu!* is a registered trademark of ppy Pty Ltd.
jvnkosu! is not affiliated with, or endorsed by ppy Pty Ltd., but makes use of its open-source components and resources.
This is the future – and final – iteration of the [osu!](https://osu.ppy.sh) game client which marks the beginning of an open era! Currently known by and released under the release codename "*lazer*". As in sharper than cutting-edge.
## License
Client source code is licensed under the MIT license, see the [LICENCE](LICENCE) file in repository root for more info.
## Status
Game assets are included as a NuGet package and licensed under the CC BY-NC 4.0, which prohibits commercial use. See [ppy/osu-resources](https://github.com/ppy/osu-resources) for more info.
This project is under constant development, but we do our best to keep things in a stable state. Players are encouraged to install from a release alongside their stable *osu!* client. This project will continue to evolve until we eventually reach the point where most users prefer it over the previous "osu!stable" release.
Registered trademarks "osu!" and "ppy" are property of ppy Pty Ltd., and protected by trademark law.
A few resources are available as starting points to getting involved and understanding the project:
## Compiling from source
Building jvnkosu! from source is pretty much possible (and welcome here).
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
First, you must have a desktop platform with [.NET Core SDK 8](https://dotnet.microsoft.com/download) installed. Windows, Linux, macOS should work well. You can check if you have the SDK installed by running `dotnet --version` in your command prompt/terminal.
- You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management).
- Track our current efforts [towards improving the game](https://github.com/orgs/ppy/projects/7/views/6).
## Running osu!
Then, download the source code. You may download it as an archive and unzip it, but using [Git](https://git-scm.com/) instead is recommended:
```
If you are just looking to give the game a whirl, you can grab the latest release for your platform:
You can also generally download a version for your current device from the [osu! site](https://osu.ppy.sh/home/download).
If your platform is unsupported or not listed above, there is still a chance you can run the release or manually build it by following the instructions below.
**For iOS/iPadOS users**: The iOS testflight link fills up very fast (Apple has a hard limit of 10,000 users). We reset it occasionally. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements. Our goal is to get the game on mobile app stores very soon so we don't have to live with this limitation.
## Developing a custom ruleset
osu! is designed to allow user-created gameplay variations, called "rulesets". Building one of these allows a developer to harness the power of the osu! beatmap library, game engine, and general UX for a new style of gameplay. To get started working on a ruleset, we have some templates available [here](https://github.com/ppy/osu/tree/master/Templates).
You can see some examples of custom rulesets by visiting the [custom ruleset directory](https://github.com/ppy/osu/discussions/13096).
## Developing osu!
### Prerequisites
Please make sure you have the following prerequisites:
- A desktop platform with the [.NET 8.0 SDK](https://dotnet.microsoft.com/download) installed.
When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as the latest version of [Visual Studio](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/), or [Visual Studio Code](https://code.visualstudio.com/) with the [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) and [C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit) plugin installed.
### Downloading the source code
Clone the repository:
```shell
git clone https://github.com/ppy/osu
cd osu
```
```
To update the source code to the latest commit, run the following command inside the `osu` directory:
```shell
To **run** the project, switch to project's directory and run the following:
git pull
```
```
### Building
#### From an IDE
You should load the solution via one of the platform-specific `.slnf` files, rather than the main `.sln`. This will reduce dependencies and hide platforms that you don't care about. Valid `.slnf` files are:
-`osu.Desktop.slnf` (most common)
-`osu.Android.slnf`
-`osu.iOS.slnf`
Run configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `osu! (Tests)` project/configuration. More information on this is provided [below](#contributing).
To build for mobile platforms, you will likely need to run `sudo dotnet workload restore` if you haven't done so previously. This will install Android/iOS tooling required to complete the build.
#### From CLI
You can also build and run *osu!* from the command-line with a single command:
```shell
dotnet run --project osu.Desktop
dotnet run --project osu.Desktop
```
```
When running locally to do any kind of performance testing, make sure to add `-c Release` to the build command, as the overhead of running with the default `Debug` configuration can be large (especially when testing with local framework modifications as below).
To **compile**:
```
If the build fails, try to restore NuGet packages with `dotnet restore`.
dotnet build osu.Desktop
### Testing with resource/framework modifications
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be quickly achieved using included commands:
Windows:
```ps
UseLocalFramework.ps1
UseLocalResources.ps1
```
```
macOS / Linux:
To reduce performance overhead in custom builds, it's recommended to build with the `-c Release` flag, that will use the release profile and remove possibly unneeded debugging code.
```ps
### See the [original readme](README.original.md) for more info.
UseLocalFramework.sh
UseLocalResources.sh
```
Note that these commands assume you have the relevant project(s) checked out in adjacent directories:
```
|- osu // this repository
|- osu-framework
|- osu-resources
```
### Code analysis
Before committing your code, please run a code formatter. This can be achieved by running `dotnet format` in the command line, or using the `Format code` command in your IDE.
We have adopted some cross-platform, compiler integrated analyzers. They can provide warnings when you are editing, building inside IDE or from command line, as-if they are provided by the compiler itself.
JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`. Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
## Contributing
When it comes to contributing to the project, the two main things you can do to help out are reporting issues and submitting pull requests. Please refer to the [contributing guidelines](CONTRIBUTING.md) to understand how to help in the most effective way possible.
If you wish to help with localisation efforts, head over to [crowdin](https://crowdin.com/project/osu-web).
We love to reward quality contributions. If you have made a large contribution, or are a regular contributor, you are welcome to [submit an expense via opencollective](https://opencollective.com/ppy/expenses/new). If you have any questions, feel free to [reach out to peppy](mailto:pe@ppy.sh) before doing so.
## Licence
*osu!*'s code and framework are licensed under the [MIT licence](https://opensource.org/licenses/MIT). Please see [the licence file](LICENCE) for more information. [tl;dr](https://tldrlegal.com/license/mit-license) you can do whatever you want as long as you include the original copyright and license notice in any copy of the software/source.
Please note that this *does not cover* the usage of the "osu!" or "ppy" branding in any software, resources, advertising or promotion, as this is protected by trademark law.
Please also note that game resources are covered by a separate licence. Please see the [ppy/osu-resources](https://github.com/ppy/osu-resources) repository for clarifications.
A free-to-win rhythm game. Rhythm is just a *click* away!
This is the future – and final – iteration of the [osu!](https://osu.ppy.sh) game client which marks the beginning of an open era! Currently known by and released under the release codename "*lazer*". As in sharper than cutting-edge.
## Status
This project is under constant development, but we do our best to keep things in a stable state. Players are encouraged to install from a release alongside their stable *osu!* client. This project will continue to evolve until we eventually reach the point where most users prefer it over the previous "osu!stable" release.
A few resources are available as starting points to getting involved and understanding the project:
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
- You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management).
- Track our current efforts [towards improving the game](https://github.com/orgs/ppy/projects/7/views/6).
## Running osu!
If you are just looking to give the game a whirl, you can grab the latest release for your platform:
You can also generally download a version for your current device from the [osu! site](https://osu.ppy.sh/home/download).
If your platform is unsupported or not listed above, there is still a chance you can run the release or manually build it by following the instructions below.
**For iOS/iPadOS users**: The iOS testflight link fills up very fast (Apple has a hard limit of 10,000 users). We reset it occasionally. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements. Our goal is to get the game on mobile app stores very soon so we don't have to live with this limitation.
## Developing a custom ruleset
osu! is designed to allow user-created gameplay variations, called "rulesets". Building one of these allows a developer to harness the power of the osu! beatmap library, game engine, and general UX for a new style of gameplay. To get started working on a ruleset, we have some templates available [here](https://github.com/ppy/osu/tree/master/Templates).
You can see some examples of custom rulesets by visiting the [custom ruleset directory](https://github.com/ppy/osu/discussions/13096).
## Developing osu!
### Prerequisites
Please make sure you have the following prerequisites:
- A desktop platform with the [.NET 8.0 SDK](https://dotnet.microsoft.com/download) installed.
When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as the latest version of [Visual Studio](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/), or [Visual Studio Code](https://code.visualstudio.com/) with the [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) and [C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit) plugin installed.
### Downloading the source code
Clone the repository:
```shell
git clone https://github.com/ppy/osu
cd osu
```
To update the source code to the latest commit, run the following command inside the `osu` directory:
```shell
git pull
```
### Building
#### From an IDE
You should load the solution via one of the platform-specific `.slnf` files, rather than the main `.sln`. This will reduce dependencies and hide platforms that you don't care about. Valid `.slnf` files are:
-`osu.Desktop.slnf` (most common)
-`osu.Android.slnf`
-`osu.iOS.slnf`
Run configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `osu! (Tests)` project/configuration. More information on this is provided [below](#contributing).
To build for mobile platforms, you will likely need to run `sudo dotnet workload restore` if you haven't done so previously. This will install Android/iOS tooling required to complete the build.
#### From CLI
You can also build and run *osu!* from the command-line with a single command:
```shell
dotnet run --project osu.Desktop
```
When running locally to do any kind of performance testing, make sure to add `-c Release` to the build command, as the overhead of running with the default `Debug` configuration can be large (especially when testing with local framework modifications as below).
If the build fails, try to restore NuGet packages with `dotnet restore`.
### Testing with resource/framework modifications
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be quickly achieved using included commands:
Windows:
```ps
UseLocalFramework.ps1
UseLocalResources.ps1
```
macOS / Linux:
```ps
UseLocalFramework.sh
UseLocalResources.sh
```
Note that these commands assume you have the relevant project(s) checked out in adjacent directories:
```
|- osu // this repository
|- osu-framework
|- osu-resources
```
### Code analysis
Before committing your code, please run a code formatter. This can be achieved by running `dotnet format` in the command line, or using the `Format code` command in your IDE.
We have adopted some cross-platform, compiler integrated analyzers. They can provide warnings when you are editing, building inside IDE or from command line, as-if they are provided by the compiler itself.
JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`. Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
## Contributing
When it comes to contributing to the project, the two main things you can do to help out are reporting issues and submitting pull requests. Please refer to the [contributing guidelines](CONTRIBUTING.md) to understand how to help in the most effective way possible.
If you wish to help with localisation efforts, head over to [crowdin](https://crowdin.com/project/osu-web).
We love to reward quality contributions. If you have made a large contribution, or are a regular contributor, you are welcome to [submit an expense via opencollective](https://opencollective.com/ppy/expenses/new). If you have any questions, feel free to [reach out to peppy](mailto:pe@ppy.sh) before doing so.
## Licence
*osu!*'s code and framework are licensed under the [MIT licence](https://opensource.org/licenses/MIT). Please see [the licence file](LICENCE) for more information. [tl;dr](https://tldrlegal.com/license/mit-license) you can do whatever you want as long as you include the original copyright and license notice in any copy of the software/source.
Please note that this *does not cover* the usage of the "osu!" or "ppy" branding in any software, resources, advertising or promotion, as this is protected by trademark law.
Please also note that game resources are covered by a separate licence. Please see the [ppy/osu-resources](https://github.com/ppy/osu-resources) repository for clarifications.
// While .NET 8 only supports Windows 10 and above, running on Windows 7/8.1 may still work. We are limited by realm currently, as they choose to only support 8.1 and higher.
// While .NET 8 only supports Windows 10 and above, running on Windows 7/8.1 may still work. We are limited by realm currently, as they choose to only support 8.1 and higher.
// See https://www.mongodb.com/docs/realm/sdk/dotnet/compatibility/
// See https://www.mongodb.com/docs/realm/sdk/dotnet/compatibility/
Text=$"Running osu! as {(RuntimeInfo.IsUnix ? "root" : "administrator")} does not improve performance, may break integrations and poses a security risk. Please run the game as a normal user.";
*Math.Pow((Math.Min(catchCurrent.StrainTime*catcherSpeedMultiplier,265)/265),1.5);// Edge Dashes are easier at lower ms values
}
// There is an edge case where horizontal back and forth sliders create "buzz" patterns which are repeated "movements" with a distance lower than
// the platter's width but high enough to be considered a movement due to the absolute_player_positioning_error and NORMALIZED_HALF_CATCHER_WIDTH offsets
// We are detecting this exact scenario. The first back and forth is counted but all subsequent ones are nullified.
// To achieve that, we need to store the exact distances (distance ignoring absolute_player_positioning_error and NORMALIZED_HALF_CATCHER_WIDTH)
/// Normalized position of <see cref="BaseObject"/>.
/// </summary>
publicreadonlyfloatNormalizedPosition;
publicreadonlyfloatNormalizedPosition;
/// <summary>
/// Normalized position of <see cref="LastObject"/>.
/// </summary>
publicreadonlyfloatLastNormalizedPosition;
publicreadonlyfloatLastNormalizedPosition;
/// <summary>
/// Normalized position of the player required to catch <see cref="BaseObject"/>, assuming the player moves as little as possible.
/// </summary>
publicfloatPlayerPosition{get;privateset;}
/// <summary>
/// Normalized position of the player after catching <see cref="LastObject"/>.
/// </summary>
publicfloatLastPlayerPosition{get;privateset;}
/// <summary>
/// Normalized distance between <see cref="LastPlayerPosition"/> and <see cref="PlayerPosition"/>.
/// </summary>
/// <remarks>
/// The sign of the value indicates the direction of the movement: negative is left and positive is right.
/// </remarks>
publicfloatDistanceMoved{get;privateset;}
/// <summary>
/// Normalized distance the player has to move from <see cref="LastPlayerPosition"/> in order to catch <see cref="BaseObject"/> at its <see cref="NormalizedPosition"/>.
/// </summary>
/// <remarks>
/// The sign of the value indicates the direction of the movement: negative is left and positive is right.
/// </remarks>
publicfloatExactDistanceMoved{get;privateset;}
/// <summary>
/// <summary>
/// Milliseconds elapsed since the start time of the previous <see cref="CatchDifficultyHitObject"/>, with a minimum of 40ms.
/// Milliseconds elapsed since the start time of the previous <see cref="CatchDifficultyHitObject"/>, with a minimum of 40ms.
*Math.Pow((Math.Min(catchCurrent.StrainTime*catcherSpeedMultiplier,265)/265),1.5);// Edge Dashes are easier at lower ms values
}
// There is an edge case where horizontal back and forth sliders create "buzz" patterns which are repeated "movements" with a distance lower than
// the platter's width but high enough to be considered a movement due to the absolute_player_positioning_error and normalized_hitobject_radius offsets
// We are detecting this exact scenario. The first back and forth is counted but all subsequent ones are nullified.
// To achieve that, we need to store the exact distances (distance ignoring absolute_player_positioning_error and normalized_hitobject_radius)
publicoverrideLocalisableStringDescription=>@"Play with ten keys.";
publicoverrideLocalisableStringDescription=>@"Play with ten keys.";
publicoverrideboolRanked=>false;
}
}
}
}
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.