Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ jobs:
exit $exit_code

- name: InspectCode
uses: JetBrains/ReSharper-InspectCode@v0.12
uses: JetBrains/ReSharper-InspectCode@v0.13
with:
tool-version: 2026.1.0.1
# this is WTF tier but if you don't specify *both* of these the defaults assume `build: true`
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<EmbeddedResource Include="Resources\**\*.*" />
</ItemGroup>
<ItemGroup Label="Code Analysis">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" PrivateAssets="All" />
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="4.14.0" PrivateAssets="All" />
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
<!-- Rider compatibility: .globalconfig needs to be explicitly referenced instead of using the global file name. -->
<GlobalAnalyzerConfigFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\osu.globalconfig" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="NUnit" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="NUnit" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="NUnit" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="NUnit" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
</ItemGroup>
<ItemGroup>
Expand Down
13 changes: 13 additions & 0 deletions osu.Android.props
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@
we make it explicit so a future SDK bump cannot silently start stripping IL
and reproduce the trim-then-AOT crash class we removed above. -->
<AndroidStripILAfterAOT>false</AndroidStripILAfterAOT>
<!-- AndroidLinkSkip: bypass ILLink entirely for osu.Framework.Android and SDL3-CS.
Both assemblies have preserve="all" entries in Linker.xml, but those entries carry
IL2007 at descriptor-processing time (the assemblies are NuGet-resolved and not yet
in ILLink's search path). The IL2007 deferred-rule mechanism has proven unreliable
with ILLink 10.0.x — the v2026.508.228 crash was identical to v2026.508.226 despite
the Linker.xml entries being present, because the deferred rule for osu.Framework.Android
was silently dropped. AndroidLinkSkip is an MSBuild-level directive processed before
ILLink runs; it reliably prevents any trimming of the listed assemblies regardless of
the deferred-rule issue.
osu.Framework.Android: carries AndroidGameActivity (RegisterAttribute-driven JNI glue).
SDL3-CS: carries Org.Libsdl.App.SDLActivity whose (IntPtr, JniHandleOwnership) JNI
activation constructor is only called via JNI and has no managed call-graph path. -->
<AndroidLinkSkip>$(AndroidLinkSkip);osu.Framework.Android;SDL3-CS</AndroidLinkSkip>
</PropertyGroup>

<ItemGroup>
Expand Down
63 changes: 30 additions & 33 deletions osu.Android/Linker.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,24 @@
Shader attribute scanning, TextureLoaderStore fallback chain, bindable system,
resource store extension routing — all reflection-dependent.
osu.Framework.Android: Android-specific host, input handling, surface wrappers.
AndroidGameActivity carries the (IntPtr, JniHandleOwnership) JNI-activation
constructor and RegisterAttribute-driven type-binding needed for all derived
Activity/Fragment types. Without preserve="all", ILLink will trim these
constructors (they are only reachable via JNI, not through any managed call
graph), causing TypeManager.Activate to throw NotSupportedException:
AndroidGameActivity is the abstract base of OsuGameActivity but does NOT declare
its own (IntPtr, JniHandleOwnership) JNI-activation constructor — that constructor
lives on its SDLActivity grandparent (in the SDL3-CS assembly, listed below).
Without preserve="all", ILLink trims reflection-only members such as
RegisterAttribute-driven type-binding and generated JNI glue — none of which have
a managed call-graph path.
SDL3-CS (ppy.SDL3-CS): Contains Org.Libsdl.App.SDLActivity, the Java-binding class
from which AndroidGameActivity (and transitively OsuGameActivity) inherits.
SDLActivity's JNI-activation constructors — in particular the
(IntPtr, JniHandleOwnership) overload used by TypeManager.Activate to wrap the
freshly-created Android Activity Java object in a managed peer — are never called
from managed code, so ILLink has no static call-graph path to them and strips them
under AndroidLinkMode=Full. The resulting crash is exactly the
"Could not activate JNI Handle ... as managed type 'osu.Android.OsuGameActivity'"
at startup.
NotSupportedException seen in the v2026.508.226 and v2026.508.228 field logs.
(The v2026.508.228 fix added osu.Framework.Android to Linker.xml and a
[DynamicDependency] on OsuGameActivity() but missed SDL3-CS entirely, because the
assumption was that AndroidGameActivity carried the JNI ctor — it does not.)
ppy.Veldrid.SPIRV: SPIR-V cross-compiler; dynamically loads shader reflector types.
(NuGet package ID and assembly name are both ppy.Veldrid.SPIRV — no mismatch.)

Expand All @@ -84,38 +95,24 @@
NuGet package ID → CLR assembly name (AssemblyName in csproj)
ppy.osu.Framework → osu.Framework
ppy.osu.Framework.Android → osu.Framework.Android
ppy.SDL3-CS → SDL3-CS
ppy.Veldrid.SPIRV → ppy.Veldrid.SPIRV (same — no ppy. mismatch here)
Using the NuGet package ID instead of the assembly name causes the preserve rule
to be stored but never matched: ILLink looks for an assembly named
"ppy.osu.Framework.Android" in the link set, finds only "osu.Framework.Android",
skips preservation, and freely trims the JNI-activation constructor from
AndroidGameActivity. The resulting startup crash ("Could not activate JNI Handle
... as managed type 'osu.Android.OsuGameActivity'") is exactly what was reported
in the v2026.508.225 field log.
Using the NuGet package ID instead of the CLR assembly name causes the preserve rule
to be stored but never matched.

NOTE: osu.Framework and osu.Framework.Android originate from net10.0 NuGet
NOTE: osu.Framework, osu.Framework.Android, and SDL3-CS originate from NuGet
packages and are not visible in ILLink's assembly search path at descriptor-
processing time. ILLink therefore emits warning IL2007 ("Could not resolve
assembly") for each entry. This warning is BENIGN and EXPECTED: ILLink stores
the preserve="all" rule in its internal table even when it cannot resolve the
assembly immediately. When the assembly is later encountered during the trimming
pass (where the .NET Android SDK does include it in the link set), ILLink finds
the stored rule and preserves the assembly in full. Removing these entries
"to silence the warnings" allows ILLink to freely trim JNI-only constructors
from AndroidGameActivity, crashing the app on launch.

ADDITIONAL DEFENCE (v2026.508.228+): OsuGameActivity carries a [DynamicDependency]
attribute that explicitly roots AndroidGameActivity's constructors from the
osu.Android assembly. Because osu.Android is unambiguously preserve="all" and is
always resolvable at descriptor-processing time, ILLink processes this root
reference before it can trim the JNI-activation constructor, regardless of whether
the IL2007-deferred rule for osu.Framework.Android is honoured. This supersedes
the v2026.508.227 approach (explicit OsuGameActivity(IntPtr, JniHandleOwnership)
constructor) which did not compile because AndroidGameActivity does not expose an
explicit (IntPtr, JniHandleOwnership) constructor — only its SDLActivity grandparent
does, and C# base() invocations resolve only against the direct parent class. -->
processing time, causing IL2007 ("Could not resolve assembly") for each entry.
In theory ILLink stores the rule for later application; in practice the deferred
mechanism has proven unreliable with ILLink 10.0.x + NuGet-resolved assemblies
(evidenced by the v2026.508.228 field crash being identical to v2026.508.226 despite
these entries being present). The primary protection is therefore AndroidLinkSkip
in osu.Android.props, which bypasses ILLink entirely for these assemblies; these
Linker.xml entries and the [DynamicDependency] attributes on OsuGameActivity serve
as secondary and tertiary backstops respectively. -->
<assembly fullname="osu.Framework" preserve="all" />
<assembly fullname="osu.Framework.Android" preserve="all" />
<assembly fullname="SDL3-CS" preserve="all" />
<assembly fullname="ppy.Veldrid.SPIRV" preserve="all" />

<!-- ═══════════════════════════════════════════════════════════════════════
Expand Down
16 changes: 11 additions & 5 deletions osu.Android/OsuGameActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,19 @@ protected override osu.Framework.Game CreateGame()
return game;
}

// Preserve AndroidGameActivity's (IntPtr, JniHandleOwnership) JNI-activation constructor.
// The [assembly: preserve="all"] descriptor for osu.Framework.Android in Linker.xml is deferred
// (IL2007) because the framework assembly is not yet in ILLink's search path at descriptor time.
// Placing [DynamicDependency] on this constructor (which lives in osu.Android — always resolvable)
// roots the AndroidGameActivity constructor in ILLink's walk before it can be trimmed away.
// Belt-and-suspenders: preserve the JNI activation constructor chain that TypeManager.Activate
// uses when Android creates this Activity: OsuGameActivity → AndroidGameActivity → SDLActivity.
// The primary guard is AndroidLinkSkip in osu.Android.props (bypasses ILLink entirely for
// osu.Framework.Android and SDL3-CS). These [DynamicDependency] attributes are a code-level
// backstop in case AndroidLinkSkip is removed: ILLink's walk from OsuGameActivity() roots
Comment on lines +92 to +96
// AndroidGameActivity constructors, which in turn roots SDLActivity() via the static call graph.
// The explicit SDL3-CS attribute roots SDLActivity(IntPtr, JniHandleOwnership) — the actual
// overload TypeManager.Activate calls — which has NO managed call-graph path and would
// otherwise be silently trimmed even if the parameterless constructor chain is preserved.
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors,
"osu.Framework.Android.AndroidGameActivity", "osu.Framework.Android")]
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors,
"Org.Libsdl.App.SDLActivity", "SDL3-CS")]
public OsuGameActivity()
{
game = new OsuGameAndroid(this);
Expand Down
2 changes: 1 addition & 1 deletion osu.Android/osu.Android.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Essentials" Version="10.0.51" />
<PackageReference Include="Microsoft.Maui.Essentials" Version="10.0.60" />
</ItemGroup>
<!-- Mono runtime environment variables baked into the APK. Enables Mono's
jitmap output (read by Native/crash_handler.cpp's resolveViaPerfmap()
Expand Down
4 changes: 2 additions & 2 deletions osu.Desktop/osu.Desktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="10.0.5" />
<PackageReference Include="System.IO.Packaging" Version="10.0.7" />
<!-- Held back due to invite bug in newer versions. See https://github.com/Lachee/discord-rpc-csharp/issues/286-->
<PackageReference Include="DiscordRichPresence" Version="1.5.0.51" />
<PackageReference Include="DiscordRichPresence" Version="1.6.1.70" />
<PackageReference Include="Velopack" Version="0.0.1298" />
</ItemGroup>
<ItemGroup Label="Resources">
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
<PackageReference Include="nunit" Version="4.5.1" />
<PackageReference Include="nunit" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="NUnit" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
</ItemGroup>
<PropertyGroup Label="Project">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="NUnit" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
</ItemGroup>
<PropertyGroup Label="Project">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="DeepEqual" Version="5.1.0" />
<PackageReference Include="Moq" Version="4.20.72" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="NUnit" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
</ItemGroup>
<PropertyGroup Label="Project">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="NUnit" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
</ItemGroup>
<PropertyGroup Label="Project">
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Tests.Android/osu.Game.Tests.Android.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Bogus" Version="35.6.5" />
<PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="DeepEqual" Version="5.1.0" />
<PackageReference Include="Moq" Version="4.20.72" />
</ItemGroup>
<!-- osu.Framework.Android depends on https://www.nuget.org/packages/Xamarin.AndroidX.Window,
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Bogus" Version="35.6.5" />
<PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="DeepEqual" Version="5.1.0" />
<PackageReference Include="Moq" Version="4.20.72" />
</ItemGroup>
</Project>
4 changes: 2 additions & 2 deletions osu.Game.Tests/osu.Game.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Bogus" Version="35.6.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="NUnit" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="Moq" Version="4.20.72" />
</ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<StartupObject>osu.Game.Tournament.Tests.TournamentTestRunner</StartupObject>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="NUnit" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
</ItemGroup>
<PropertyGroup Label="Project">
Expand Down
Loading
Loading