As [per
comment](https://github.com/ppy/osu/pull/36943#issuecomment-4045361755).
Unfortunately I'm not able to reproduce such a big impact of triangles
using linked pr (or at all really). In my case usage was low in the
first place and went from 0.9% to 0.6% with this pr, so outside
benchmarking would be great
Improvement list:
* No more sorted list. Basic list is being used which is sorted once
after all the triangles added.
* Out-of-bounds triangles are reused rather than removed and re-added.
* On `Reset` if `AimCount` stays the same, all triangles are reused
instead of being cleared and re-added.
- closes https://github.com/ppy/osu/issues/36942
- fixes https://osu.ppy.sh/community/forums/topics/2187041?n=1
## [Ensure Simplified Rhythm mod does not produce beatmaps with objects
out of
order](https://github.com/ppy/osu/commit/f4807b3a42dbf46ea0e669b0ba658feaef9baca5)
This is a last ditch safety.
This mod has more apparent issues (see below) but this is a first step
of restoring sanity.
Aside than the other fix described below I have not attempted to figure
out further why this is happening because the conversion logic is in
over my head. I just want hard breakage to not be possible. Why this
extra sort is necessary can be investigated by @Hiviexd if he's so
inclined.
## [Fix `GetClosestBeatDivisor()` not working correctly with negative
time
instants](https://github.com/ppy/osu/commit/88dc0d8a73c712a040635af262d7519fe6d772a0)
This was causing the 1/3 -> 1/2 conversion in Simplified Rhythm to
engage on some maps that weren't even mapped in waltz time. Those maps
had the common trait of having a timing point with a negative start
time.
Notably this could feasibly affect other places as well, like mania beat
snap colouring, or the Synesthesia osu! mod.
<details>
<summary>testing</summary>
Tested with Simplified Rhythm engaged, with 1/6 -> 1/4 and 1/3 -> 1/2
conversion enabled
[BULANOVA - NE PLACH'
[oni]](https://osu.ppy.sh/beatmapsets/1740291#taiko/3664995) (1/8 snap):
- No mods: 5.18*
- Simplified Rhythm @ master: 0.00*
- Simplified Rhythm @ f4807b3a: 3.48*
- Simplified Rhythm @ 933de7ab: 5.18*
[BULANOVA - NE PLACH' [inner
oni]](https://osu.ppy.sh/beatmapsets/1740291#taiko/3648625) (1/8 snap):
- No mods: 5.84*
- Simplified Rhythm @ master: 0.00*
- Simplified Rhythm @ f4807b3a: 4.13*
- Simplified Rhythm @ 933de7ab: 5.84*
[BULANOVA - NE PLACH' [don't
cry]](https://osu.ppy.sh/beatmapsets/1740291#taiko/3557681) (1/8 snap):
- No mods: 6.91*
- Simplified Rhythm @ master: 0.00*
- Simplified Rhythm @ f4807b3a: 4.84*
- Simplified Rhythm @ 933de7ab: 6.91*
[Mili - Peach Pit and Cyanide [nik's
Normal]](https://osu.ppy.sh/beatmapsets/2468654#osu/5405909) (1/6 snap):
- No mods: 1.63*
- Simplified Rhythm @ master: 0.00*
- Simplified Rhythm @ f4807b3a: 1.10*
- Simplified Rhythm @ 933de7ab: 1.10*
[Mili - Peach Pit and Cyanide [Ix's
Hard]](https://osu.ppy.sh/beatmapsets/2468654#osu/5405906) (1/3 snap):
- No mods: 2.50*
- Simplified Rhythm @ master: 0.00*
- Simplified Rhythm @ f4807b3a: 1.71*
- Simplified Rhythm @ 933de7ab: 1.71*
[Mili - Peach Pit and Cyanide [nomi's Hidden
Insane]](https://osu.ppy.sh/beatmapsets/2468654#osu/5405910) (1/3 snap):
- No mods: 3.93*
- Simplified Rhythm @ master: 0.00*
- Simplified Rhythm @ f4807b3a: 2.59*
- Simplified Rhythm @ 933de7ab: 2.59*
[Within Temptation - The Unforgiving [Stairway To The
Skies]](https://osu.ppy.sh/beatmapsets/29157#osu/172617) (1/3 snap in
editor):
- No mods: 1.53*
- Simplified Rhythm @ master: 0.00*
- Simplified Rhythm @ f4807b3a: 1.48*
- Simplified Rhythm @ 933de7ab: 1.48*
[Within Temptation - The Unforgiving
[Iron]](https://osu.ppy.sh/beatmapsets/29157#osu/172612) (1/6 snap in
editor):
- No mods: 2.76*
- Simplified Rhythm @ master: 0.00*
- Simplified Rhythm @ f4807b3a: 1.75*
- Simplified Rhythm @ 933de7ab: 1.75*
[Within Temptation - The Unforgiving
[Marathon]](https://osu.ppy.sh/beatmapsets/29157#osu/156352) (1/4 snap
in editor, but has 1/3 / 1/6 sections for sure):
- No mods: 2.96*
- Simplified Rhythm @ master: 0.00*
- Simplified Rhythm @ f4807b3a: 2.75*
- Simplified Rhythm @ 933de7ab: 2.75*
[Halozy - Genryuu Kaiko [Higan
Torrent]](https://osu.ppy.sh/beatmapsets/180138#osu/433005) (1/4 snap):
- No mods: 5.37*
- Simplified Rhythm @ master: 0.00*
- Simplified Rhythm @ f4807b3a: 3.95*
- Simplified Rhythm @ 933de7ab: 5.37*
</details>
As mentioned in https://github.com/ppy/osu/discussions/36883.
This has caught me off-guard a few times.
Was a quick one to make this work like it does on stable. It doesn't fit
as well as stable because we have a lot of elements at the top of the
screen, but I think it's better than nothing, as it lets you know you're
in a replay quick obviously.
I don't think we can easily localise strings with formatting in them
yet. Maybe using a `MarkdownContainer` or something?
There's been [some
feedback](https://discord.com/channels/90072389919997952/1476979671886205060/1477868178775216148)
that there's no timer during some screens. Personally I feel like some
screens shouldn't require it, but I get it.
The results screen is going to be the most controversial one, where I've
decided to shift the content down a bit - it would otherwise overlap
with the progress bar. Another option is to make the progress bar
shorter, but I feel like that makes things inconsistent.
<img width="1922" height="1035" alt="image"
src="https://github.com/user-attachments/assets/ec4ba01e-bb13-45ff-8a1b-b39d7765c1f4"
/>
Regarding implementation, I'm not entirely sure on it being added to
every subscreen via `RankedPlaySubScreen` instead of just a single
component at a top level. I did it this way because the colour scheme
seems annoying to change without recreating the entire component (or
otherwise all of its children) anyway.
This PR extracts the classes used for the actions dropdown on the user
profile overlay to separate components, in preparation to be used on the
team overlay (#32584).
Kinda RFC since I'm not sure if this is the best way to do this.
Co-authored-by: Dean Herbert <pe@ppy.sh>
* Do not forcibly log out user if user retrieval fails with a server error code
This behaviour caused users to get forcibly logged out of the game
during yesterday's redis outage.
From one case where logs were provided
(https://discord.com/channels/188630481301012481/1097318920991559880/1480201862610423933):
- User had repeated timeouts on API requests; consequently, API went
into failing state
- On one of the login retries `/api/v2/me` returned a 500 with no error
details (`{"error":"null}` JSON response) which resulted in
an instant logout as per
https://github.com/ppy/osu/blob/7263551aa868911a7d9148cf2cb16f9e0325f531/osu.Game/Online/API/APIAccess.cs#L323-L324
This PR intends to only forcibly log the user out if the returned error
code indicates a client error. If it is a server error, the login is
preserved and a normal retry loop proceeds.
This can be tested with a local web instance via following steps:
1. Start `osu-web` and a client instance connected to it.
2. Log in on the client instance.
3. Kill (`^C`) `osu-web`.
4. Trigger a few requests in the client and wait for enough of them to
fail for the API to change to `Failing` state.
5. Apply
```diff
diff --git a/app/Http/Controllers/UsersController.php b/app/Http/Controllers/UsersController.php
index db34639abf2..392a844882a 100644
--- a/app/Http/Controllers/UsersController.php
+++ b/app/Http/Controllers/UsersController.php
@@ -581,6 +581,8 @@ class UsersController extends Controller
*/
public function me($mode = null)
{
+ abort(500);
+
$user = \Auth::user();
$currentMode = $mode ?? $user->playmode;
```
6. Start `osu-web` again.
7. On master this will log the user out forcibly. On this PR, the user
will remain in `Failing` state.
8. Undo patch from step (5) (restarting web is not required).
9. On this PR, the client will be logged back in.
* Update framework
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
Addresses #36267
The drag now checks all the selected objects in the blueprint container
to see if they have the same `StartTime` and `Duration` as the dragged
note, and if so, adjust them accordingly.
---------
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
Randomly encountered this one. Not sure how it behaves in
practice/whether this is the correct fix, but it should be quite rare?
```
System.ArgumentOutOfRangeException: duration ('-199.99999999999955') must be a non-negative value. (Parameter 'duration')
Actual value was -199.99999999999955.
at System.ArgumentOutOfRangeException.ThrowNegative[T](T value, String paramName)
at System.ArgumentOutOfRangeException.ThrowIfNegative[T](T value, String paramName)
at osu.Framework.Graphics.TransformableExtensions.PopulateTransform[TValue,TEasing,TThis](TThis t, Transform`3 transform, TValue newValue, Double duration, TEasing& easing)
at osu.Framework.Graphics.TransformableExtensions.MakeTransform[TThis,TEasing,TValue](TThis t, String propertyOrFieldName, TValue newValue, Double duration, TEasing& easing, String grouping)
at osu.Framework.Graphics.TransformableExtensions.TransformTo[TThis,TValue,TEasing](TThis t, String propertyOrFieldName, TValue newValue, Double duration, TEasing& easing, String grouping)
at osu.Framework.Graphics.TransformableExtensions.ScaleTo[T,TEasing](T drawable, Vector2 newScale, Double duration, TEasing& easing)
at osu.Framework.Graphics.TransformableExtensions.ScaleTo[T,TEasing](T drawable, Single newScale, Double duration, TEasing& easing)
at osu.Framework.Graphics.TransformSequenceExtensions.<>c__DisplayClass42_0`2.<ScaleTo>b__0(T o)
at osu.Framework.Graphics.Transforms.TransformSequence`1.Append(Generator childGenerator)
at osu.Framework.Graphics.TransformSequenceExtensions.ScaleTo[T,TEasing](TransformSequence`1 t, Single newScale, Double duration, TEasing easing)
at osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Card.RankedPlayCard.SongPreviewContainer.PulseContainer.OnNewBeat(Int32 beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
```
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>
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:
https://github.com/ppy/osu/blob/15b6e28ebe888b1a87574891be1a0db3b04093b7/osu.Game/Rulesets/Edit/ScrollingHitObjectComposer.cs#L145https://github.com/ppy/osu/blob/64a29313a852d50095ae4b7ea8f22fde23aa634f/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>