1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-05 14:22:55 +08:00

Merge branch 'master' into fix-breadcrumb-tab-item-click-area

This commit is contained in:
Bartłomiej Dach 2022-12-23 16:36:58 +01:00 committed by GitHub
commit 67aea34e7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
199 changed files with 1205 additions and 1098 deletions

View File

@ -31,7 +31,7 @@ jobs:
run: dotnet tool restore run: dotnet tool restore
- name: Restore Packages - name: Restore Packages
run: dotnet restore run: dotnet restore osu.Desktop.slnf
- name: Restore inspectcode cache - name: Restore inspectcode cache
uses: actions/cache@v3 uses: actions/cache@v3
@ -113,27 +113,36 @@ jobs:
with: with:
dotnet-version: "6.0.x" dotnet-version: "6.0.x"
- name: Setup MSBuild - name: Install .NET workloads
uses: microsoft/setup-msbuild@v1 run: dotnet workload install maui-android
- name: Build - name: Compile
run: msbuild osu.Android/osu.Android.csproj /restore /p:Configuration=Debug run: dotnet build -c Debug osu.Android.slnf
build-only-ios: build-only-ios:
name: Build only (iOS) name: Build only (iOS)
runs-on: macos-latest # change to macos-latest once GitHub finishes migrating all repositories to macOS 12.
runs-on: macos-12
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
# see https://github.com/actions/runner-images/issues/6771#issuecomment-1354713617
# remove once all workflow VMs use Xcode 14.1
- name: Set Xcode Version
shell: bash
run: |
sudo xcode-select -s "/Applications/Xcode_14.1.app"
echo "MD_APPLE_SDK_ROOT=/Applications/Xcode_14.1.app" >> $GITHUB_ENV
- name: Install .NET 6.0.x - name: Install .NET 6.0.x
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: "6.0.x" dotnet-version: "6.0.x"
# Contrary to seemingly any other msbuild, msbuild running on macOS/Mono - name: Install .NET Workloads
# cannot accept .sln(f) files as arguments. run: dotnet workload install maui-ios
# Build just the main game for now.
- name: Build - name: Build
run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug run: dotnet build -c Debug osu.iOS

View File

@ -32,7 +32,7 @@ If you are looking to install or test osu! without setting up a development envi
**Latest build:** **Latest build:**
| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 10.15+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) | | [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 10.15+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 13.4+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) |
| ------------- | ------------- | ------------- | ------------- | ------------- | | ------------- | ------------- | ------------- | ------------- | ------------- |
- The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets. - The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets.

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.EmptyFreeform</AssemblyTitle> <AssemblyTitle>osu.Game.Rulesets.EmptyFreeform</AssemblyTitle>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle> <AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.EmptyScrolling</AssemblyTitle> <AssemblyTitle>osu.Game.Rulesets.EmptyScrolling</AssemblyTitle>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle> <AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>

View File

@ -1,6 +1,6 @@
clone_depth: 1 clone_depth: 1
version: '{branch}-{build}' version: '{branch}-{build}'
image: Visual Studio 2019 image: Visual Studio 2022
cache: cache:
- '%LOCALAPPDATA%\NuGet\v3-cache -> appveyor.yml' - '%LOCALAPPDATA%\NuGet\v3-cache -> appveyor.yml'
@ -11,6 +11,8 @@ dotnet_csproj:
before_build: before_build:
- cmd: dotnet --info # Useful when version mismatch between CI and local - cmd: dotnet --info # Useful when version mismatch between CI and local
- cmd: dotnet workload install maui-android # Change to `dotnet workload restore` once there's no old projects
- cmd: dotnet workload install maui-ios # Change to `dotnet workload restore` once there's no old projects
- cmd: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects - cmd: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects
build: build:

View File

@ -1,64 +1,20 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<LangVersion>8.0</LangVersion> <SupportedOSPlatformVersion>21.0</SupportedOSPlatformVersion>
<OutputPath>bin\$(Configuration)</OutputPath> <RuntimeIdentifiers>android-x86;android-arm;android-arm64</RuntimeIdentifiers>
<WarningLevel>4</WarningLevel> <AndroidPackageFormat>apk</AndroidPackageFormat>
<SchemaVersion>2.0</SchemaVersion> <MandroidI18n>CJK;Mideast;Rare;West;Other;</MandroidI18n>
<BundleAssemblies>false</BundleAssemblies> <AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidMessageHandler</AndroidHttpClientHandlerType>
<AotAssemblies>false</AotAssemblies> <!-- NullabilityInfoContextSupport is disabled by default for Android -->
<OutputType>Library</OutputType> <NullabilityInfoContextSupport>true</NullabilityInfoContextSupport>
<FileAlignment>512</FileAlignment>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidApplication>True</AndroidApplication>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
<MandroidI18n>cjk,mideast,other,rare,west</MandroidI18n>
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DebugSymbols>True</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>False</Optimize>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AndroidManagedSymbols>false</AndroidManagedSymbols>
<AndroidUseSharedRuntime>true</AndroidUseSharedRuntime>
<EmbedAssembliesIntoApk>false</EmbedAssembliesIntoApk>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
<AndroidManagedSymbols>false</AndroidManagedSymbols>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk> <EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="$(MSBuildThisFileDirectory)\osu.licenseheader"> <PackageReference Include="ppy.osu.Framework.Android" Version="2022.1219.0" />
<Link>osu.licenseheader</Link>
</None>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Mono.Android" />
<Reference Include="Java.Interop" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1207.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1208.0" />
</ItemGroup>
<ItemGroup Label="Transitive Dependencies">
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
<PackageReference Include="Realm" Version="10.18.0" />
</ItemGroup>
<ItemGroup>
<LinkDescription Include="$(MSBuildThisFileDirectory)\osu.Android\Linker.xml"/>
</ItemGroup> </ItemGroup>
<PropertyGroup>
<!-- Fody does not handle Android build well, and warns when unchanged.
Since Realm objects are not declared directly in Android projects, simply disable Fody. -->
<DisableFody>true</DisableFody>
</PropertyGroup>
</Project> </Project>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" package="sh.ppy.osulazer" android:installLocation="auto" android:versionName="0.1.0"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" package="sh.ppy.osulazer" android:installLocation="auto" android:versionName="0.1.0">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" />
</manifest> </manifest>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<linker>
<assembly fullname="mscorlib">
<!-- see https://github.com/ppy/osu/issues/21516 -->
<type fullname="System.Globalization.*Calendar"/>
</assembly>
</linker>

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
@ -74,11 +75,23 @@ namespace osu.Android
Debug.Assert(Resources?.DisplayMetrics != null); Debug.Assert(Resources?.DisplayMetrics != null);
Point displaySize = new Point(); Point displaySize = new Point();
#pragma warning disable 618 // GetSize is deprecated
WindowManager.DefaultDisplay.GetSize(displaySize); WindowManager.DefaultDisplay.GetSize(displaySize);
#pragma warning restore 618
float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density; float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density;
bool isTablet = smallestWidthDp >= 600f; bool isTablet = smallestWidthDp >= 600f;
RequestedOrientation = DefaultOrientation = isTablet ? ScreenOrientation.FullUser : ScreenOrientation.SensorLandscape; RequestedOrientation = DefaultOrientation = isTablet ? ScreenOrientation.FullUser : ScreenOrientation.SensorLandscape;
// Currently (SDK 6.0.200), BundleAssemblies is not runnable for net6-android.
// The assembly files are not available as files either after native AOT.
// Manually load them so that they can be loaded by RulesetStore.loadFromAppDomain.
// REMEMBER to fully uninstall previous version every time when investigating this!
// Don't forget osu.Game.Tests.Android too.
Assembly.Load("osu.Game.Rulesets.Osu");
Assembly.Load("osu.Game.Rulesets.Taiko");
Assembly.Load("osu.Game.Rulesets.Catch");
Assembly.Load("osu.Game.Rulesets.Mania");
} }
protected override void OnNewIntent(Intent intent) => handleIntent(intent); protected override void OnNewIntent(Intent intent) => handleIntent(intent);
@ -127,7 +140,7 @@ namespace osu.Android
cursor.MoveToFirst(); cursor.MoveToFirst();
int filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName); int filenameColumn = cursor.GetColumnIndex(IOpenableColumns.DisplayName);
string filename = cursor.GetString(filenameColumn); string filename = cursor.GetString(filenameColumn);
// SharpCompress requires archive streams to be seekable, which the stream opened by // SharpCompress requires archive streams to be seekable, which the stream opened by

View File

