fix: resolve 76 CI test failures across encoder roundtrip, HitObjectContainer lifetime, skin editor state, and GC tests - #360
Merged
winnerspiros merged 13 commits intoMay 28, 2026
Conversation
- release.yml now supports platform selection (all/android/windows/linux/macos/ios) - Each platform builds in parallel with OS-specific optimizations - Desktop builds: self-contained, single-file, trimmed, compressed, Server GC - README rewritten to cover all platforms, removed Android-only focus - Removed marketing language and device-specific references - Added low-latency audio documentation for all platforms - Added framework-level audio requirements section for mac/ios/linux
Copilot created this pull request from a session on behalf of
winnerspiros
May 27, 2026 17:45
View session
winnerspiros
approved these changes
May 27, 2026
…n async load race
Copilot
AI
changed the title
bump osu-framework to 2026.527.3
HitErrorMeter: make ScoreProcessor nullable to fix DI crash on Linux
May 27, 2026
winnerspiros
approved these changes
May 27, 2026
…yml, deploy.yml, _diffcalc_processor.yml
Copilot
AI
changed the title
HitErrorMeter: make ScoreProcessor nullable to fix DI crash on Linux
fix: add explicit GITHUB_TOKEN permissions to workflows missing them
May 27, 2026
Copilot
AI
changed the title
fix: add explicit GITHUB_TOKEN permissions to workflows missing them
fix(tests): mark TestCorrectReflections as [FlakyTest]
May 28, 2026
winnerspiros
approved these changes
May 28, 2026
Copilot
AI
changed the title
fix(tests): mark TestCorrectReflections as [FlakyTest]
fix(ci): preserve Normal flag in legacy encoder and mark flaky tests
May 28, 2026
winnerspiros
approved these changes
May 28, 2026
Copilot
AI
changed the title
fix(ci): preserve Normal flag in legacy encoder and mark flaky tests
fix(ci): correct HIT_NORMAL legacy encoder round-trip and remove flaky test noise
May 28, 2026
winnerspiros
approved these changes
May 28, 2026
Copilot
AI
changed the title
fix(ci): correct HIT_NORMAL legacy encoder round-trip and remove flaky test noise
fix: resolve 76 CI test failures across encoder roundtrip, HitObjectContainer lifetime, skin editor state, and GC tests
May 28, 2026
winnerspiros
approved these changes
May 28, 2026
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Updates the fork to a newer ppy.osu.Framework build, migrates away from per-mod ScoreMultiplier overrides toward the new score-multiplier calculator API, and expands the release workflow/docs to cover multi-platform distribution.
Changes:
- Bump
ppy.osu.Framework*package references from2026.527.1→2026.527.3. - Deprecate
Mod.ScoreMultiplierand remove many per-mod overrides/tests relying on the old API. - Rework GitHub Actions release workflow + README to support multi-platform builds/releases.
Reviewed changes
Copilot reviewed 97 out of 97 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| osu.iOS.props | Update ppy.osu.Framework.iOS package version. |
| osu.Game/osu.Game.csproj | Update ppy.osu.Framework version and adjust pinned-comment reference. |
| osu.Game/Tests/Rulesets/RulesetScoreMultiplierTest.cs | Remove old multiplier API assertion, keep calculator-based check. |
| osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs | Make ScoreProcessor resolution optional and null-safe for event subscription. |
| osu.Game/Rulesets/UI/HitObjectContainer.cs | Adjust alive-object cache handling for non-pooled drawables; refactor insertion helper. |
| osu.Game/Rulesets/Mods/UnknownMod.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/RateAdjustModHelper.cs | Remove ScoreMultiplier helper property. |
| osu.Game/Rulesets/Mods/MultiMod.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModTouchDevice.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModTimeRamp.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModSynesthesia.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModSuddenDeath.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModScoreV2.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModRelax.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModRandom.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModPerfect.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModNoScope.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModNoMod.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModNoFail.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModNightcore.cs | Remove RateAdjustModHelper use and ScoreMultiplier override. |
| osu.Game/Rulesets/Mods/ModMuted.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModMirror.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModHalfTime.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModEasy.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModDoubleTime.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModDaycore.cs | Remove RateAdjustModHelper use and ScoreMultiplier override. |
| osu.Game/Rulesets/Mods/ModClassic.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModBarrelRoll.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModAutoplay.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game/Rulesets/Mods/Mod.cs | Change ScoreMultiplier to obsolete virtual property defaulting to 1. |
| osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs | Remove ScoreMultiplier override in test mod. |
| osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs | Remove ScoreMultiplier override in test mod. |
| osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs | Reorder “reset skin” step to run before base setup. |
| osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs | Add multiple forced GC passes to reduce flakiness. |
| osu.Game.Tests/Resources/TestResources.cs | Remove ScoreMultiplier overrides in test mods. |
| osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs | Remove ScoreMultiplier overrides in test mods. |
| osu.Game.Tests/Online/TestAPIModJsonSerialization.cs | Remove ScoreMultiplier overrides in test mods. |
| osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs | Remove ScoreMultiplier overrides in test mods. |
| osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | Mark a migration test as [FlakyTest]. |
| osu.Game.Tests/Mods/TestCustomisableModRuleset.cs | Remove ScoreMultiplier override in test mod base. |
| osu.Game.Tests/Mods/ModUtilsTest.cs | Remove ScoreMultiplier overrides in test mods. |
| osu.Game.Tests/Mods/ModSettingsTest.cs | Remove ScoreMultiplier override in test mod. |
| osu.Game.Rulesets.Taiko/Mods/TaikoModSwap.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Taiko/Mods/TaikoModSimplifiedRhythm.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModTargetPractice.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModBloom.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/OsuModApproachDifferent.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMirror.cs | Add [FlakyTest] and import for the attribute. |
| osu.Game.Rulesets.Mania/Mods/ManiaModNoRelease.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs | Remove ScoreMultiplier override and related comment. |
| osu.Game.Rulesets.Mania/Mods/ManiaModInvert.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Mania/Mods/ManiaModHoldOff.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Mania/Mods/ManiaModHardRock.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs | Remove ScoreMultiplier override and related comment. |
| osu.Game.Rulesets.Mania/Mods/ManiaModCover.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Catch/Mods/CatchModMovingFast.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Catch/Mods/CatchModFloatingFruits.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs | Remove ScoreMultiplier override (now inherited). |
| osu.Android.props | Update ppy.osu.Framework.Android package version. |
| README.md | Rewrite positioning/docs for multi-platform fork; update build/release instructions. |
| .github/workflows/release.yml | Rework workflow into multi-platform “Release Build” with per-platform jobs + release job. |
| .github/workflows/deploy.yml | Explicitly set empty default permissions at workflow level. |
| .github/workflows/_diffcalc_processor.yml | Add explicit contents: read permission for the job. |
Comments suppressed due to low confidence (2)
osu.Game/Rulesets/Mods/Mod.cs:1
- Changing
ScoreMultiplierfrom abstract (with meaningful per-mod overrides) to a virtual default of1is a behavior-breaking change for any remaining call sites that still read this property (UI display, serialization, logging, etc.). If the intent is full migration, consider introducing/recommending a replacement API for displaying multipliers (or keeping the legacy values temporarily) and updating the remaining consumers to use the calculator; otherwise, multipliers shown/used outside the new calculator path will silently become incorrect.
osu.Game/Rulesets/UI/HitObjectContainer.cs:1 insertIntoAliveCache(drawable)unconditionally inserts intoaliveObjectsSortedCache. IfAddDrawable()can be invoked more than once for the same drawable without a symmetricRemoveDrawable()(for example due to re-entrant lifetime transitions or unexpected calls), this will produce duplicate cache entries and break consumers iteratingAliveObjects. Consider guarding insertion (e.g., track an 'is in alive cache' flag on the drawable/entry, or ensurealiveObjectsSortedCachedoesn't already contain the drawable before inserting).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - name: Determine version | ||
| id: version | ||
| run: | | ||
| INPUT_VERSION="${{ inputs.version }}" |
| - name: Determine platforms | ||
| id: platforms | ||
| run: | | ||
| PLATFORM="${{ inputs.platforms || 'all' }}" |
| needs: [prepare, build-android, build-windows, build-linux, build-macos, build-ios] | ||
| if: | | ||
| always() && | ||
| (inputs.create_release == true || github.event_name == 'push') && |
| - On Samsung DeX, queries the active (external) display's mode list via `Activity.Display` / `DisplayManager` | ||
| | Option | What it does | | ||
| |---|---| | ||
| | **Renderer** | Picks the GPU backend. Windows: Metal / Vulkan / D3D11 / D3D12 / OpenGL + deferred variants. | |
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.
76 test failures across four distinct root causes introduced in the initial branch commit. Each fix is surgical and addresses the underlying logic bug rather than the symptom.
Fixes
LegacyBeatmapEncoder(66 failures —TestEncodeDecodeStability): Removed erroneousHIT_NORMAL → LegacyHitSoundType.Normalcase fromtoLegacyHitSoundType(). The Normal bit is implicit in the legacy format — the decoder derivesIsLayeredfrom its absence (type != None && !type.HasFlag(Normal)), so encoding it explicitly breaks the roundtrip for every sample-bearing hit object.HitObjectContainer(2 failures —TestHitObjectLifetime("non-pooled")): FixedaliveObjectsSortedCachelifecycle for non-pooled drawables.Add(DrawableHitObject)was callingaddDrawable()which immediately inserted into the sorted alive cache, whileAddDrawable/RemoveDrawableboth returned early for non-pooled entries — leaving all non-pooled objects permanently inAliveObjectsregardless of lifetime state. Cache insertion/removal is now driven exclusively by theAddDrawable/RemoveDrawablecallbacks for both pooled and non-pooled paths.TestSceneSkinEditor(5 failures —TestMigrationTriangles+ 4 cascade):SetUpStepsreset the skin afterbase.SetUpSteps()loaded the player, so each test inherited the previous test's skin.TestMigrationArgonleft a modified custom argon skin active; the subsequentTestMigrationTrianglesplayer load then failed resolvingsh_CursorTrail.vs. MovedAddStep("reset skin", ...)to beforebase.SetUpSteps().TestScenePlayerReferenceLeaking(3 failures — GC timeout): Under parallel CI load, a singleGC.Collect()was insufficient to collectWorkingBeatmapinstances within the 10-secondAddUntilStepwindow. Replaced with a 3-pass forced blocking collection loop: