Repository navigation
Conversation
… until current score is done (#38362) Noticed through monitoring datadog metrics after a deploy. The client not waiting for the play to complete before switching over to a new instance of spectator server would cause the replay to get split in half; the half prior to the switchover would be discarded by the old instance, while only the half after the switchover would be recorded and saved to a replay. Have half-tested via local environment (on master, shutting down spectator during a play will cause it to start spewing errors instantly due to attempting to reconnect to a shutting-down instance; with this change, that only occurs on the next started play).
RFC. This is coming from #38351. Of note, the reported issue is only reproducible with non-English language active. The reason why "localisation is taking a long time to load" is that *the localisation is not there yet*. The localisation store attempts to scrape out the string from `ResourceManager`, which needs to walk all resource files to find the resource, and it can only return the `null` if every single lookup fails - and currently, for those particular strings, it does. <img width="1056" height="409" alt="Screenshot 2026-07-21 at 10 46 53" src="https://github.com/user-attachments/assets/25223662-63dc-41bb-b2f8-7a1067d646d7" /> This is going to only get worse in time as we are overloaded with requests to merge localisation changes so I am proposing this naive caching scheme in earnest. Of particular note, this still doesn't eliminate the spikes of update thread on filters in the key binding panel - those are still there due to copious (over)use of `ReadableKeyCombinationProvider` / `EnumExtensions.GetValuesInOrder()` underneath. But at least this is a return to baseline. <img width="1058" height="451" alt="Screenshot 2026-07-21 at 10 47 14" src="https://github.com/user-attachments/assets/83bb5ad8-8998-4552-a4d1-3ef44b3287fe" /> When it comes to cache eviction, in very crude testing with Russian language (opening a bunch of screens to preload a bunch of strings) I got the manager instance to occupy ~170kB which is in the "probably fine to not worry about it" territory. <img width="1858" height="230" alt="Screenshot 2026-07-21 at 10 46 18" src="https://github.com/user-attachments/assets/de572f91-d055-466c-ab7d-55dcae118ac0" />
- Client-side counterpart to / depends on ppy/osu-server-spectator#523 - [x] Requires on server deploy of above
https://github.com/user-attachments/assets/f6d2b245-85f6-48ce-865e-1b4e92fe170a There has been previous discussion about this feature [here](#23656). Tests check that the circles are all hidden/visible depending on the option, judgments are still complete on all hit types and the map finishes (does not wait because of the optional second hit for finishers). Should catch future changes breaking the feature silently. As an aside, I saw that a recent change for the rate the hit objects fly away there broke rewind in replays. Tried rewinding with these changes and it still works. --------- Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com> Co-authored-by: Dean Herbert <pe@ppy.sh>
…rrupted" because of taglib internals (#38367) Fixes https://osu.ppy.sh/community/forums/topics/2227414. See also: https://github.com/mono/taglib-sharp/blob/da41dc30756189217624d8fdde3c70956e7341f9/src/TaglibSharp/File.cs#L1263
) RFC Part of #2084 ## [Passing & failing triggers](d84cbec) Caveats here are very similar to #28480 in that when these are triggered *will* not match stable because stable's code surrounding that area is one of the worst parts of stable I've ever seen (`Player.UpdatePassing()`, `Ruleset.IsPassing`, `Player.Passing`, none of those three behaving in organoleptically observable practice as I would expect them based on source inspection...) and I have well run out of my "getting through stable code" juice for this week. This implementation is very simple: HP above half = passing, HP below half = failing. ## [Hit object hit triggers](df549d7) For whatever reason these do not appear to work on stable at all in catch or mania, but they will work fine with this implementation. On osu! and taiko this implementation should probably be close enough to not need getting touched in the future, though I have not tested that very much. ## Hit sound triggers: [The rest of the proverbial owl](https://knowyourmeme.com/photos/572078-how-to-draw-an-owl) These are not implemented here for two reasons: - The logic to when these fire is significantly more complicated conceptually than the other two based on source inspection - I have no idea how I am going to couple hit sound playing with storyboard triggering. On stable, which treats component layering as a loose suggestion at best, this is not as much of a problem - but when you want to actually separate concerns, it's a bigger pickle. The closest thing lazer has that *may* somehow be usable here is `DrawableHitObject.PlaySamples()` but I generally just do not want to get into those weeds too deep at this point in time as this is an L-size diff stat anyhow. --------- Co-authored-by: Dean Herbert <pe@ppy.sh>
This just feels right. Very weird interaction when hitting Ctrl+N with the login overlay visible otherwise.
Alternative to #38358. This is the most I'm willing to compromise. So we can take this in two directions: - Add this in and see how many users end up finding it amicable as an alternative to skin hacks (not going to satisfy everyone as per referenced PR discussion). - Close this and leave users to continue to hack via skins. For what it's worth, using the skin number hack in stable/lazer classic skins is NOT and never has been "insta-fade", it's [still 60 ms](https://github.com/ppy/osu/blob/a4f66f5988d5079875ee2095b7333d05062596f5/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs#L181). This change is actually making the fade out faster than that hack. The only difference is that a bit of animation remains. I believe this should be amicable to a majority of users, which means we may be able to move forward with less hacky skins. 🤷 Reviewer's note: A pass on code quality and general non-subjective checks would be appreciated, along with an approval. I'll make a final call as to whether to merge or not. --------- Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
Noticed some gigabytes of traffic during a full test run.
…elapsed time (#38369) Tangentially related to #38359, as the symptom makes a player area appear to stutter while spectating a multiplayer room. --- `SpectatorPlayerClock.ProcessFrame` accumulates the elapsed time from the master clock. In some cases, `ProcessFrame` is called more than once in the same Update loop, causing it to accumuate the elapsed time twice. For example: When calling `GameplayClockContainer.Start` (e.g. resuming a player's clock after initial sync of players), - `StartGameplayClock` is scheduled after children update via `SchedulerAfterChildren` - In the same Update loop, `FramedBeatmapClock.Update` calls `SpectatorPlayerClock.ProcessFrame` - Towards the end of the Update loop, the `StartGameplayClock` schedued earlier runs, which calls `SpectatorPlayerClock.ProcessFrame` again (2nd time in this Update loop) In most cases, this stabilizes after a few start/stop loops as the elapsed time on the master clock is small, but when the update thread runs at a low frequency (e.g. 60Hz when tabbed out, common when streaming tournament matches with the client tabbed out), the master clock elapsed time overshoots `SpectatorSyncManager.SYNC_TARGET` and causes a player's clock to Start and Stop indefinitely. | master | <video src="https://github.com/user-attachments/assets/93584955-7163-4989-b760-23c5bc77cbf1" /> | | --- | --- | | PR | <video src="https://github.com/user-attachments/assets/f53d66cb-c733-4d6e-acca-f8795c983f34" /> | --------- Co-authored-by: Dean Herbert <pe@ppy.sh>
…ing (#38200) Seems like the most maintainable way to do this. Using `IBeatmap`'s `HitObjects` and keeping the old method doesn't really work as we want to improve support for mods where clock rate may vary per object, and using `StartTime` on the DHO itself is really the only way we can guarantee this. --------- Co-authored-by: Dean Herbert <pe@ppy.sh>
Hopefully each commit should be self-explanatory.
It's a recurring question we get: why is my game on "lazer" when i chose "tachyon"? Until we maybe decide to change how this works (not sure what makes more sense and don't really want to change anything for now), this should help alleviate user concerns. In theory. <img width="657" height="326" alt="osu! 2026-07-27 at 07 39 39" src="https://github.com/user-attachments/assets/2a953a20-f7bf-4710-acd5-b3b03fe1b902" /> --- This also drive-by fixes another informational message not correctly being shown since the settings v2 design updates (was never bound).
…ardised score conversion algorithm (#38321) - [x] Pending sheet generation & verification > [!WARNING] > The deployment of this fix, if accepted, will consist of reverification of **all** mania legacy scores. > > Of particular note, #38203 is also a change that requires server-side reprocessing and that reprocessing has not happened yet as far as I am aware. Coming from #38102 (but does not fix it). Compare: https://github.com/ppy/osu/blob/d54366dd1192c030b133d85887a19220a4024399/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs#L28-L33 https://github.com/ppy/osu/blob/d54366dd1192c030b133d85887a19220a4024399/osu.Game/Database/StandardisedScoreMigrationTools.cs#L336-L339 Note once more that this *does not fix* the reported user issue. In that particular instance, the outcome will just be flipped (the lazer score will be favoured over the stable score). Unfortunately there is no way to improve the estimate any further to reproduce the exact result. The reason why is that the combo portion is calculated as https://github.com/ppy/osu/blob/d54366dd1192c030b133d85887a19220a4024399/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs#L35-L38 The key factor in the above calculation is `result.ComboAfterJudgement`, which is not replicable without being able to access full judgement history for a score, which is not available for stable scores unless the play is somehow fully simulated headless.
…ains multiple volume values (#38435) https://github.com/user-attachments/assets/53625d01-1908-448f-a9c5-ad329cb56207 --- Addresses https://osu.ppy.sh/community/forums/topics/2228924?n=1 somewhat.
…38429) Closes #38388. Honestly we may want to do this a different way in the future. If more buttons are ever added, this will still end up looking wrong. These screens which already have big elements likely need UI scale applied at a fractional ratio or not at all. https://github.com/user-attachments/assets/30acf869-115e-4bf6-b386-04df25c878f2
…ing (#38451) Closes #38437. `string.Join()` is not safe to use on arbitrary objects even inside of `FormattableString.Invariant()` invocations. What actually happens is that the result of `string.Join()` is computed first before any of the `FormattableString.Invariant()` machinery kicks in, which means that the values being joined will be formatted using the ambient culture rather than the invariant one. Additionally the hand-rolled parsing in `LegacyBeatmapDecoder` (done manually because `Parsing` helper methods do not return whether the parsing failed) was culture-sensitive.
### e7c0487 Update various terms and locations of some settings Just some stuff I saw and couldn't ignore. Cases where localisation keys are not changed are intentional (will not remove translations) because they are very minor changes. ### b760afe Add a note about using "change folder location" for existing data Has come up a few times in help channels. I doubt this will do much to shift discoverability, but better than nothing?
RFC. This is what I interpret #36329 (reply in thread) to mean lacking further elaboration. See comparison video: https://drive.google.com/file/d/1bLnkH8bAWlXwafw59O2uoroQp-53fkLF/view?usp=sharing (cannot embed directly due to size). On "normal resolutions", i.e. anything between 4:3 and 16:9, this matches stable. On "abnormal resolutions" there is no stable to match and lazer has a bespoke behaviour of scaling the playfield, that I've attempted to compensate for in cc35065. It *kind* of does the job but is not great. https://github.com/user-attachments/assets/febe0e6c-4ece-4971-b477-95fca12faa54 What any of this means for any existing taiko flashlight scores is not for me to decide. The other discrepancies pointed out in the discussion thread will be addressed separately because they have wider-ranging implications. <details> <summary>osu-resources diff for crosshair on flashlight</summary> ```diff diff --git a/osu.Game.Resources/Shaders/sh_Flashlight.h b/osu.Game.Resources/Shaders/sh_Flashlight.h index fcfc34d2..493effc9 100644 --- a/osu.Game.Resources/Shaders/sh_Flashlight.h +++ b/osu.Game.Resources/Shaders/sh_Flashlight.h @@ -17,6 +17,13 @@ void main(void) { // todo: workaround for a SPIR-V bug (ppy/osu-framework#5719) float one = g_WrapModeS >= 0 ? 1 : 0; - - o_Colour = mix(getColourAt(flashlightPos - v_Position, flashlightSize, v_Colour), vec4(0.0, 0.0, 0.0, v_Colour.a), flashlightDim) * one; + vec2 offset = flashlightPos - v_Position; + + o_Colour = mix(getColourAt(offset, flashlightSize, v_Colour), vec4(0.0, 0.0, 0.0, v_Colour.a), flashlightDim) * one; + + if (abs(offset.x) < 2.0 || abs(offset.y) < 2) + o_Colour = vec4(1); + + if (abs(length(offset) - length(flashlightSize * flashlightSmoothness)) < 2) + o_Colour = vec4(1); } \ No newline at end of file ``` </details>
…#38380) Resolves #38191 forwarding my thoughts from the issue for visibility: I thought about this idea and I strongly think it'd improve familiarity for mappers coming from stable, it mimics stable's UX of focusing on the timeline when placing objects and not having to pay attention to placement on the playfield. I tried mapping with it a bit and I found it significantly more natural as a long-time stable mapper trying to move to lazer. one may wonder why not just have your cursor stationary on the hit area? from my experience mapping multiple times on lazer, I found this to be one extra thing to focus on having correct which gets annoying in prolonged sessions. the way this mimics stable's UX makes it all that natural IMO. This is a default-off setting with it being under the "show speed changes" one. --------- Co-authored-by: Dean Herbert <pe@ppy.sh>
https://github.com/user-attachments/assets/333a626c-d5b7-4caf-a3a5-b148a0b33a19 --- RFC. Continuation of #2084 (comment). In general I ask this to be treated like a first pass at this feature rather than a complete pristine implementation. ## Behaviour & known issues Most testing that I've done is with the test map I made and linked in #2084 (comment). This is *close enough* to stable, but differs in several key aspects. Of most note: - Some hit sounds will activate a trigger on stable but will not activate a trigger on lazer. The one case I know of where this happens is with triggers of the form `HitSoundAllSoft`. The intent of the trigger is to activate "for any sound, with any normal bank, and `soft` addition bank". The difference is when a hit object does *not* have an addition sound. stable still considers that object to have "`soft` addition bank" despite not having an addition sound, and in lazer that's not the case, because when the addition sound and its associated `HitSampleInfo` isn't present, there's nowhere to put its bank, so the trigger cannot fire. - Some hit sounds will not activate a trigger on stable but will activate a trigger on lazer. This occurs for e.g. slider ticks and repeats. Addressing this would require even further granular control of when a `DrawableHitObject.PlaySamples()` call activates a trigger and I'm not sure that's in our interest. The stable logic as to which actions on which objects activate triggers is very ad-hoc. - Hitting the taiko drums and the mania keys activates triggers even if they don't hit any specific object. This matches stable mania but does not match stable taiko. Unifying this behaviour seems rational and I think this is the better direction. - There are other issues I'd rather not even name other than mention in passing (some maps don't work *quite* right with singular sprites not fading out correctly, seeking sometimes breaks things...). I would ask to disregard those and let people report these issues as they are spotted, and the issues can be fixed later as bandwidth allows. ## Performance There are two factors to consider for performance here: - The added overhead from DHOs updating `GameplayState.LastPlayedSamples`, and - The actual overhead from determining if the triggers should be played and then adding the transforms for them. With the first part, I'm pretty convinced it's not a *large* issue (measured with storyboard turned off): <img width="878" height="128" alt="Screenshot 2026-07-24 at 13 11 46" src="https://github.com/user-attachments/assets/6b9e801a-7aa9-4064-b81b-4a61e11ceadc" /> The second is somewhat more dicey, but in large part most of the overhead is not really negotiable in scope of this PR as it is applying transforms. I added a slight optimization for it in f985bc0 and in general it seems good enough in that basically all of the overhead left is the transforms. <img width="1114" height="485" alt="Screenshot 2026-07-24 at 14 23 10" src="https://github.com/user-attachments/assets/58d75c17-5a3d-4207-a372-ad493729c476" /> --------- Co-authored-by: Dean Herbert <pe@ppy.sh>
- [x] depends on ppy/osu-framework#6778 - closes #38405 port of ppy/osu-web#13079 Wiki is already live with this new styled alert (ppy/osu-wiki#14941) I decided to use this component in `OsuMarkdownContainer` because styled alert is used in both wiki and news (if we will implement news page fully in the future). --------- Co-authored-by: Dean Herbert <pe@ppy.sh> Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
How this came to be: I looked at the last page of CI results, went through all failures, and attempted to fix any single one that was either directly reproducible or seemed to maybe have an easy fix in 5 minutes or less. From a representative sample size of N=1 test runs on my fork this perhaps maybe possibly hopefully helps. (Feel free to wait out CI on this one to bring that tally to a whopping N=2.) See commit messages for explanations.
#38464) Completely random diff for no reason. If unacceptable feel free to just close without justification. It's still horrible but the rest of the bad things in profiling are not easily resolved. ## [Fix O(n²) overhead from individual blueprint additions / removals by batching](a6913b2) `BlueprintContainer` enforces sorting, which means that adding hundreds of items to it one by one essentially incurs an insertion sort which is quadratic and a major, quickly avoidable, hotspot in profiling. Resolved by adding items to temporary unsorted list and propagating insertions / removals once per frame. | before | after | | :-: | :-: | | <img width="1002" height="411" alt="Screenshot 2026-07-29 at 11 33 51" src="https://github.com/user-attachments/assets/2ef81250-9e74-4782-bdef-1f8431327392" /> | <img width="1121" height="590" alt="Screenshot 2026-07-29 at 11 34 29" src="https://github.com/user-attachments/assets/afa0af79-2fc7-4b5d-ac5f-15c74fe4313c" /> | ## [Fix O(n²) overhead from timeline blueprints re-sorting on every depth change](f312006) Another very avoidable hotspot in profiling. Selecting and deselecting items causes them to be either brought to front or sent to back via `ChangeChildDepth()`. To make matters worse, `ChangeChildDepth()` in the timeline specifically would incur a full sort of the list every time to cover off a bug scenario that could occur when a placement is in progress (as it is not ensured that the placement's location in the list of blueprints is correctly sorted as the placement's properties change; see #30377). In the case of a plain selection this is just pure overhead for nothing, so change the logic to only perform the sort when a placement is active. | before | after | | :-: | :-: | | <img width="1396" height="546" alt="Screenshot 2026-07-29 at 11 45 04" src="https://github.com/user-attachments/assets/2e3e81e6-4e9b-4525-962e-7d897372cb1b" /> | <img width="1373" height="526" alt="Screenshot 2026-07-29 at 11 47 04" src="https://github.com/user-attachments/assets/6dfaf119-bc3e-4663-82e1-2b52291ce305" /> |
This migrates `MenuParallax` over to `MenuParallaxScale`, which can go from 0-4, which lets people dial in the parallax to a value that feels comfortable (or to crank it all the way up like a nutcase). This pull request is not AI-generated, AI-assisted, vibe coded, or whatever people call slop nowadays. --------- Co-authored-by: Dean Herbert <pe@ppy.sh> Co-authored-by: TheRealStevie <75444413+TheRealStevie@users.noreply.github.com>
…map has null `MD5Hash` (#38480) Closes #38479, also reported in https://osu.ppy.sh/community/forums/topics/2230279?n=1. The logs attached in the forum thread show 2026-07-31 00:05:50 [error]: Unobserved exception occurred via FireAndForget call: Value cannot be null. (Parameter 'key') 2026-07-31 00:05:50 [error]: System.ArgumentNullException: Value cannot be null. (Parameter 'key') 2026-07-31 00:05:50 [error]: at System.Collections.Generic.Dictionary`2.FindValue(TKey key) 2026-07-31 00:05:50 [error]: at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value) 2026-07-31 00:05:50 [error]: at osu.Game.Screens.Select.BeatmapCarouselFilterGrouping.defineGroupsByCollection(List`1 carouselItems, List`1 allCollections) 2026-07-31 00:05:50 [error]: at osu.Game.Screens.Select.BeatmapCarouselFilterGrouping.getGroups(List`1 items, FilterCriteria criteria) 2026-07-31 00:05:50 [error]: at osu.Game.Screens.Select.BeatmapCarouselFilterGrouping.<>c__DisplayClass33_0.<Run>b__0() There are two `TryGetValue()` calls in the mentioned method: https://github.com/ppy/osu/blob/6b2606c51b4334f36d9d09dc96dc5d6bafb468a1/osu.Game/Screens/Select/BeatmapCarouselFilterGrouping.cs#L425 https://github.com/ppy/osu/blob/6b2606c51b4334f36d9d09dc96dc5d6bafb468a1/osu.Game/Screens/Select/BeatmapCarouselFilterGrouping.cs#L441 Checking the realm database schema as presented by Realm Studio, `BeatmapCollection.BeatmapMD5Hashes` does not permit nulls, but `Beatmap.MD5Hash` *does* permit nulls. So in theory only the latter appears possible. As to how `Beatmap.MD5Hash` *becomes* null? - no idea. The only thing I can think of is some external interference. I can't see any actual game code path that would write a null there. But given how crippling the result of that null is here (the carousel completely disappears), I think it's probably better to guard against it?
https://github.com/user-attachments/assets/66c3f8b5-7e3d-48e4-8e95-8513ca32b839 --- Part of #24996. The most interesting part here is the `LegacyTextureLoaderStore` changes. Stable buttons are skinned using three sprites: `button-{left,middle,right}`; the side sprites are used unaltered, while the middle one is stretched to fill space as required. [`NineSliceSprite`](ppy/osu-framework#6380) can handle this, except for the fact wherein it wants *one* texture to work with for the entire drawable and not one texture per region. This is worked around here by splicing the button textures into a single texture & lookup. If that offends I can explore alternative changes to framework that could permit accepting an array of textures there instead.
https://github.com/user-attachments/assets/3019ff0c-300a-4f5e-842a-d6d2a4a774b4 --- Part of #24996 Caveats: - argon design: - Lacks footer content, this will be added in follow-ups. - Sound design is rudimentary. - Appearance and transitions are subject to at-will adjustments. I'm sure @peppy will have many to apply. - legacy design: - No white flash at the end of the animation sequence. - Missing `applause.wav`. - New mod display is used instead of any skin assets. This can be discussed further in #37552. For one thing, I will remark that if mod icons are to become fully skinnable, then icons for lazer-specific mods will need to be added for the classic and retro skins so that things do not look stupid. It is also very unclear, how - if at all - adjusted settings would be highlighted on those icons. - in general: - Demonstrated freeform skinnability capabilities may be constrained in order to prevent terminal breakage of this screen. More on this later. - I currently have the intention of providing only one variant of the extended results screen ("online ranking" in stable parlance) and it will *not* be the stable one, as the stable one is just kind of bad.
| Before | After | Web | | --- | --- | --- | | <img width="652" height="353" alt="Screenshot 2026-11-01 at 2 42 08 PM" src="https://github.com/user-attachments/assets/baef5f77-5ec7-4037-ad0c-400f58f126f4" /> | <img width="634" height="364" alt="Screenshot 2026-11-01 at 2 41 18 PM" src="https://github.com/user-attachments/assets/b526ff1e-73ad-43b1-841f-23ce87b79578" /> | <img width="420" height="250" alt="Screenshot 2026-11-01 at 2 40 11 PM" src="https://github.com/user-attachments/assets/8dd56643-bee9-48c2-abc9-80a64aa897fa" /> |
- Regressed in #28089 - Closes #31865 | Before | After | | --- | --- | | https://github.com/user-attachments/assets/54b064e2-e84e-48dc-885f-2497cc5ae61a | https://github.com/user-attachments/assets/607b6279-010b-47f7-b202-4029eae1811c |
…9095) i noticed that in some cases displayed total play time differs from osu!web, so i decided to do some refactoring and also add localisation support [osu!web reference](https://github.com/ppy/osu-web/blob/70b3b6f34b9371e8f738cc40f67260f283324444/resources/js/profile-page/play-time.tsx#L15-L37) | case | master | PR | osu!web | |:-:|-|-|-| | hours < 2 | <img width="190" height="154" alt="image" src="https://github.com/user-attachments/assets/ab1bc0c4-f1b4-4d4c-9e47-37976c72f57c" /> | <img width="195" height="150" alt="image" src="https://github.com/user-attachments/assets/1a8548ca-b949-478c-b82e-4ace45ef8a96" /> | <img width="146" height="87" alt="image" src="https://github.com/user-attachments/assets/6b2a9afc-dd9b-436d-b200-a0c6f1de84b4" /> | | hours > 2 | <img width="175" height="142" alt="image" src="https://github.com/user-attachments/assets/1e0a6b15-8fbd-45d8-8237-7b1b9506456e" /> | <img width="180" height="153" alt="image" src="https://github.com/user-attachments/assets/7999e052-373e-46f9-93e9-efbf6aee8f7d" /> | <img width="130" height="85" alt="image" src="https://github.com/user-attachments/assets/5e717250-9998-44b8-a399-ba63dc021cd7" /> | | some random big time | <img width="199" height="141" alt="image" src="https://github.com/user-attachments/assets/c2c79154-1ad6-4258-b6bf-441ca58cbfa4" /> | <img width="191" height="140" alt="image" src="https://github.com/user-attachments/assets/2c91f8bb-52b3-4c71-bd4c-6daca1ffe2e6" /> | <img width="120" height="88" alt="image" src="https://github.com/user-attachments/assets/91eb14c4-5399-48ea-aa0c-ab3034eaa554" /> |
…39100) Note: While investigating this, I have found [a PR](#13657) saying that this specific test scene can't be refactored in this way until `RoomManager` is removed, which did seem to happen in [here](#31866). Also from my testing, and the fact that everything is going through the lounge and `MultiplayerClient`, I don't think this should cause any issues. Feel free to close if I'm wrong, however. There seems to be a few multiplayer test cases that are using the `TestMultiplayerComponents` screen instead of the `MultiplayerTestScene` for providing the dependencies necessary for running multi tests. This PR aims to refactor them to use the latter, for two reasons: - the screen stack flow is confusing to read compared to other tests, given that `TestMultiplayerComponents` creates its own inner stack on top of the one provided by the test scene, which handles both the online play screen and any player/spectator screens pushed on top of it. This seems to have caused at least one [broken test](https://github.com/ppy/osu/blob/b77b028f9a0256ce23b94ef7eb916648641553e7/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs#L584) which would always pass regardless of the actual test state, as it targets the wrong screen stack, - because of the above, the entire thing breaks when the global footer is introduced, as `ScreenStackFooter` fails to properly account for the aforementioned additional screen stack. This came up while I was working on #39092, where the footer would never show up on screens spawned during the tests, causing breakage. Large part of this PR is just `%s/multiplayerClient/MultiplayerClient/g`, which has been pulled out into a separate commit for review convenience. The rest is mostly adjusting the tests to point to the actual `Multiplayer` screen, or others where appropriate. There is still one remaining user of `TestMultiplayerComponents`, which is [this specific test in `TestSceneScreenNavigation`](https://github.com/ppy/osu/blob/b77b028f9a0256ce23b94ef7eb916648641553e7/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs#L928). Dealing with that is left for another PR, as I'm not sure how to properly provide the multiplayer dependencies into `OsuGameTestScene` in a different way. However, said test appears to be duplicating a [different test in `TestSceneMultiplayer`](https://github.com/ppy/osu/blob/b77b028f9a0256ce23b94ef7eb916648641553e7/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs#L264), so maybe it's fine to just remove it. - Blocks #39092.
I haven't found a good solution to this yet, even in an isolated project. - `--abort-on-container-failure` and `--detach` are mutually exclusive. This is the main reason I'm applying this revert. - `--abort-on-container-failure` and `--abort-on-container-exit` are also mutually exclusive, and fail in a similar way to the above. - `--abort-on-container-failure` and `--exit-code-from` are also mutually exclusive, though they are allowed to both be present at the same time. I think I'll probably need to look for an external solution to this (or at least a completely different path), because it doesn't seem like docker compose can handle it.
See: https://github.com/ppy/osu/actions/runs/41579178775/job/125729699856?pr=38912 (`TestSceneSoloResultsScreenV2.TestDisplay()`)
This PR adds `Filter` property to `UserTagControl` which allows overriding the tags which are displayed in it. This is meant to be used in ranked play (as discussed [on](https://discord.com/channels/90072389919997952/1476979671886205060/1525627693490765834) [discord](https://discord.com/channels/90072389919997952/1476979671886205060/1527607201508884633)) to display a prompt allowing players to quickly tag the skillset of the previously played beatmap. | Default | `Filter` set | |--------|--------| | <img width="737" height="201" alt="image" src="https://github.com/user-attachments/assets/337af17f-1263-46b3-a285-58f916967463" /> | <img width="421" height="142" alt="image" src="https://github.com/user-attachments/assets/3479d63d-f7a8-4e60-8d3b-75ef5b52a3c6" /> | Essentially what happens is that all tags matching the filter function are shown, even if they would normally be hidden because of zero votes. Additionally, the 'add new tag' button is hidden, as all tags intended to be available for users are already displayed. --------- Co-authored-by: Dan Balasescu <smoogipoo@smgi.me>
Currently, if the footer contains any buttons on the right side, and a blocking overlay is shown, the right buttons will cause any overlay footer content to be pushed to the left, instead of being anchored to the right side of the screen. This PR fixes said issue by pulling the right buttons container out of the grid which is currently holding the buttons and overlay content. Alternatively, this could be accomplished through the same button hiding and reparenting dance which is currently done with left buttons through `temporarilyHiddenLeftButtons`, however given that: - I am not aware of the right buttons being intended to open any blocking overlays (which would necessitate doing the 'hide every button other than the one which triggered the overlay' thing) - we're not guaranteeing that the left and right buttons won't overlap in a case where there's not enough space in the footer for both, therefore we're not really gaining anything by having them in the grid container, I decided that this would have added unnecessary complexity for little/no gain and opted against doing that. Fixes a bug in #39111.
Requires ppy/osu-resources#440. This PR adds components rendering both the full and compact variants of the badge displaying the user's division inside of ranked play. Also includes a proposed DTO for storing division metadata, which I hope will later be used when we get web to serve tier/division data to the client. I prefered doing this over implicitly assuming divisions start at 600, are spaced 100 ELO apart, etc. since the intention is to have this metadata served by web anyway. Regular badges: <img width="727" height="750" alt="image" src="https://github.com/user-attachments/assets/f005f912-08ad-4a9a-af1a-565a0d5c6137" /> Compact badges: <img width="590" height="605" alt="image" src="https://github.com/user-attachments/assets/42e45d12-edf3-44a8-8e92-3f3c89e60301" />
#39138) To be clear: this was already [not supported server-side](https://github.com/ppy/osu-server-spectator/blob/e63febb9129d1c7377d23ba23af01b57975ae6d5/osu.Server.Spectator/LegacyHelper.cs#L19-L20) and thus the lack of such guard client-side was raising server-side errors. The introduction of this guard is not supposed to be interpreted as a statement against custom ruleset support. At this point in time it is merely a measure to reduce noise. There are no actual meaningful behavioural changes incurred by this change.
…#39128) RFC. Currently, `ISerialisableDrawable` has https://github.com/ppy/osu/blob/18b71a28facae9b4ef12dbf2c5b69df98e738175/osu.Game/Skinning/ISerialisableDrawable.cs#L25-L28 The issue with it (other than that nothing actually ever used it) is that it allows for a very crude degree of control over the relevant component. You can do either everything or nothing to it. This diff allows controlling which specific operations apply to which component, as well as controlling *whether a component can be placed* alongside others. That last possibility allows to implement singleton components (e.g. components which there should be never more than one of), or mutually exclusive components (if component A is present, then B cannot be). All of this is relevant for results screen work (#24996). Aside from enforcement in skin editor via hiding all relevant controls, this also adds enforcement at skin deserialization / load time by ignoring impermissible changes made externally in `.json` files. If wishing to test this outside of the provided test scenes, one can use ```diff diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs b/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs index 52dcadd..505bf7586c 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs @@ -2,16 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; +using osu.Game.Scoring; using osu.Game.Screens; +using osu.Game.Screens.RankingV2; using osu.Game.Screens.Select; using osuTK; @@ -31,6 +35,15 @@ public partial class SkinEditorSceneLibrary : CompositeDrawable [Resolved] private SkinEditorOverlay? skinEditorOverlay { get; set; } + [Resolved] + private ScoreManager? scoreManager { get; set; } = null!; + + [Resolved] + private BeatmapManager? beatmapManager { get; set; } = null!; + + [Resolved] + private Bindable<WorkingBeatmap> beatmap { get; set; } = null!; + public SkinEditorSceneLibrary() { Height = HEIGHT; @@ -88,6 +101,21 @@ private void load(OverlayColourProvider overlayColourProvider) Origin = Anchor.CentreLeft, Action = () => skinEditorOverlay?.PresentGameplay(), }, + new SceneButton + { + Text = "Results", + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Action = () => performer?.PerformFromScreen(screen => + { + var score = scoreManager?.Query(_ => true); + if (score == null || beatmapManager == null) + return; + + beatmap.Value = beatmapManager.GetWorkingBeatmap(score.BeatmapInfo); + screen.Push(new SoloResultsScreenV2(score)); + }), + }, } }, } ``` and then use the scene library in the skin editor to navigate to the current draft of new results to test.
as always, changes can be reviewed commit by commit should be a quick one to get in, i'll leave my comments in a review below --------- Co-authored-by: Dean Herbert <pe@ppy.sh>
) <img width="1606" height="954" alt="Screenshot 2026-11-09 at 09 41 54" src="https://github.com/user-attachments/assets/d00fc33f-e5dc-453a-ba48-a2b10164e57a" /> Buttons do nothing as of now, this is visual-only for now. Intended meaning of buttons: - "Ranking" expands a view showing a contextually relevant leaderboard (one of the beatmap global leaderboards in solo, the room leaderboard in multi, the daily challenge leaderboard in daily challenge, etc.) - "More statistics" expands a view showing user statistics updates, performance breakdown, accuracy heatmap, distribution graph etc. (the equivalent of current screen's when an expanded score panel is clicked) - "Catalogue" did not feature on any designs and is my invention. The intent for that screen is to feature controls for - (un)favoriting the beatmap - rating the beatmap - voting for user tags on the beatmap - adding the beatmap to relevant collections These intentions are potentially subject to change through review and through user feedback as required. Of note, this component outwardly *appears* to behave like a normal skin component, and as such it is possible to mix it into legacy skins: https://github.com/user-attachments/assets/467cea7f-e6e0-499e-b404-591b17787929 This component is mutually exclusive with the legacy "Online Ranking" button. --- - Part of #24996 - [x] Depends on resources package containing ppy/osu-resources#450 From a technical standpoint this is one of the most "inventive" usages of skinning to date. - The component itself is basically a fake, in actual usage it displays nothing of its own, it's basically a stand-in for manipulating the global footer contents via the API exposed in #39026 - In the toolbox the footer buttons *are* actually shown so that the component's button isn't just blank. This is done via checking if `SkinEditor` available via DI. This trick was previously used in #33503. --------- Co-authored-by: Dean Herbert <pe@ppy.sh>
Part of #39001 Effect points are now displayed in the objects timeline and have their own toolbox section. Star icon indicates kiai toggle status. Upcoming PRs would add tooling for controlling effect points from there. <img width="982" height="189" alt="image" src="https://github.com/user-attachments/assets/288c62e6-2988-4741-940b-59663fbb379d" /> Remarks: This implementation works well for osu!/osu!catch given the lines only indicate kiai toggles, whereas the osu!taiko/osu!mania side is less ideal given the potential timeline clutter it could cause and the fact the scroll speed values are being overlapped by objects, which is ironic because osu!taiko/osu!mania are the modes that really need some sort of effect points display in the timeline. Here's how it looks on a common notes speed-up. You can see the scroll speed values by either hovering over the points or looking at the toolbox section, but it's very much not ideal. <img width="1517" height="129" alt="image" src="https://github.com/user-attachments/assets/48834aa9-14bb-4db3-a020-9c28fcc88e20" /> Ideally, effect points would be displayed just like velocity changes in the timeline, as pills above objects. This works perfectly for osu!taiko and osu!mania, whereas it doesn't for osu! and osu!catch as we don't have space for them because of the velocity pill. Best of both worlds would be using the line display for osu!/osu!catch and the pill display for osu!taiko/osu!mania, which leads us right back to potentially wanting ruleset-specific blueprints as PR'd in #29973. Easiest approach that doesn't involve said PR would be to conditionally render effect points as `ruleset has scroll speed ? render pill : render line`. This is what would this suggestion look like: <table> <tr> <td>osu!/osu!catch <td> <img width="848" height="118" alt="image" src="https://github.com/user-attachments/assets/a7a07116-d573-46d4-a4e0-17437009d72c" /> <tr> <td> osu!taiko/osu!mania <td> <img width="1180" height="112" alt="image" src="https://github.com/user-attachments/assets/53607291-3b8a-45d5-a980-1d848b84355c" /> </table> Personally I'm leaning to the approach above but I'll need clarity on the direction devs want to take with this. --------- Co-authored-by: Dean Herbert <pe@ppy.sh>
This proposal introduces the following changes to username colours in chat: - Users who have supported the game and customised their profile hue get a username colour based on that selected hue. This is written in a way such that the custom colour does *not* go away once supporter expires, but also cannot be changed unless user acquires supporter again. This matches the user profile behaviour. This addresses #19757 which has demonstrated significant support. - Users without supporter no longer get assigned a random colour based on their user ID, and instead get either an off-white secondary content colour (if in an overlay context), or straight white. This addresses #38705 and partially addresses #23145, though I expect this particular choice of method of addressing it may be more controversial than the previous one. | before | after | | :-: | :-: | | <img width="1284" height="430" alt="image" src="https://github.com/user-attachments/assets/86f2b96a-0bc5-48d0-b17a-5ae70e707bb0" /> | <img width="1284" height="425" alt="image" src="https://github.com/user-attachments/assets/dc21b771-42f3-4bec-89e7-e45e10e11e14" /> | | <img width="480" height="610" alt="image" src="https://github.com/user-attachments/assets/35b12c19-c3c8-443f-b432-4b2243167600" /> | <img width="479" height="616" alt="image" src="https://github.com/user-attachments/assets/9dfb1020-8806-4074-922a-34f540668548" /> |
…38795) Closes #38727. In freestyle select it seems counterproductive to apply filters that are persisted to config such as: the user's star difficulty range, convert preference, and collection selection, since applying those filters could yield no results and then make the impression that the user has no choices available. The caveat here is that this change as written does not change the visual state of the relevant controls on the screen in any way. Listening for opinions as to whether that should be done, as it'll likely complicate matters significantly. Co-authored-by: Dean Herbert <pe@ppy.sh>
…39155) RFC. At CS5 this roughly matches the old sizing. Using that as a control, the algorithm now scales in line with actual circle size, which feels a lot better when reaching extremities. Closes #33794 (comment). | CS | Before | After | | :---: | :---: | :---: | | CS0 | <img width="816" height="704" alt="osu! 2026-11-10 at 10 33 49@2x" src="https://github.com/user-attachments/assets/8bfde576-77c0-4f1f-b8d3-7780072c854d" /> | <img width="816" height="704" alt="osu! 2026-11-10 at 10 33 35@2x" src="https://github.com/user-attachments/assets/98ceb1d7-0fb5-4193-b898-129a6a5a4a38" /> | | CS4 | <img width="816" height="704" alt="osu! 2026-11-10 at 10 30 27@2x" src="https://github.com/user-attachments/assets/2d638674-2723-4fca-a32f-1a468247a76f" /> | <img width="816" height="704" alt="osu! 2026-11-10 at 10 30 50@2x" src="https://github.com/user-attachments/assets/3e30f42b-02de-43bf-9950-396d03a70d44" /> | | CS8 | <img width="816" height="704" alt="osu! 2026-11-10 at 10 32 20@2x" src="https://github.com/user-attachments/assets/f3bbbb82-e165-4be7-adc1-b5cfbe628971" /> | <img width="816" height="704" alt="osu! 2026-11-10 at 10 32 07@2x" src="https://github.com/user-attachments/assets/86c0a54d-75c4-4f62-9c5f-4e34fb48f4d4" /> | | CS10 | <img width="816" height="704" alt="osu! 2026-11-10 at 10 32 58@2x" src="https://github.com/user-attachments/assets/bd408860-1518-484a-bf2e-d69ae74ad183" /> | <img width="816" height="704" alt="osu! 2026-11-10 at 10 33 07@2x" src="https://github.com/user-attachments/assets/cd077dc9-9cf5-4d7a-addc-9c688d0c8542" /> |
https://github.com/user-attachments/assets/1f5da689-d9c4-45a3-a4e3-61def18fdcb8 --- - Part of #24996 Note that the data flow is just to make visuals work for now. I'm not super sure it will all hold up in the future, especially when online data sources are involved, but I'll cross that bridge when I get to integration.
Currently, `QueueController` will instantly transition `ScreenQueue` to the 'in room' state, without waiting for the actual room join to complete. This could cause the matchmaking screen to be pushed without the room being properly set up client-side, if for some reason the join took longer than the 2 seconds specified on the `pushScreenDelegate`. This PR makes `QueueController` wait for join success before changing the queue state, and also moves setting the delay from `BeginDelayedSequence` to the delegate, since the former is kinda redundant here. If I'm understanding things correctly, this is probably the proper fix for #38482.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot]
Can you help keep this open source service alive? 💖 Please sponsor : )