@ -5,7 +5,7 @@
using System; using System;
using Android.App; using Android.App;
using Android.OS; using Microsoft.Maui.Devices;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Android.Input; using osu.Framework.Android.Input;
using osu.Framework.Input.Handlers; using osu.Framework.Input.Handlers;
@ -14,7 +14,6 @@ using osu.Game;
using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings;
using osu.Game.Updater; using osu.Game.Updater;
using osu.Game.Utils; using osu.Game.Utils;
using Xamarin.Essentials;
namespace osu.Android namespace osu.Android
{ {
@ -48,7 +47,7 @@ namespace osu.Android
// https://stackoverflow.com/questions/52977079/android-sdk-28-versioncode-in-packageinfo-has-been-deprecated // https://stackoverflow.com/questions/52977079/android-sdk-28-versioncode-in-packageinfo-has-been-deprecated
string versionName = string.Empty; string versionName = string.Empty;
if (Build.VERSION.SdkInt >= BuildVersionCodes.P) if (OperatingSystem.IsAndroidVersionAtLeast(28))
{ {
versionName = packageInfo.LongVersionCode.ToString(); versionName = packageInfo.LongVersionCode.ToString();
// ensure we only read the trailing portion of long (the part we are interested in). // ensure we only read the trailing portion of long (the part we are interested in).

View File

@ -1,73 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{D1D5F9A8-B40B-40E6-B02F-482D03346D3D}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Android</RootNamespace> <RootNamespace>osu.Android</RootNamespace>
<AssemblyName>osu.Android</AssemblyName> <AssemblyName>osu.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest> <UseMauiEssentials>true</UseMauiEssentials>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis> <!-- This currently causes random lockups during gameplay. https://github.com/mono/mono/issues/18973 -->
<EnableLLVM>false</EnableLLVM> <!-- This currently causes random lockups during gameplay. https://github.com/mono/mono/issues/18973 --> <EnableLLVM>false</EnableLLVM>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<AndroidDexTool>d8</AndroidDexTool>
<AndroidLinkTool>r8</AndroidLinkTool>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AndroidJoystickSettings.cs" /> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
<Compile Include="AndroidMouseSettings.cs" /> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<Compile Include="GameplayScreenRotationLocker.cs" /> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<Compile Include="OsuGameActivity.cs" /> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<Compile Include="OsuGameAndroid.cs" /> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj">
<Project>{58f6c80c-1253-4a0e-a465-b8c85ebeadf3}</Project>
<Name>osu.Game.Rulesets.Catch</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj">
<Project>{48f4582b-7687-4621-9cbe-5c24197cb536}</Project>
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
<Project>{c92a607b-1fdd-4954-9f92-03ff547d9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{f167e17a-7de6-4af5-b920-a5112296c695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\lazer.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Formats.Asn1">
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- using a different name because package name cannot contain 'catch' --> <!-- using a different name because package name cannot contain 'catch' -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Catch_Tests.Android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Catch_Tests.Android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" />
</manifest> </manifest>

View File

@ -1,49 +1,24 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Catch.Tests.Android</AssemblyName> <AssemblyName>osu.Game.Rulesets.Catch.Tests.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="MainActivity.cs" /> <Compile Include="..\osu.Game.Rulesets.Catch.Tests\**\*.cs" Exclude="**\obj\**">
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\osu.Game.Rulesets.Catch.Tests\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
<!-- TargetPath is relative to RootNamespace,
and DllResourceStore is relative to AssemblyName. -->
<EmbeddedResource Include="..\osu.Game.Rulesets.Catch.Tests\**\Resources\**\*">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
<TargetPath>Android\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
<Project>{58f6c80c-1253-4a0e-a465-b8c85ebeadf3}</Project> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Name>osu.Game.Rulesets.Catch</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="System.Formats.Asn1">
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -3,7 +3,6 @@
#nullable disable #nullable disable
using osu.Framework.iOS;
using UIKit; using UIKit;
namespace osu.Game.Rulesets.Catch.Tests.iOS namespace osu.Game.Rulesets.Catch.Tests.iOS
@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Catch.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }
} }

View File

@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.0</string> <string>13.4</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>

View File

@ -1,35 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{4004C7B7-1A62-43F1-9DF2-52450FA67E70}</ProjectGuid>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0-ios</TargetFramework>
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Catch.Tests.iOS</AssemblyName> <AssemblyName>osu.Game.Rulesets.Catch.Tests.iOS</AssemblyName>
</PropertyGroup> </PropertyGroup>
<Import Project="..\osu.iOS.props" /> <Import Project="..\osu.iOS.props" />
<ItemGroup> <ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>
<Compile Include="Application.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="..\osu.Game.Rulesets.Catch.Tests\**\*.cs" Exclude="**\obj\**"> <Compile Include="..\osu.Game.Rulesets.Catch.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
<Name>osu.Game</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj">
<Project>{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}</Project>
<Name>osu.Game.Rulesets.Catch</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project> </Project>

View File

@ -121,9 +121,7 @@ namespace osu.Game.Rulesets.Catch.Edit
return new SnapResult(originPosition, StartTime); return new SnapResult(originPosition, StartTime);
} }
return enumerateSnappingCandidates(time) return enumerateSnappingCandidates(time).MinBy(pos => Vector2.DistanceSquared(screenSpacePosition, pos.ScreenSpacePosition));
.OrderBy(pos => Vector2.DistanceSquared(screenSpacePosition, pos.ScreenSpacePosition))
.FirstOrDefault();
} }
private IEnumerable<SnapResult> enumerateSnappingCandidates(double time) private IEnumerable<SnapResult> enumerateSnappingCandidates(double time)

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>catch the fruit. to the beat.</Description> <Description>catch the fruit. to the beat.</Description>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Mania.Tests.Android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Mania.Tests.Android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!mania Test" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!mania Test" />
</manifest> </manifest>

View File

@ -1,49 +1,24 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{531F1092-DB27-445D-AA33-2A77C7187C99}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Game.Rulesets.Mania.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Mania.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Mania.Tests.Android</AssemblyName> <AssemblyName>osu.Game.Rulesets.Mania.Tests.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="MainActivity.cs" /> <Compile Include="..\osu.Game.Rulesets.Mania.Tests\**\*.cs" Exclude="**\obj\**">
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\osu.Game.Rulesets.Mania.Tests\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
<!-- TargetPath is relative to RootNamespace,
and DllResourceStore is relative to AssemblyName. -->
<EmbeddedResource Include="..\osu.Game.Rulesets.Mania.Tests\**\Resources\**\*">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
<TargetPath>Android\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<Project>{48f4582b-7687-4621-9cbe-5c24197cb536}</Project> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="System.Formats.Asn1">
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -3,7 +3,6 @@
#nullable disable #nullable disable
using osu.Framework.iOS;
using UIKit; using UIKit;
namespace osu.Game.Rulesets.Mania.Tests.iOS namespace osu.Game.Rulesets.Mania.Tests.iOS
@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }
} }

View File

@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.0</string> <string>13.4</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>

View File

@ -1,35 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{39FD990E-B6CE-4B2A-999F-BC008CF2C64C}</ProjectGuid>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0-ios</TargetFramework>
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<RootNamespace>osu.Game.Rulesets.Mania.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Mania.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Mania.Tests.iOS</AssemblyName> <AssemblyName>osu.Game.Rulesets.Mania.Tests.iOS</AssemblyName>
</PropertyGroup> </PropertyGroup>
<Import Project="..\osu.iOS.props" /> <Import Project="..\osu.iOS.props" />
<ItemGroup> <ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>
<Compile Include="Application.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="..\osu.Game.Rulesets.Mania.Tests\**\*.cs" Exclude="**\obj\**"> <Compile Include="..\osu.Game.Rulesets.Mania.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<Name>osu.Game</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj">
<Project>{48F4582B-7687-4621-9CBE-5C24197CB536}</Project>
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project> </Project>

View File

@ -35,8 +35,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
protected PatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) protected PatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap)
: base(hitObject, beatmap, previousPattern) : base(hitObject, beatmap, previousPattern)
{ {
if (random == null) throw new ArgumentNullException(nameof(random)); ArgumentNullException.ThrowIfNull(random);
if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap)); ArgumentNullException.ThrowIfNull(originalBeatmap);
Random = random; Random = random;
OriginalBeatmap = originalBeatmap; OriginalBeatmap = originalBeatmap;

View File

@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
{ {
if (hitObject == null) throw new ArgumentNullException(nameof(hitObject)); ArgumentNullException.ThrowIfNull(hitObject);
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); ArgumentNullException.ThrowIfNull(beatmap);
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern)); ArgumentNullException.ThrowIfNull(previousPattern);
HitObject = hitObject; HitObject = hitObject;
Beatmap = beatmap; Beatmap = beatmap;

View File

@ -22,8 +22,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
public static void Sort(T[] keys, IComparer<T> comparer) public static void Sort(T[] keys, IComparer<T> comparer)
{ {
if (keys == null) ArgumentNullException.ThrowIfNull(keys);
throw new ArgumentNullException(nameof(keys));
if (keys.Length == 0) if (keys.Length == 0)
return; return;

View File

@ -134,6 +134,9 @@ namespace osu.Game.Rulesets.Mania.UI
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
// must happen before children are disposed in base call to prevent illegal accesses to the hit explosion pool.
NewResult -= OnNewResult;
base.Dispose(isDisposing); base.Dispose(isDisposing);
if (skin != null) if (skin != null)

View File

@ -29,8 +29,7 @@ namespace osu.Game.Rulesets.Mania.UI
public ManiaPlayfield(List<StageDefinition> stageDefinitions) public ManiaPlayfield(List<StageDefinition> stageDefinitions)
{ {
if (stageDefinitions == null) ArgumentNullException.ThrowIfNull(stageDefinitions);
throw new ArgumentNullException(nameof(stageDefinitions));
if (stageDefinitions.Count <= 0) if (stageDefinitions.Count <= 0)
throw new ArgumentException("Can't have zero or fewer stages."); throw new ArgumentException("Can't have zero or fewer stages.");

View File

@ -156,6 +156,9 @@ namespace osu.Game.Rulesets.Mania.UI
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
// must happen before children are disposed in base call to prevent illegal accesses to the judgement pool.
NewResult -= OnNewResult;
base.Dispose(isDisposing); base.Dispose(isDisposing);
if (currentSkin != null) if (currentSkin != null)

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>smash the keys. to the beat.</Description> <Description>smash the keys. to the beat.</Description>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Osu.Tests.Android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Osu.Tests.Android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!standard Test" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!standard Test" />
</manifest> </manifest>

View File

@ -1,49 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{90CAB706-39CB-4B93-9629-3218A6FF8E9B}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Osu.Tests.Android</AssemblyName> <AssemblyName>osu.Game.Rulesets.Osu.Tests.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="MainActivity.cs" /> <Compile Include="..\osu.Game.Rulesets.Osu.Tests\**\*.cs" Exclude="**\obj\**">
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\osu.Game.Rulesets.Osu.Tests\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
<!-- TargetPath is relative to RootNamespace,
and DllResourceStore is relative to AssemblyName. -->
<EmbeddedResource Include="..\osu.Game.Rulesets.Osu.Tests\**\Resources\**\*">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
<TargetPath>Android\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<Project>{c92a607b-1fdd-4954-9f92-03ff547d9080}</Project> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Formats.Asn1"> <PackageReference Include="Moq" Version="4.17.2" />
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -3,7 +3,6 @@
#nullable disable #nullable disable
using osu.Framework.iOS;
using UIKit; using UIKit;
namespace osu.Game.Rulesets.Osu.Tests.iOS namespace osu.Game.Rulesets.Osu.Tests.iOS
@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }
} }

View File

@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.0</string> <string>13.4</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>

View File

@ -1,35 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <OutputType>Exe</OutputType>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform> <TargetFramework>net6.0-ios</TargetFramework>
<ProjectGuid>{6653CA6F-DB06-4604-A3FD-762E25C2AF96}</ProjectGuid> <SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Osu.Tests.iOS</AssemblyName> <AssemblyName>osu.Game.Rulesets.Osu.Tests.iOS</AssemblyName>
</PropertyGroup> </PropertyGroup>
<Import Project="..\osu.iOS.props" /> <Import Project="..\osu.iOS.props" />
<ItemGroup> <ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>
<Compile Include="Application.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="..\osu.Game.Rulesets.Osu.Tests\**\*.cs" Exclude="**\obj\**"> <Compile Include="..\osu.Game.Rulesets.Osu.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<Name>osu.Game</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
<Project>{C92A607B-1FDD-4954-9F92-03FF547D9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project> </Project>

View File

@ -133,6 +133,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
switch (e.Action) switch (e.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
Debug.Assert(e.NewItems != null);
// If inserting in the path (not appending), // If inserting in the path (not appending),
// update indices of existing connections after insert location // update indices of existing connections after insert location
if (e.NewStartingIndex < Pieces.Count) if (e.NewStartingIndex < Pieces.Count)
@ -164,6 +166,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
Debug.Assert(e.OldItems != null);
foreach (var point in e.OldItems.Cast<PathControlPoint>()) foreach (var point in e.OldItems.Cast<PathControlPoint>())
{ {
foreach (var piece in Pieces.Where(p => p.ControlPoint == point).ToArray()) foreach (var piece in Pieces.Where(p => p.ControlPoint == point).ToArray())

View File

@ -82,10 +82,10 @@ namespace osu.Game.Rulesets.Osu.Replays
private class ReplayFrameComparer : IComparer<ReplayFrame> private class ReplayFrameComparer : IComparer<ReplayFrame>
{ {
public int Compare(ReplayFrame f1, ReplayFrame f2) public int Compare(ReplayFrame? f1, ReplayFrame? f2)
{ {
if (f1 == null) throw new ArgumentNullException(nameof(f1)); ArgumentNullException.ThrowIfNull(f1);
if (f2 == null) throw new ArgumentNullException(nameof(f2)); ArgumentNullException.ThrowIfNull(f2);
return f1.Time.CompareTo(f2.Time); return f1.Time.CompareTo(f2.Time);
} }

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>click the circles. to the beat.</Description> <Description>click the circles. to the beat.</Description>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Taiko.Tests.Android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Taiko.Tests.Android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!taiko Test" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!taiko Test" />
</manifest> </manifest>

View File

@ -1,49 +1,24 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{3701A0A1-8476-42C6-B5C4-D24129B4A484}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Game.Rulesets.Taiko.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Taiko.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Taiko.Tests.Android</AssemblyName> <AssemblyName>osu.Game.Rulesets.Taiko.Tests.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="MainActivity.cs" /> <Compile Include="..\osu.Game.Rulesets.Taiko.Tests\**\*.cs" Exclude="**\obj\**">
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\osu.Game.Rulesets.Taiko.Tests\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
<!-- TargetPath is relative to RootNamespace,
and DllResourceStore is relative to AssemblyName. -->
<EmbeddedResource Include="..\osu.Game.Rulesets.Taiko.Tests\**\Resources\**\*">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
<TargetPath>Android\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<Project>{f167e17a-7de6-4af5-b920-a5112296c695}</Project> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="System.Formats.Asn1">
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -3,7 +3,6 @@
#nullable disable #nullable disable
using osu.Framework.iOS;
using UIKit; using UIKit;
namespace osu.Game.Rulesets.Taiko.Tests.iOS namespace osu.Game.Rulesets.Taiko.Tests.iOS
@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }
} }

View File

@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.0</string> <string>13.4</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>

View File

@ -1,35 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{7E408809-66AC-49D1-AF4D-98834F9B979A}</ProjectGuid>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0-ios</TargetFramework>
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<RootNamespace>osu.Game.Rulesets.Taiko.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Taiko.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Taiko.Tests.iOS</AssemblyName> <AssemblyName>osu.Game.Rulesets.Taiko.Tests.iOS</AssemblyName>
</PropertyGroup> </PropertyGroup>
<Import Project="..\osu.iOS.props" /> <Import Project="..\osu.iOS.props" />
<ItemGroup> <ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>
<Compile Include="Application.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="..\osu.Game.Rulesets.Taiko.Tests\**\*.cs" Exclude="**\obj\**"> <Compile Include="..\osu.Game.Rulesets.Taiko.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<Name>osu.Game</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{F167E17A-7DE6-4AF5-B920-A5112296C695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project> </Project>

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>bash the drum. to the beat.</Description> <Description>bash the drum. to the beat.</Description>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Tests.Android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Tests.Android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!visual Test" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!visual Test" />
</manifest> </manifest>

View File

@ -3,7 +3,9 @@
#nullable disable #nullable disable
using System.Reflection;
using Android.App; using Android.App;
using Android.OS;
using osu.Framework.Android; using osu.Framework.Android;
namespace osu.Game.Tests.Android namespace osu.Game.Tests.Android
@ -12,5 +14,16 @@ namespace osu.Game.Tests.Android
public class MainActivity : AndroidGameActivity public class MainActivity : AndroidGameActivity
{ {
protected override Framework.Game CreateGame() => new OsuTestBrowser(); protected override Framework.Game CreateGame() => new OsuTestBrowser();
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// See the comment in OsuGameActivity
Assembly.Load("osu.Game.Rulesets.Osu");
Assembly.Load("osu.Game.Rulesets.Taiko");
Assembly.Load("osu.Game.Rulesets.Catch");
Assembly.Load("osu.Game.Rulesets.Mania");
}
} }
} }

View File

@ -1,88 +1,34 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{5CC222DC-5716-4499-B897-DCBDDA4A5CF9}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Game.Tests</RootNamespace> <RootNamespace>osu.Game.Tests</RootNamespace>
<AssemblyName>osu.Game.Tests.Android</AssemblyName> <AssemblyName>osu.Game.Tests.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Include="MainActivity.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<NoWarn>$(NoWarn);CA2007</NoWarn> <NoWarn>$(NoWarn);CA2007</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\osu.Game.Tests\**\Beatmaps\**\*.cs"> <Compile Include="..\osu.Game.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
<Compile Remove="..\osu.Game.Tests\Beatmaps\Formats\OsuJsonDecoderTest.cs" /> <!-- TargetPath is relative to RootNamespace,
<Compile Include="..\osu.Game.Tests\**\Chat\**\*.cs"> and DllResourceStore is relative to AssemblyName. -->
<EmbeddedResource Include="..\osu.Game.Tests\**\Resources\**\*">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> <TargetPath>Android\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
<Compile Include="..\osu.Game.Tests\**\NonVisual\**\*.cs"> </EmbeddedResource>
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\osu.Game.Tests\**\Scores\**\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\osu.Game.Tests\**\ScrollAlgorithms\**\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\osu.Game.Tests\**\Visual\**\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\osu.Game.Tests\**\Resources\**\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\osu.Game.Tests\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
<Project>{58f6c80c-1253-4a0e-a465-b8c85ebeadf3}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<Name>osu.Game.Rulesets.Catch</Name> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
</ProjectReference> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{48f4582b-7687-4621-9cbe-5c24197cb536}</Project>
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
<Project>{c92a607b-1fdd-4954-9f92-03ff547d9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{f167e17a-7de6-4af5-b920-a5112296c695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="DeepEqual" Version="2.0.0" /> <PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Moq" Version="4.17.2" /> <PackageReference Include="Moq" Version="4.17.2" />
<PackageReference Include="System.Formats.Asn1">
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -3,7 +3,6 @@
#nullable disable #nullable disable
using osu.Framework.iOS;
using UIKit; using UIKit;
namespace osu.Game.Tests.iOS namespace osu.Game.Tests.iOS
@ -12,7 +11,7 @@ namespace osu.Game.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }
} }

View File

@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.0</string> <string>13.4</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>

View File

@ -1,54 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<ProjectGuid>{65FF8E19-6934-469B-B690-23C6D6E56A17}</ProjectGuid> <TargetFramework>net6.0-ios</TargetFramework>
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<RootNamespace>osu.Game.Tests</RootNamespace> <RootNamespace>osu.Game.Tests</RootNamespace>
<AssemblyName>osu.Game.Tests.iOS</AssemblyName> <AssemblyName>osu.Game.Tests.iOS</AssemblyName>
</PropertyGroup> </PropertyGroup>
<Import Project="..\osu.iOS.props" /> <Import Project="..\osu.iOS.props" />
<ItemGroup> <ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>
<Compile Include="Application.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="..\osu.Game.Tests\**\*.cs" Exclude="**\obj\**"> <Compile Include="..\osu.Game.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007</NoWarn>
</PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<Name>osu.Game</Name> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
</ProjectReference> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<Project>{C92A607B-1FDD-4954-9F92-03FF547D9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj">
<Project>{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}</Project>
<Name>osu.Game.Rulesets.Catch</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj">
<Project>{48F4582B-7687-4621-9CBE-5C24197CB536}</Project>
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{F167E17A-7DE6-4AF5-B920-A5112296C695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="DeepEqual" Version="2.0.0" /> <PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Moq" Version="4.17.2" /> <PackageReference Include="Moq" Version="4.17.2" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project> </Project>

View File

@ -26,6 +26,16 @@ namespace osu.Game.Tests.Chat
MessageFormatter.WebsiteRootUrl = originalWebsiteRootUrl; MessageFormatter.WebsiteRootUrl = originalWebsiteRootUrl;
} }
[Test]
public void TestUnsupportedProtocolLink()
{
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a gopher://really-old-protocol we don't support." });
Assert.AreEqual(result.Content, result.DisplayContent);
Assert.AreEqual(1, result.Links.Count);
Assert.AreEqual("gopher://really-old-protocol", result.Links[0].Url);
}
[Test] [Test]
public void TestBareLink() public void TestBareLink()
{ {

View File

@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Editing
{ {
public partial class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene public partial class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene
{ {
private ZoomableScrollContainer scrollContainer; private TestZoomableScrollContainer scrollContainer;
private Drawable innerBox; private Drawable innerBox;
[SetUpSteps] [SetUpSteps]
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Editing
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(30) Colour = OsuColour.Gray(30)
}, },
scrollContainer = new ZoomableScrollContainer(1, 60, 1) scrollContainer = new TestZoomableScrollContainer(1, 60, 1)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -93,6 +93,14 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("Inner container width matches scroll container", () => innerBox.DrawWidth == scrollContainer.DrawWidth); AddAssert("Inner container width matches scroll container", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
} }
[Test]
public void TestWidthUpdatesOnSecondZoomSetup()
{
AddAssert("Inner container width = 1x", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
AddStep("reload zoom", () => scrollContainer.SetupZoom(10, 10, 60));
AddAssert("Inner container width = 10x", () => innerBox.DrawWidth == scrollContainer.DrawWidth * 10);
}
[Test] [Test]
public void TestZoom0() public void TestZoom0()
{ {
@ -190,5 +198,15 @@ namespace osu.Game.Tests.Visual.Editing
private Quad scrollQuad => scrollContainer.ScreenSpaceDrawQuad; private Quad scrollQuad => scrollContainer.ScreenSpaceDrawQuad;
private Quad boxQuad => innerBox.ScreenSpaceDrawQuad; private Quad boxQuad => innerBox.ScreenSpaceDrawQuad;
private partial class TestZoomableScrollContainer : ZoomableScrollContainer
{
public TestZoomableScrollContainer(int minimum, float maximum, float initial)
: base(minimum, maximum, initial)
{
}
public new void SetupZoom(float initial, float minimum, float maximum) => base.SetupZoom(initial, minimum, maximum);
}
} }
} }

View File

@ -77,14 +77,14 @@ namespace osu.Game.Tests.Visual.Online
{ {
var comments = this.ChildrenOfType<DrawableComment>(); var comments = this.ChildrenOfType<DrawableComment>();
var ourComment = comments.SingleOrDefault(x => x.Comment.Id == 1); var ourComment = comments.SingleOrDefault(x => x.Comment.Id == 1);
return ourComment != null && ourComment.ChildrenOfType<OsuSpriteText>().Any(x => x.Text == "Delete"); return ourComment != null && ourComment.ChildrenOfType<OsuSpriteText>().Any(x => x.Text == "delete");
}); });
AddAssert("Second doesn't", () => AddAssert("Second doesn't", () =>
{ {
var comments = this.ChildrenOfType<DrawableComment>(); var comments = this.ChildrenOfType<DrawableComment>();
var ourComment = comments.Single(x => x.Comment.Id == 2); var ourComment = comments.Single(x => x.Comment.Id == 2);
return ourComment.ChildrenOfType<OsuSpriteText>().All(x => x.Text != "Delete"); return ourComment.ChildrenOfType<OsuSpriteText>().All(x => x.Text != "delete");
}); });
} }
@ -102,7 +102,7 @@ namespace osu.Game.Tests.Visual.Online
}); });
AddStep("It has delete button", () => AddStep("It has delete button", () =>
{ {
var btn = ourComment.ChildrenOfType<OsuSpriteText>().Single(x => x.Text == "Delete"); var btn = ourComment.ChildrenOfType<OsuSpriteText>().Single(x => x.Text == "delete");
InputManager.MoveMouseTo(btn); InputManager.MoveMouseTo(btn);
}); });
AddStep("Click delete button", () => AddStep("Click delete button", () =>
@ -175,7 +175,7 @@ namespace osu.Game.Tests.Visual.Online
}); });
AddStep("It has delete button", () => AddStep("It has delete button", () =>
{ {
var btn = ourComment.ChildrenOfType<OsuSpriteText>().Single(x => x.Text == "Delete"); var btn = ourComment.ChildrenOfType<OsuSpriteText>().Single(x => x.Text == "delete");
InputManager.MoveMouseTo(btn); InputManager.MoveMouseTo(btn);
}); });
AddStep("Click delete button", () => AddStep("Click delete button", () =>
@ -189,7 +189,7 @@ namespace osu.Game.Tests.Visual.Online
if (request is not CommentDeleteRequest req) if (request is not CommentDeleteRequest req)
return false; return false;
req.TriggerFailure(new Exception()); req.TriggerFailure(new InvalidOperationException());
delete = true; delete = true;
return false; return false;
}; };
@ -245,7 +245,7 @@ namespace osu.Game.Tests.Visual.Online
}); });
AddStep("Click the button", () => AddStep("Click the button", () =>
{ {
var btn = targetComment.ChildrenOfType<OsuSpriteText>().Single(x => x.Text == "Report"); var btn = targetComment.ChildrenOfType<OsuSpriteText>().Single(x => x.Text == "report");
InputManager.MoveMouseTo(btn); InputManager.MoveMouseTo(btn);
InputManager.Click(MouseButton.Left); InputManager.Click(MouseButton.Left);
}); });

View File

@ -16,13 +16,17 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Graphics.Containers.Markdown; using osu.Game.Graphics.Containers.Markdown;
using osu.Game.Graphics.Containers.Markdown.Footnotes;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Wiki.Markdown; using osu.Game.Overlays.Wiki.Markdown;
using osu.Game.Users.Drawables;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
public partial class TestSceneWikiMarkdownContainer : OsuTestScene public partial class TestSceneWikiMarkdownContainer : OsuManualInputManagerTestScene
{ {
private OverlayScrollContainer scrollContainer;
private TestMarkdownContainer markdownContainer; private TestMarkdownContainer markdownContainer;
[Cached] [Cached]
@ -38,16 +42,26 @@ namespace osu.Game.Tests.Visual.Online
Colour = overlayColour.Background5, Colour = overlayColour.Background5,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
new BasicScrollContainer scrollContainer = new OverlayScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(20), Padding = new MarginPadding(20),
}
};
scrollContainer.Child = new DependencyProvidingContainer
{
CachedDependencies = new (Type, object)[]
{
(typeof(OverlayScrollContainer), scrollContainer)
},
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = markdownContainer = new TestMarkdownContainer Child = markdownContainer = new TestMarkdownContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
} }
}
}; };
}); });
@ -197,6 +211,63 @@ Line after image";
markdownContainer.CurrentPath = @"https://dev.ppy.sh"; markdownContainer.CurrentPath = @"https://dev.ppy.sh";
markdownContainer.Text = "::{flag=\"AU\"}:: ::{flag=\"ZZ\"}::"; markdownContainer.Text = "::{flag=\"AU\"}:: ::{flag=\"ZZ\"}::";
}); });
AddAssert("Two flags visible", () => markdownContainer.ChildrenOfType<DrawableFlag>().Count(), () => Is.EqualTo(2));
}
[Test]
public void TestHeadingWithIdAttribute()
{
AddStep("Add heading with ID", () =>
{
markdownContainer.Text = "# This is a heading with an ID {#this-is-the-id}";
});
AddAssert("ID not visible", () => markdownContainer.ChildrenOfType<SpriteText>().All(spriteText => spriteText.Text != "{#this-is-the-id}"));
}
[Test]
public void TestFootnotes()
{
AddStep("set content", () => markdownContainer.Text = @"This text has a footnote[^test].
Here's some more text[^test2] with another footnote!
# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam efficitur laoreet posuere. Ut accumsan tortor in ipsum tincidunt ultrices. Suspendisse a malesuada tellus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce a sagittis nibh. In et velit sit amet mauris aliquet consectetur quis vehicula lorem. Etiam sit amet tellus ac velit ornare maximus. Donec quis metus eget libero ullamcorper imperdiet id vitae arcu. Vivamus iaculis rhoncus purus malesuada mollis. Vestibulum dictum at nisi sed tincidunt. Suspendisse finibus, ipsum ut dapibus commodo, leo eros porttitor sapien, non scelerisque nisi ligula sed ex. Pellentesque magna orci, hendrerit eu iaculis sit amet, ullamcorper in urna. Vivamus dictum mauris orci, nec facilisis dolor fringilla eu. Sed at porttitor nisi, at venenatis urna. Ut at orci vitae libero semper ullamcorper eu ut risus. Mauris hendrerit varius enim, ut varius nisi feugiat mattis.
## In at eros urna. Sed ipsum lorem, tempor sit amet purus in, vehicula pellentesque leo. Fusce volutpat pellentesque velit sit amet porttitor. Nulla eget erat ex. Praesent eu lacinia est, quis vehicula lacus. Donec consequat ultrices neque, at finibus quam efficitur vel. Vestibulum molestie nisl sit amet metus semper, at vestibulum massa rhoncus. Quisque imperdiet suscipit augue, et dignissim odio eleifend ut.
Aliquam sed vestibulum mauris, ut lobortis elit. Sed quis lacinia erat. Nam ultricies, risus non pellentesque sollicitudin, mauris dolor tincidunt neque, ac porta ipsum dui quis libero. Integer eget velit neque. Vestibulum venenatis mauris vitae rutrum vestibulum. Maecenas suscipit eu purus eu tempus. Nam dui nisl, bibendum condimentum mollis et, gravida vel dui. Sed et eros rutrum, facilisis sapien eu, mattis ligula. Fusce finibus pulvinar dolor quis consequat.
Donec ipsum felis, feugiat vel fermentum at, commodo eu sapien. Suspendisse nec enim vitae felis laoreet laoreet. Phasellus purus quam, fermentum a pharetra vel, tempor et urna. Integer vitae quam diam. Aliquam tincidunt tortor a iaculis convallis. Suspendisse potenti. Cras quis risus quam. Nullam tincidunt in lorem posuere sagittis.
Phasellus eu nunc nec ligula semper fringilla. Aliquam magna neque, placerat sed urna tristique, laoreet pharetra nulla. Vivamus maximus turpis purus, eu viverra dolor sodales porttitor. Praesent bibendum sapien purus, sed ultricies dolor iaculis sed. Fusce congue hendrerit malesuada. Nulla nulla est, auctor ac fringilla sed, ornare a lorem. Donec quis velit imperdiet, imperdiet sem non, pellentesque sapien. Maecenas in orci id ipsum placerat facilisis non sed nisi. Duis dictum lorem sodales odio dictum eleifend. Vestibulum bibendum euismod quam, eget pharetra orci facilisis sed. Vivamus at diam non ipsum consequat tristique. Pellentesque gravida dignissim pellentesque. Donec ullamcorper lacinia orci, id consequat purus faucibus quis. Phasellus metus nunc, iaculis a interdum vel, congue sed erat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam eros libero, hendrerit luctus nulla vitae, luctus maximus nunc.
[^test]: This is a **footnote**.
[^test2]: This is another footnote [with a link](https://google.com/)!");
AddStep("shrink scroll height", () => scrollContainer.Height = 0.5f);
AddStep("press second footnote link", () =>
{
InputManager.MoveMouseTo(markdownContainer.ChildrenOfType<OsuMarkdownFootnoteLink>().ElementAt(1));
InputManager.Click(MouseButton.Left);
});
AddUntilStep("second footnote scrolled into view", () =>
{
var footnote = markdownContainer.ChildrenOfType<OsuMarkdownFootnote>().ElementAt(1);
return scrollContainer.ScreenSpaceDrawQuad.Contains(footnote.ScreenSpaceDrawQuad.TopLeft)
&& scrollContainer.ScreenSpaceDrawQuad.Contains(footnote.ScreenSpaceDrawQuad.BottomRight);
});
AddStep("press first footnote backlink", () =>
{
InputManager.MoveMouseTo(markdownContainer.ChildrenOfType<OsuMarkdownFootnoteBacklink>().First());
InputManager.Click(MouseButton.Left);
});
AddUntilStep("first footnote link scrolled into view", () =>
{
var footnote = markdownContainer.ChildrenOfType<OsuMarkdownFootnoteLink>().First();
return scrollContainer.ScreenSpaceDrawQuad.Contains(footnote.ScreenSpaceDrawQuad.TopLeft)
&& scrollContainer.ScreenSpaceDrawQuad.Contains(footnote.ScreenSpaceDrawQuad.BottomRight);
});
} }
private partial class TestMarkdownContainer : WikiMarkdownContainer private partial class TestMarkdownContainer : WikiMarkdownContainer

View File

@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.SongSelect
if (testRequest.Progress >= 0.5f) if (testRequest.Progress >= 0.5f)
{ {
testRequest.TriggerFailure(new Exception()); testRequest.TriggerFailure(new InvalidOperationException());
return true; return true;
} }
} }

View File

@ -32,7 +32,7 @@ namespace osu.Game.Tournament.Components
public TournamentBeatmapPanel(TournamentBeatmap beatmap, string mod = null) public TournamentBeatmapPanel(TournamentBeatmap beatmap, string mod = null)
{ {
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); ArgumentNullException.ThrowIfNull(beatmap);
Beatmap = beatmap; Beatmap = beatmap;
this.mod = mod; this.mod = mod;

View File

@ -245,8 +245,10 @@ namespace osu.Game.Tournament.IPC
{ {
string stableInstallPath; string stableInstallPath;
#pragma warning disable CA1416
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", ""); stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", "");
#pragma warning restore CA1416
if (ipcFileExistsInDirectory(stableInstallPath)) if (ipcFileExistsInDirectory(stableInstallPath))
return stableInstallPath; return stableInstallPath;

View File

@ -70,7 +70,7 @@ namespace osu.Game.Tournament
private async Task checkForChanges() private async Task checkForChanges()
{ {
string serialisedLadder = await Task.Run(() => tournamentGame.GetSerialisedLadder()); string serialisedLadder = await Task.Run(() => tournamentGame.GetSerialisedLadder()).ConfigureAwait(false);
// If a save hasn't been triggered by the user yet, populate the initial value // If a save hasn't been triggered by the user yet, populate the initial value
lastSerialisedLadder ??= serialisedLadder; lastSerialisedLadder ??= serialisedLadder;

View File

@ -5,7 +5,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -54,8 +53,6 @@ namespace osu.Game.Tournament.Screens.Editors
}); });
} }
Debug.Assert(countries != null);
foreach (var c in countries) foreach (var c in countries)
Storage.Add(c); Storage.Add(c);
} }

View File

@ -4,6 +4,7 @@
#nullable disable #nullable disable
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -102,10 +103,14 @@ namespace osu.Game.Tournament.Screens.Editors
switch (args.Action) switch (args.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
args.NewItems.Cast<TModel>().ForEach(i => flow.Add(CreateDrawable(i))); args.NewItems.Cast<TModel>().ForEach(i => flow.Add(CreateDrawable(i)));
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
args.OldItems.Cast<TModel>().ForEach(i => flow.RemoveAll(d => d.Model == i, true)); args.OldItems.Cast<TModel>().ForEach(i => flow.RemoveAll(d => d.Model == i, true));
break; break;
} }

View File

@ -5,6 +5,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -20,8 +21,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
{ {
public partial class LadderEditorSettings : PlayerSettingsGroup public partial class LadderEditorSettings : PlayerSettingsGroup
{ {
private const int padding = 10;
private SettingsDropdown<TournamentRound> roundDropdown; private SettingsDropdown<TournamentRound> roundDropdown;
private PlayerCheckbox losersCheckbox; private PlayerCheckbox losersCheckbox;
private DateTextBox dateTimeBox; private DateTextBox dateTimeBox;
@ -103,10 +102,14 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
switch (args.Action) switch (args.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
args.NewItems.Cast<TournamentRound>().ForEach(add); args.NewItems.Cast<TournamentRound>().ForEach(add);
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
args.OldItems.Cast<TournamentRound>().ForEach(i => Control.RemoveDropdownItem(i)); args.OldItems.Cast<TournamentRound>().ForEach(i => Control.RemoveDropdownItem(i));
break; break;
} }

View File

@ -5,6 +5,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
@ -25,10 +26,14 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
switch (args.Action) switch (args.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
args.NewItems.Cast<TournamentTeam>().ForEach(add); args.NewItems.Cast<TournamentTeam>().ForEach(add);
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
args.OldItems.Cast<TournamentTeam>().ForEach(i => Control.RemoveDropdownItem(i)); args.OldItems.Cast<TournamentTeam>().ForEach(i => Control.RemoveDropdownItem(i));
break; break;
} }

View File

@ -4,6 +4,7 @@
#nullable disable #nullable disable
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Caching; using osu.Framework.Caching;
@ -81,11 +82,15 @@ namespace osu.Game.Tournament.Screens.Ladder
switch (args.Action) switch (args.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
Debug.Assert(args.NewItems != null);
foreach (var p in args.NewItems.Cast<TournamentMatch>()) foreach (var p in args.NewItems.Cast<TournamentMatch>())
addMatch(p); addMatch(p);
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
Debug.Assert(args.OldItems != null);
foreach (var p in args.OldItems.Cast<TournamentMatch>()) foreach (var p in args.OldItems.Cast<TournamentMatch>())
{ {
foreach (var d in MatchesContainer.Where(d => d.Match == p)) foreach (var d in MatchesContainer.Where(d => d.Match == p))
@ -153,7 +158,7 @@ namespace osu.Game.Tournament.Screens.Ladder
foreach (var round in LadderInfo.Rounds) foreach (var round in LadderInfo.Rounds)
{ {
var topMatch = MatchesContainer.Where(p => !p.Match.Losers.Value && p.Match.Round.Value == round).OrderBy(p => p.Y).FirstOrDefault(); var topMatch = MatchesContainer.Where(p => !p.Match.Losers.Value && p.Match.Round.Value == round).MinBy(p => p.Y);
if (topMatch == null) continue; if (topMatch == null) continue;
@ -167,7 +172,7 @@ namespace osu.Game.Tournament.Screens.Ladder
foreach (var round in LadderInfo.Rounds) foreach (var round in LadderInfo.Rounds)
{ {
var topMatch = MatchesContainer.Where(p => p.Match.Losers.Value && p.Match.Round.Value == round).OrderBy(p => p.Y).FirstOrDefault(); var topMatch = MatchesContainer.Where(p => p.Match.Losers.Value && p.Match.Round.Value == round).MinBy(p => p.Y);
if (topMatch == null) continue; if (topMatch == null) continue;

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>tools for tournaments.</Description> <Description>tools for tournaments.</Description>

View File

@ -35,7 +35,7 @@ namespace osu.Game.Audio
public bool Equals(SampleInfo? other) public bool Equals(SampleInfo? other)
=> other != null && sampleNames.SequenceEqual(other.sampleNames); => other != null && sampleNames.SequenceEqual(other.sampleNames);
public override bool Equals(object obj) public override bool Equals(object? obj)
=> obj is SampleInfo other && Equals(other); => obj is SampleInfo other && Equals(other);
} }
} }

View File

@ -44,7 +44,7 @@ namespace osu.Game.Beatmaps
public override async Task<Live<BeatmapSetInfo>?> ImportAsUpdate(ProgressNotification notification, ImportTask importTask, BeatmapSetInfo original) public override async Task<Live<BeatmapSetInfo>?> ImportAsUpdate(ProgressNotification notification, ImportTask importTask, BeatmapSetInfo original)
{ {
var imported = await Import(notification, new[] { importTask }); var imported = await Import(notification, new[] { importTask }).ConfigureAwait(true);
if (!imported.Any()) if (!imported.Any())
return null; return null;

View File

@ -178,7 +178,7 @@ namespace osu.Game.Beatmaps
{ {
try try
{ {
await cacheDownloadRequest.PerformAsync(); await cacheDownloadRequest.PerformAsync().ConfigureAwait(false);
} }
catch catch
{ {

View File

@ -16,7 +16,7 @@ namespace osu.Game.Beatmaps.ControlPoints
public void AttachGroup(ControlPointGroup pointGroup) => Time = pointGroup.Time; public void AttachGroup(ControlPointGroup pointGroup) => Time = pointGroup.Time;
public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time); public int CompareTo(ControlPoint? other) => Time.CompareTo(other?.Time);
public virtual Color4 GetRepresentingColour(OsuColour colours) => colours.Yellow; public virtual Color4 GetRepresentingColour(OsuColour colours) => colours.Yellow;
@ -32,7 +32,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// </summary> /// </summary>
public ControlPoint DeepClone() public ControlPoint DeepClone()
{ {
var copy = (ControlPoint)Activator.CreateInstance(GetType()); var copy = (ControlPoint)Activator.CreateInstance(GetType())!;
copy.CopyFrom(this); copy.CopyFrom(this);

View File

@ -26,7 +26,7 @@ namespace osu.Game.Beatmaps.ControlPoints
Time = time; Time = time;
} }
public int CompareTo(ControlPointGroup other) => Time.CompareTo(other.Time); public int CompareTo(ControlPointGroup? other) => Time.CompareTo(other?.Time);
public void Add(ControlPoint point) public void Add(ControlPoint point)
{ {

View File

@ -70,14 +70,14 @@ namespace osu.Game.Beatmaps.ControlPoints
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public double BPMMaximum => public double BPMMaximum =>
60000 / (TimingPoints.OrderBy(c => c.BeatLength).FirstOrDefault() ?? TimingControlPoint.DEFAULT).BeatLength; 60000 / (TimingPoints.MinBy(c => c.BeatLength) ?? TimingControlPoint.DEFAULT).BeatLength;
/// <summary> /// <summary>
/// Finds the minimum BPM represented by any timing control point. /// Finds the minimum BPM represented by any timing control point.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public double BPMMinimum => public double BPMMinimum =>
60000 / (TimingPoints.OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? TimingControlPoint.DEFAULT).BeatLength; 60000 / (TimingPoints.MaxBy(c => c.BeatLength) ?? TimingControlPoint.DEFAULT).BeatLength;
/// <summary> /// <summary>
/// Remove all <see cref="ControlPointGroup"/>s and return to a pristine state. /// Remove all <see cref="ControlPointGroup"/>s and return to a pristine state.
@ -211,8 +211,7 @@ namespace osu.Game.Beatmaps.ControlPoints
public static T BinarySearch<T>(IReadOnlyList<T> list, double time) public static T BinarySearch<T>(IReadOnlyList<T> list, double time)
where T : class, IControlPoint where T : class, IControlPoint
{ {
if (list == null) ArgumentNullException.ThrowIfNull(list);
throw new ArgumentNullException(nameof(list));
if (list.Count == 0) if (list.Count == 0)
return null; return null;
@ -300,7 +299,7 @@ namespace osu.Game.Beatmaps.ControlPoints
public ControlPointInfo DeepClone() public ControlPointInfo DeepClone()
{ {
var controlPointInfo = (ControlPointInfo)Activator.CreateInstance(GetType()); var controlPointInfo = (ControlPointInfo)Activator.CreateInstance(GetType())!;
foreach (var point in AllControlPoints) foreach (var point in AllControlPoints)
controlPointInfo.Add(point.Time, point.DeepClone()); controlPointInfo.Add(point.Time, point.DeepClone());

View File

@ -51,11 +51,11 @@ namespace osu.Game.Beatmaps
if (!recommendedDifficultyMapping.TryGetValue(r, out double recommendation)) if (!recommendedDifficultyMapping.TryGetValue(r, out double recommendation))
continue; continue;
BeatmapInfo beatmapInfo = beatmaps.Where(b => b.Ruleset.ShortName.Equals(r)).OrderBy(b => BeatmapInfo beatmapInfo = beatmaps.Where(b => b.Ruleset.ShortName.Equals(r, StringComparison.Ordinal)).MinBy(b =>
{ {
double difference = b.StarRating - recommendation; double difference = b.StarRating - recommendation;
return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder
}).FirstOrDefault(); });
if (beatmapInfo != null) if (beatmapInfo != null)
return beatmapInfo; return beatmapInfo;
@ -90,7 +90,7 @@ namespace osu.Game.Beatmaps
return recommendedDifficultyMapping return recommendedDifficultyMapping
.OrderByDescending(pair => pair.Value) .OrderByDescending(pair => pair.Value)
.Select(pair => pair.Key) .Select(pair => pair.Key)
.Where(r => !r.Equals(ruleset.Value.ShortName)) .Where(r => !r.Equals(ruleset.Value.ShortName, StringComparison.Ordinal))
.Prepend(ruleset.Value.ShortName); .Prepend(ruleset.Value.ShortName);
} }
} }

View File

@ -15,8 +15,7 @@ namespace osu.Game.Beatmaps.Drawables
public BeatmapBackgroundSprite(IWorkingBeatmap working) public BeatmapBackgroundSprite(IWorkingBeatmap working)
{ {
if (working == null) ArgumentNullException.ThrowIfNull(working);
throw new ArgumentNullException(nameof(working));
this.working = working; this.working = working;
} }

View File

@ -18,8 +18,7 @@ namespace osu.Game.Beatmaps.Drawables
public OnlineBeatmapSetCover(IBeatmapSetOnlineInfo set, BeatmapSetCoverType type = BeatmapSetCoverType.Cover) public OnlineBeatmapSetCover(IBeatmapSetOnlineInfo set, BeatmapSetCoverType type = BeatmapSetCoverType.Cover)
{ {
if (set == null) ArgumentNullException.ThrowIfNull(set);
throw new ArgumentNullException(nameof(set));
this.set = set; this.set = set;
this.type = type; this.type = type;

View File

@ -57,8 +57,7 @@ namespace osu.Game.Beatmaps.Formats
public static Decoder<T> GetDecoder<T>(LineBufferedReader stream) public static Decoder<T> GetDecoder<T>(LineBufferedReader stream)
where T : new() where T : new()
{ {
if (stream == null) ArgumentNullException.ThrowIfNull(stream);
throw new ArgumentNullException(nameof(stream));
if (!decoders.TryGetValue(typeof(T), out var typedDecoders)) if (!decoders.TryGetValue(typeof(T), out var typedDecoders))
throw new IOException(@"Unknown decoder type"); throw new IOException(@"Unknown decoder type");

View File

@ -91,15 +91,15 @@ namespace osu.Game.Configuration
OrderPosition = orderPosition; OrderPosition = orderPosition;
} }
public int CompareTo(SettingSourceAttribute other) public int CompareTo(SettingSourceAttribute? other)
{ {
if (OrderPosition == other.OrderPosition) if (OrderPosition == other?.OrderPosition)
return 0; return 0;
// unordered items come last (are greater than any ordered items). // unordered items come last (are greater than any ordered items).
if (OrderPosition == null) if (OrderPosition == null)
return 1; return 1;
if (other.OrderPosition == null) if (other?.OrderPosition == null)
return -1; return -1;
// ordered items are sorted by the order value. // ordered items are sorted by the order value.
@ -113,7 +113,7 @@ namespace osu.Game.Configuration
{ {
foreach (var (attr, property) in obj.GetOrderedSettingsSourceProperties()) foreach (var (attr, property) in obj.GetOrderedSettingsSourceProperties())
{ {
object value = property.GetValue(obj); object value = property.GetValue(obj)!;
if (attr.SettingControlType != null) if (attr.SettingControlType != null)
{ {
@ -121,7 +121,7 @@ namespace osu.Game.Configuration
if (controlType.EnumerateBaseTypes().All(t => !t.IsGenericType || t.GetGenericTypeDefinition() != typeof(SettingsItem<>))) if (controlType.EnumerateBaseTypes().All(t => !t.IsGenericType || t.GetGenericTypeDefinition() != typeof(SettingsItem<>)))
throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} had an unsupported custom control type ({controlType.ReadableName()})"); throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} had an unsupported custom control type ({controlType.ReadableName()})");
var control = (Drawable)Activator.CreateInstance(controlType); var control = (Drawable)Activator.CreateInstance(controlType)!;
controlType.GetProperty(nameof(SettingsItem<object>.SettingSourceObject))?.SetValue(control, obj); controlType.GetProperty(nameof(SettingsItem<object>.SettingSourceObject))?.SetValue(control, obj);
controlType.GetProperty(nameof(SettingsItem<object>.LabelText))?.SetValue(control, attr.Label); controlType.GetProperty(nameof(SettingsItem<object>.LabelText))?.SetValue(control, attr.Label);
controlType.GetProperty(nameof(SettingsItem<object>.TooltipText))?.SetValue(control, attr.Description); controlType.GetProperty(nameof(SettingsItem<object>.TooltipText))?.SetValue(control, attr.Description);
@ -188,7 +188,7 @@ namespace osu.Game.Configuration
case IBindable bindable: case IBindable bindable:
var dropdownType = typeof(ModSettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]); var dropdownType = typeof(ModSettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]);
var dropdown = (Drawable)Activator.CreateInstance(dropdownType); var dropdown = (Drawable)Activator.CreateInstance(dropdownType)!;
dropdownType.GetProperty(nameof(SettingsDropdown<object>.LabelText))?.SetValue(dropdown, attr.Label); dropdownType.GetProperty(nameof(SettingsDropdown<object>.LabelText))?.SetValue(dropdown, attr.Label);
dropdownType.GetProperty(nameof(SettingsDropdown<object>.TooltipText))?.SetValue(dropdown, attr.Description); dropdownType.GetProperty(nameof(SettingsDropdown<object>.TooltipText))?.SetValue(dropdown, attr.Description);
@ -231,7 +231,7 @@ namespace osu.Game.Configuration
// An unknown (e.g. enum) generic type. // An unknown (e.g. enum) generic type.
var valueMethod = u.GetType().GetProperty(nameof(IBindable<int>.Value)); var valueMethod = u.GetType().GetProperty(nameof(IBindable<int>.Value));
Debug.Assert(valueMethod != null); Debug.Assert(valueMethod != null);
return valueMethod.GetValue(u); return valueMethod.GetValue(u)!;
default: default:
// fall back for non-bindable cases. // fall back for non-bindable cases.

View File

@ -79,16 +79,16 @@ namespace osu.Game.Database
switch (content) switch (content)
{ {
case StableContent.Beatmaps: case StableContent.Beatmaps:
return await new LegacyBeatmapImporter(beatmaps).GetAvailableCount(stableStorage); return await new LegacyBeatmapImporter(beatmaps).GetAvailableCount(stableStorage).ConfigureAwait(false);
case StableContent.Skins: case StableContent.Skins:
return await new LegacySkinImporter(skins).GetAvailableCount(stableStorage); return await new LegacySkinImporter(skins).GetAvailableCount(stableStorage).ConfigureAwait(false);
case StableContent.Collections: case StableContent.Collections:
return await new LegacyCollectionImporter(realmAccess).GetAvailableCount(stableStorage); return await new LegacyCollectionImporter(realmAccess).GetAvailableCount(stableStorage).ConfigureAwait(false);
case StableContent.Scores: case StableContent.Scores:
return await new LegacyScoreImporter(scores).GetAvailableCount(stableStorage); return await new LegacyScoreImporter(scores).GetAvailableCount(stableStorage).ConfigureAwait(false);
default: default:
throw new ArgumentException($"Only one {nameof(StableContent)} flag should be specified."); throw new ArgumentException($"Only one {nameof(StableContent)} flag should be specified.");

View File

@ -61,6 +61,6 @@ namespace osu.Game.Database
public override int GetHashCode() => HashCode.Combine(ID); public override int GetHashCode() => HashCode.Combine(ID);
public override string ToString() => PerformRead(i => i.ToString()); public override string? ToString() => PerformRead(i => i.ToString());
} }
} }

View File

@ -71,9 +71,9 @@ namespace osu.Game.Database
bool importSuccessful; bool importSuccessful;
if (originalModel != null) if (originalModel != null)
importSuccessful = (await importer.ImportAsUpdate(notification, new ImportTask(filename), originalModel)) != null; importSuccessful = (await importer.ImportAsUpdate(notification, new ImportTask(filename), originalModel).ConfigureAwait(false)) != null;
else else
importSuccessful = (await importer.Import(notification, new[] { new ImportTask(filename) })).Any(); importSuccessful = (await importer.Import(notification, new[] { new ImportTask(filename) }).ConfigureAwait(false)).Any();
// for now a failed import will be marked as a failed download for simplicity. // for now a failed import will be marked as a failed download for simplicity.
if (!importSuccessful) if (!importSuccessful)

View File

@ -481,7 +481,7 @@ namespace osu.Game.Database
// server, which we don't use. May want to report upstream or revisit in the future. // server, which we don't use. May want to report upstream or revisit in the future.
using (var realm = getRealmInstance()) using (var realm = getRealmInstance())
// ReSharper disable once AccessToDisposedClosure (WriteAsync should be marked as [InstantHandle]). // ReSharper disable once AccessToDisposedClosure (WriteAsync should be marked as [InstantHandle]).
await realm.WriteAsync(() => action(realm)); await realm.WriteAsync(() => action(realm)).ConfigureAwait(false);
pendingAsyncWrites.Signal(); pendingAsyncWrites.Signal();
}); });
@ -558,7 +558,7 @@ namespace osu.Game.Database
return new InvokeOnDisposal(() => model.PropertyChanged -= onPropertyChanged); return new InvokeOnDisposal(() => model.PropertyChanged -= onPropertyChanged);
void onPropertyChanged(object sender, PropertyChangedEventArgs args) void onPropertyChanged(object? sender, PropertyChangedEventArgs args)
{ {
if (args.PropertyName == propertyName) if (args.PropertyName == propertyName)
onChanged(propLookupCompiled(model)); onChanged(propLookupCompiled(model));

View File

@ -66,10 +66,10 @@ namespace osu.Game.Extensions
foreach (var (_, property) in component.GetSettingsSourceProperties()) foreach (var (_, property) in component.GetSettingsSourceProperties())
{ {
if (!info.Settings.TryGetValue(property.Name.ToSnakeCase(), out object settingValue)) if (!info.Settings.TryGetValue(property.Name.ToSnakeCase(), out object? settingValue))
continue; continue;
skinnable.CopyAdjustedSetting((IBindable)property.GetValue(component), settingValue); skinnable.CopyAdjustedSetting(((IBindable)property.GetValue(component)!), settingValue);
} }
} }

View File

@ -37,7 +37,7 @@ namespace osu.Game.Extensions
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
tcs.SetCanceled(); tcs.SetCanceled(cancellationToken);
} }
else else
{ {

View File

@ -36,8 +36,7 @@ namespace osu.Game.Graphics.Containers
/// <param name="easing">The easing type of the initial transform.</param> /// <param name="easing">The easing type of the initial transform.</param>
public void StartTracking(OsuLogo logo, double duration = 0, Easing easing = Easing.None) public void StartTracking(OsuLogo logo, double duration = 0, Easing easing = Easing.None)
{ {
if (logo == null) ArgumentNullException.ThrowIfNull(logo);
throw new ArgumentNullException(nameof(logo));
if (logo.IsTracking && Logo == null) if (logo.IsTracking && Logo == null)
throw new InvalidOperationException($"Cannot track an instance of {typeof(OsuLogo)} to multiple {typeof(LogoTrackingContainer)}s"); throw new InvalidOperationException($"Cannot track an instance of {typeof(OsuLogo)} to multiple {typeof(LogoTrackingContainer)}s");

View File

@ -0,0 +1,34 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Markdig;
using Markdig.Extensions.GenericAttributes;
using Markdig.Renderers;
using Markdig.Syntax;
namespace osu.Game.Graphics.Containers.Markdown.Extensions
{
/// <summary>
/// A variant of <see cref="Markdig.Extensions.GenericAttributes.GenericAttributesExtension"/>
/// which only handles generic attributes in the current markdown <see cref="Block"/> and ignores inline generic attributes.
/// </summary>
/// <remarks>
/// For rationale, see implementation of <see cref="Setup(Markdig.MarkdownPipelineBuilder)"/>.
/// </remarks>
public class BlockAttributeExtension : IMarkdownExtension
{
private readonly GenericAttributesExtension genericAttributesExtension = new GenericAttributesExtension();
public void Setup(MarkdownPipelineBuilder pipeline)
{
genericAttributesExtension.Setup(pipeline);
// GenericAttributesExtension registers a GenericAttributesParser in pipeline.InlineParsers.
// this conflicts with the CustomContainerExtension, leading to some custom containers (e.g. flags) not displaying.
// as a workaround, remove the inline parser here before it can do damage.
pipeline.InlineParsers.RemoveAll(parser => parser is GenericAttributesParser);
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) => genericAttributesExtension.Setup(pipeline, renderer);
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Markdig;
namespace osu.Game.Graphics.Containers.Markdown.Extensions
{
public static class OsuMarkdownExtensions
{
/// <summary>
/// Uses the block attributes extension.
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline.</returns>
public static MarkdownPipelineBuilder UseBlockAttributes(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<BlockAttributeExtension>();
return pipeline;
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Markdig.Extensions.Footnotes;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers.Markdown;
using osu.Framework.Graphics.Containers.Markdown.Footnotes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
namespace osu.Game.Graphics.Containers.Markdown.Footnotes
{
public partial class OsuMarkdownFootnote : MarkdownFootnote
{
public OsuMarkdownFootnote(Footnote footnote)
: base(footnote)
{
}
public override SpriteText CreateOrderMarker(int order) => CreateSpriteText().With(marker =>
{
marker.Text = LocalisableString.Format("{0}.", order);
});
public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(textFlow =>
{
textFlow.Margin = new MarginPadding { Left = 30 };
});
}
}

View File

@ -0,0 +1,62 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using Markdig.Extensions.Footnotes;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers.Markdown;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Testing;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Graphics.Containers.Markdown.Footnotes
{
public partial class OsuMarkdownFootnoteBacklink : OsuHoverContainer
{
private readonly FootnoteLink backlink;
private SpriteIcon spriteIcon = null!;
[Resolved]
private IMarkdownTextComponent parentTextComponent { get; set; } = null!;
protected override IEnumerable<Drawable> EffectTargets => spriteIcon.Yield();
public OsuMarkdownFootnoteBacklink(FootnoteLink backlink)
{
this.backlink = backlink;
}
[BackgroundDependencyLoader(true)]
private void load(OverlayColourProvider colourProvider, OsuMarkdownContainer markdownContainer, OverlayScrollContainer? scrollContainer)
{
float fontSize = parentTextComponent.CreateSpriteText().Font.Size;
Size = new Vector2(fontSize);
IdleColour = colourProvider.Light2;
HoverColour = colourProvider.Light1;
Add(spriteIcon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Left = 5 },
Size = new Vector2(fontSize / 2),
Icon = FontAwesome.Solid.ArrowUp,
});
if (scrollContainer != null)
{
Action = () =>
{
var footnoteLink = markdownContainer.ChildrenOfType<OsuMarkdownFootnoteLink>().Single(footnoteLink => footnoteLink.FootnoteLink.Index == backlink.Index);
scrollContainer.ScrollIntoView(footnoteLink);
};
}
}
}
}

View File

@ -0,0 +1,80 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using Markdig.Extensions.Footnotes;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers.Markdown;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Framework.Testing;
using osu.Game.Overlays;
namespace osu.Game.Graphics.Containers.Markdown.Footnotes
{
public partial class OsuMarkdownFootnoteLink : OsuHoverContainer, IHasCustomTooltip
{
public readonly FootnoteLink FootnoteLink;
private SpriteText spriteText = null!;
[Resolved]
private IMarkdownTextComponent parentTextComponent { get; set; } = null!;
[Resolved]
private OverlayColourProvider colourProvider { get; set; } = null!;
[Resolved]
private OsuMarkdownContainer markdownContainer { get; set; } = null!;
protected override IEnumerable<Drawable> EffectTargets => spriteText.Yield();
public OsuMarkdownFootnoteLink(FootnoteLink footnoteLink)
{
FootnoteLink = footnoteLink;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader(true)]
private void load(OsuMarkdownContainer markdownContainer, OverlayScrollContainer? scrollContainer)
{
IdleColour = colourProvider.Light2;
HoverColour = colourProvider.Light1;
spriteText = parentTextComponent.CreateSpriteText();
Add(spriteText.With(t =>
{
float baseSize = t.Font.Size;
t.Font = t.Font.With(size: baseSize * 0.58f);
t.Margin = new MarginPadding { Bottom = 0.33f * baseSize };
t.Text = LocalisableString.Format("[{0}]", FootnoteLink.Index);
}));
if (scrollContainer != null)
{
Action = () =>
{
var footnote = markdownContainer.ChildrenOfType<OsuMarkdownFootnote>().Single(footnote => footnote.Footnote.Label == FootnoteLink.Footnote.Label);
scrollContainer.ScrollIntoView(footnote);
};
}
}
public object TooltipContent
{
get
{
var span = FootnoteLink.Footnote.LastChild.Span;
return markdownContainer.Text.Substring(span.Start, span.Length);
}
}
public ITooltip GetCustomTooltip() => new OsuMarkdownFootnoteTooltip(colourProvider);
}
}

View File

@ -0,0 +1,76 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Markdig.Extensions.Footnotes;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Containers.Markdown;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Graphics.Containers.Markdown.Footnotes
{
public partial class OsuMarkdownFootnoteTooltip : CompositeDrawable, ITooltip
{
private readonly FootnoteMarkdownContainer markdownContainer;
[Cached]
private OverlayColourProvider colourProvider;
public OsuMarkdownFootnoteTooltip(OverlayColourProvider colourProvider)
{
this.colourProvider = colourProvider;
Masking = true;
Width = 200;
AutoSizeAxes = Axes.Y;
CornerRadius = 4;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background6
},
markdownContainer = new FootnoteMarkdownContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
DocumentMargin = new MarginPadding(),
DocumentPadding = new MarginPadding { Horizontal = 10, Vertical = 5 }
}
};
}
public void Move(Vector2 pos) => Position = pos;
public void SetContent(object content) => markdownContainer.SetContent((string)content);
private partial class FootnoteMarkdownContainer : OsuMarkdownContainer
{
private string? lastFootnote;
public void SetContent(string footnote)
{
if (footnote == lastFootnote)
return;
lastFootnote = Text = footnote;
}
public override MarkdownTextFlowContainer CreateTextFlow() => new FootnoteMarkdownTextFlowContainer();
}
private partial class FootnoteMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer
{
protected override void AddFootnoteBacklink(FootnoteLink footnoteBacklink)
{
// we don't want footnote backlinks to show up in tooltips.
}
}
}
}

View File

@ -4,41 +4,25 @@
#nullable disable #nullable disable
using Markdig; using Markdig;
using Markdig.Extensions.AutoLinks;
using Markdig.Extensions.CustomContainers;
using Markdig.Extensions.EmphasisExtras;
using Markdig.Extensions.Footnotes; using Markdig.Extensions.Footnotes;
using Markdig.Extensions.Tables; using Markdig.Extensions.Tables;
using Markdig.Extensions.Yaml; using Markdig.Extensions.Yaml;
using Markdig.Syntax; using Markdig.Syntax;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Containers.Markdown;
using osu.Framework.Graphics.Containers.Markdown.Footnotes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Containers.Markdown.Footnotes;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osuTK;
namespace osu.Game.Graphics.Containers.Markdown namespace osu.Game.Graphics.Containers.Markdown
{ {
[Cached]
public partial class OsuMarkdownContainer : MarkdownContainer public partial class OsuMarkdownContainer : MarkdownContainer
{ {
/// <summary>
/// Allows this markdown container to parse and link footnotes.
/// </summary>
/// <seealso cref="FootnoteExtension"/>
protected virtual bool Footnotes => false;
/// <summary>
/// Allows this markdown container to make URL text clickable.
/// </summary>
/// <seealso cref="AutoLinkExtension"/>
protected virtual bool Autolinks => false;
/// <summary>
/// Allows this markdown container to parse custom containers (used for flags and infoboxes).
/// </summary>
/// <seealso cref="CustomContainerExtension"/>
protected virtual bool CustomContainers => false;
public OsuMarkdownContainer() public OsuMarkdownContainer()
{ {
LineSpacing = 21; LineSpacing = 21;
@ -99,25 +83,17 @@ namespace osu.Game.Graphics.Containers.Markdown
return new OsuMarkdownUnorderedListItem(level); return new OsuMarkdownUnorderedListItem(level);
} }
// reference: https://github.com/ppy/osu-web/blob/05488a96b25b5a09f2d97c54c06dd2bae59d1dc8/app/Libraries/Markdown/OsuMarkdown.php#L301 protected override MarkdownFootnoteGroup CreateFootnoteGroup(FootnoteGroup footnoteGroup) => base.CreateFootnoteGroup(footnoteGroup).With(g => g.Spacing = new Vector2(5));
protected override MarkdownPipeline CreateBuilder()
{
var pipeline = new MarkdownPipelineBuilder()
.UseAutoIdentifiers()
.UsePipeTables()
.UseEmphasisExtras(EmphasisExtraOptions.Strikethrough)
.UseYamlFrontMatter();
if (Footnotes) protected override MarkdownFootnote CreateFootnote(Footnote footnote) => new OsuMarkdownFootnote(footnote);
pipeline = pipeline.UseFootnotes();
if (Autolinks) protected sealed override MarkdownPipeline CreateBuilder()
pipeline = pipeline.UseAutoLinks(); => Options.BuildPipeline();
if (CustomContainers) /// <summary>
pipeline.UseCustomContainers(); /// Creates a <see cref="OsuMarkdownContainerOptions"/> instance which is used to determine
/// which CommonMark/Markdig extensions should be enabled for this <see cref="OsuMarkdownContainer"/>.
return pipeline.Build(); /// </summary>
} protected virtual OsuMarkdownContainerOptions Options => new OsuMarkdownContainerOptions();
} }
} }

View File

@ -0,0 +1,71 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Markdig;
using Markdig.Extensions.AutoLinks;
using Markdig.Extensions.CustomContainers;
using Markdig.Extensions.EmphasisExtras;
using Markdig.Extensions.Footnotes;
using osu.Game.Graphics.Containers.Markdown.Extensions;
namespace osu.Game.Graphics.Containers.Markdown
{
/// <summary>
/// Groups options of customising the set of available extensions to <see cref="OsuMarkdownContainer"/> instances.
/// </summary>
public class OsuMarkdownContainerOptions
{
/// <summary>
/// Allows the <see cref="OsuMarkdownContainer"/> to parse and link footnotes.
/// </summary>
/// <seealso cref="FootnoteExtension"/>
public bool Footnotes { get; init; }
/// <summary>
/// Allows the <see cref="OsuMarkdownContainer"/> container to make URL text clickable.
/// </summary>
/// <seealso cref="AutoLinkExtension"/>
public bool Autolinks { get; init; }
/// <summary>
/// Allows the <see cref="OsuMarkdownContainer"/> to parse custom containers (used for flags and infoboxes).
/// </summary>
/// <seealso cref="CustomContainerExtension"/>
public bool CustomContainers { get; init; }
/// <summary>
/// Allows the <see cref="OsuMarkdownContainer"/> to parse custom attributes in block elements (used e.g. for custom anchor names in the wiki).
/// </summary>
/// <seealso cref="BlockAttributeExtension"/>
public bool BlockAttributes { get; init; }
/// <summary>
/// Returns a prepared <see cref="MarkdownPipeline"/> according to the options specified by the current <see cref="OsuMarkdownContainerOptions"/> instance.
/// </summary>
/// <remarks>
/// Compare: https://github.com/ppy/osu-web/blob/05488a96b25b5a09f2d97c54c06dd2bae59d1dc8/app/Libraries/Markdown/OsuMarkdown.php#L301
/// </remarks>
public MarkdownPipeline BuildPipeline()
{
var pipeline = new MarkdownPipelineBuilder()
.UseAutoIdentifiers()
.UsePipeTables()
.UseEmphasisExtras(EmphasisExtraOptions.Strikethrough)
.UseYamlFrontMatter();
if (Footnotes)
pipeline = pipeline.UseFootnotes();
if (Autolinks)
pipeline = pipeline.UseAutoLinks();
if (CustomContainers)
pipeline = pipeline.UseCustomContainers();
if (BlockAttributes)
pipeline = pipeline.UseBlockAttributes();
return pipeline.Build();
}
}
}

View File

@ -6,6 +6,7 @@
using System; using System;
using System.Linq; using System.Linq;
using Markdig.Extensions.CustomContainers; using Markdig.Extensions.CustomContainers;
using Markdig.Extensions.Footnotes;
using Markdig.Syntax.Inlines; using Markdig.Syntax.Inlines;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -13,6 +14,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Containers.Markdown;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Containers.Markdown.Footnotes;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Users; using osu.Game.Users;
using osu.Game.Users.Drawables; using osu.Game.Users.Drawables;
@ -36,6 +38,10 @@ namespace osu.Game.Graphics.Containers.Markdown
Text = codeInline.Content Text = codeInline.Content
}); });
protected override void AddFootnoteLink(FootnoteLink footnoteLink) => AddDrawable(new OsuMarkdownFootnoteLink(footnoteLink));
protected override void AddFootnoteBacklink(FootnoteLink footnoteBacklink) => AddDrawable(new OsuMarkdownFootnoteBacklink(footnoteBacklink));
protected override SpriteText CreateEmphasisedSpriteText(bool bold, bool italic) protected override SpriteText CreateEmphasisedSpriteText(bool bold, bool italic)
=> CreateSpriteText().With(t => t.Font = t.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic)); => CreateSpriteText().With(t => t.Font = t.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic));

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -46,5 +47,8 @@ namespace osu.Game.Graphics.Containers
AddInternal(content); AddInternal(content);
Add(CreateHoverSounds(sampleSet)); Add(CreateHoverSounds(sampleSet));
} }
protected override void ClearInternal(bool disposeChildren = true) =>
throw new InvalidOperationException($"Clearing {nameof(InternalChildren)} will cause critical failure. Use {nameof(Clear)} instead.");
} }
} }

View File

@ -114,8 +114,7 @@ namespace osu.Game.Graphics.UserInterface
get => current; get => current;
set set
{ {
if (value == null) ArgumentNullException.ThrowIfNull(value);
throw new ArgumentNullException(nameof(value));
current.UnbindBindings(); current.UnbindBindings();
current.BindTo(value); current.BindTo(value);

View File

@ -277,7 +277,7 @@ namespace osu.Game.Graphics.UserInterface
{ {
var samples = sampleMap[feedbackSampleType]; var samples = sampleMap[feedbackSampleType];
if (samples == null || samples.Length == 0) if (samples.Length == 0)
return null; return null;
return samples[RNG.Next(0, samples.Length)]?.GetChannel(); return samples[RNG.Next(0, samples.Length)]?.GetChannel();

View File

@ -23,8 +23,7 @@ namespace osu.Game.IO.FileAbstraction
public void CloseStream(Stream stream) public void CloseStream(Stream stream)
{ {
if (stream == null) ArgumentNullException.ThrowIfNull(stream);
throw new ArgumentNullException(nameof(stream));
stream.Close(); stream.Close();
} }

View File

@ -63,7 +63,7 @@ namespace osu.Game.IO.Serialization.Converters
throw new JsonException("Expected $type token."); throw new JsonException("Expected $type token.");
string typeName = lookupTable[(int)tok["$type"]]; string typeName = lookupTable[(int)tok["$type"]];
var instance = (T)Activator.CreateInstance(Type.GetType(typeName).AsNonNull()); var instance = (T)Activator.CreateInstance(Type.GetType(typeName).AsNonNull())!;
serializer.Populate(itemReader, instance); serializer.Populate(itemReader, instance);
list.Add(instance); list.Add(instance);

View File

@ -27,7 +27,7 @@ namespace osu.Game.Models
public bool IsBot => false; public bool IsBot => false;
public bool Equals(RealmUser other) public bool Equals(RealmUser? other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true; if (ReferenceEquals(this, other)) return true;

View File

@ -39,7 +39,7 @@ namespace osu.Game.Online.API
foreach (var (_, property) in mod.GetSettingsSourceProperties()) foreach (var (_, property) in mod.GetSettingsSourceProperties())
{ {
var bindable = (IBindable)property.GetValue(mod); var bindable = (IBindable)property.GetValue(mod)!;
if (!bindable.IsDefault) if (!bindable.IsDefault)
Settings.Add(property.Name.ToSnakeCase(), bindable.GetUnderlyingSettingValue()); Settings.Add(property.Name.ToSnakeCase(), bindable.GetUnderlyingSettingValue());
@ -60,16 +60,16 @@ namespace osu.Game.Online.API
{ {
foreach (var (_, property) in resultMod.GetSettingsSourceProperties()) foreach (var (_, property) in resultMod.GetSettingsSourceProperties())
{ {
if (!Settings.TryGetValue(property.Name.ToSnakeCase(), out object settingValue)) if (!Settings.TryGetValue(property.Name.ToSnakeCase(), out object? settingValue))
continue; continue;
try try
{ {
resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod), settingValue); resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod)!, settingValue);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Log($"Failed to copy mod setting value '{settingValue ?? "null"}' to \"{property.Name}\": {ex.Message}"); Logger.Log($"Failed to copy mod setting value '{settingValue}' to \"{property.Name}\": {ex.Message}");
} }
} }
} }
@ -79,7 +79,7 @@ namespace osu.Game.Online.API
public bool ShouldSerializeSettings() => Settings.Count > 0; public bool ShouldSerializeSettings() => Settings.Count > 0;
public bool Equals(APIMod other) public bool Equals(APIMod? other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true; if (ReferenceEquals(this, other)) return true;

Some files were not shown because too many files have changed in this diff Show More