Closes https://github.com/ppy/osu/issues/26510.
Time for a rant.
Technically, this "broke" with 9e8d07d314,
but it is actually an end result of upstream behaviours that I am
failing to find a better description for than "utterly broken".
Squirrel (the installer we use) has unit tests. Which is great, power
to them. However, the method in which that testing is implemented leads
to epic levels of WTF breakage.
To determine whether Squirrel is being tested right now, it is checking
all currently loaded assemblies, and determining that if any loaded
assembly contains the magic string of "NUNIT" - among others - it must
be being tested right now:
2442721748/src/Squirrel/SimpleSplat/PlatformModeDetector.cs (L17-L32)
If one assumes that there is no conceivable way that an NUnit assembly
*may* be loaded *without* it being a test context, this *may* seem sane.
Foreshadowing.
(Now, to avoid being hypocritical, we also do this, *but* we do this
by checking if the *entry* assembly is an NUnit:
92db55a527/osu.Framework/Development/DebugUtils.cs (L16-L34)
which seems *much* saner, no?)
Now, why did this break with 9e8d07d314
*specifically*, you might wonder?
Well the reason is this line:
3d3f58c252/osu.Desktop/NVAPI.cs (L183)
Yes you are reading this correctly, it's not NVAPI anything itself that
breaks this, it is *a log statement*. To be precise, what the log
statement *does* to provoke this, is calling into framework. That causes
the framework assembly to load, *which* transitively loads the
`nunit.framework` assembly.
(If you ever find yourself wanting to find out this sort of cursed
knowledge - I hope you never need to - you can run something along
the lines of
dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4 -- .\osu!.exe
then open the resulting trace in PerfView, and then search the
`Microsoft-Windows-DotNETRuntime/AssemblyLoader/Start` log for
the cursed assembly. In this case, the relevant entry said something
along the lines of
HasStack="True"
ThreadID="23,924"
ProcessorNumber="0"
ClrInstanceID="6"
AssemblyName="nunit.framework, Version=3.13.3.0, Culture=neutral, PublicKeyToken=2638cd05610744eb"
AssemblyPath=""
RequestingAssembly="osu.Framework, Version=2024.113.0.0, Culture=neutral, PublicKeyToken=null"
AssemblyLoadContext="Default"
RequestingAssemblyLoadContext="Default"
ActivityID="/#21032/1/26/"
Either that or just comment the log line for kicks. But the above
is *much* faster.)
Now, what *happens* if Squirrel "detects" that it is being "tested"?
Well, it will refuse to close after executing the "hooks" defined via
`SquirrelAwareApp`:
2442721748/src/Squirrel/SquirrelAwareApp.cs (L85-L88)
and it will also refuse to create version shortcuts:
2442721748/src/Squirrel/UpdateManager.Shortcuts.cs (L63-L65)
Sounds familiar, don't it?
There are days on which I tire of computers. Today is one of them.
Temporary workaround for https://github.com/ppy/osu/issues/26404.
It appears that some audio files do not behave well with BASS, leading
BASS to report a contradictory state of affairs (i.e. a track that is
in playing state but also not progressing). This appears to be related
to seeking specifically, therefore only enable the validation of
playback rate in the most sensitive contexts, namely when any sort of
score submission is involved.
Fixes issue that occurs on *about* 246 beatmaps and was first described
by me on discord:
https://discord.com/channels/188630481301012481/188630652340404224/1154367700378865715
and then rediscovered again during work on
https://github.com/ppy/osu/pull/26405:
https://gist.github.com/bdach/414d5289f65b0399fa8f9732245a4f7c#venenog-on-ultmate-end-by-blacky-overdose-631
It so happens that in stable, due to .NET Framework internals, float
math would be performed using x87 registers and opcodes.
.NET (Core) however uses SSE instructions on 32- and 64-bit words.
x87 registers are _80 bits_ wide. Which is notably wider than _both_
float and double. Therefore, on a significant number of beatmaps,
the rounding would not produce correct values due to insufficient
precision.
See following gist for corroboration of the above:
https://gist.github.com/bdach/dcde58d5a3607b0408faa3aa2b67bf10
Thus, to crudely - but, seemingly accurately, after checking across
all ranked maps - emulate this, use `decimal`, which is slow, but has
bigger precision than `double`. The single known exception beatmap
in whose case this results in an incorrect result is
https://osu.ppy.sh/beatmapsets/1156087#osu/2625853
which is considered an "acceptable casualty" of sorts.
Doing this requires some fooling of the compiler / runtime (see second
inline comment in new method). To corroborate that this is required,
you can try the following code snippet:
Console.WriteLine(string.Join(' ', BitConverter.GetBytes(1.3f).Select(x => x.ToString("X2"))));
Console.WriteLine(string.Join(' ', BitConverter.GetBytes(1.3).Select(x => x.ToString("X2"))));
Console.WriteLine();
decimal d1 = (decimal)1.3f;
decimal d2 = (decimal)1.3;
decimal d3 = (decimal)(double)1.3f;
Console.WriteLine(string.Join(' ', decimal.GetBits(d1).SelectMany(BitConverter.GetBytes).Select(x => x.ToString("X2"))));
Console.WriteLine(string.Join(' ', decimal.GetBits(d2).SelectMany(BitConverter.GetBytes).Select(x => x.ToString("X2"))));
Console.WriteLine(string.Join(' ', decimal.GetBits(d3).SelectMany(BitConverter.GetBytes).Select(x => x.ToString("X2"))));
which will print
66 66 A6 3F
CD CC CC CC CC CC F4 3F
0D 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00
0D 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00
8C 5D 89 FB 3B 76 00 00 00 00 00 00 00 00 0E 00
Note that despite `d1` being converted from a less-precise floating-
-point value than `d2`, it still is represented 100% accurately as
a decimal number.
After applying this change, recomputation of legacy scoring attributes
for *all* rulesets will be required.