From 3b600f0a7b2dda8fa17ba85ee1563526bad44ece Mon Sep 17 00:00:00 2001
From: Roman Kapustin <TocoToucanMS@gmail.com>
Date: Fri, 20 Nov 2020 10:45:19 +0300
Subject: [PATCH 01/18] Target net5.0 instead of netcoreapp3

---
 global.json                                                     | 2 +-
 osu.Desktop/osu.Desktop.csproj                                  | 2 +-
 osu.Game.Benchmarks/osu.Game.Benchmarks.csproj                  | 2 +-
 .../osu.Game.Rulesets.Catch.Tests.csproj                        | 2 +-
 .../osu.Game.Rulesets.Mania.Tests.csproj                        | 2 +-
 osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj  | 2 +-
 .../osu.Game.Rulesets.Taiko.Tests.csproj                        | 2 +-
 osu.Game.Tests/osu.Game.Tests.csproj                            | 2 +-
 osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj      | 2 +-
 9 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/global.json b/global.json
index 10b61047ac..2cb4c02970 100644
--- a/global.json
+++ b/global.json
@@ -2,7 +2,7 @@
   "sdk": {
     "allowPrerelease": false,
     "rollForward": "minor",
-    "version": "3.1.100"
+    "version": "5.0.100"
   },
   "msbuild-sdks": {
     "Microsoft.Build.Traversal": "2.2.3"
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 62e8f7c518..7f5154f456 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup Label="Project">
-    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <TargetFramework>net5.0</TargetFramework>
     <OutputType>WinExe</OutputType>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>
diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
index ff26f4afaa..7805bfcefc 100644
--- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
+++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <TargetFramework>net5.0</TargetFramework>
     <OutputType>Exe</OutputType>
     <IsPackable>false</IsPackable>
   </PropertyGroup>
diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
index dfe3bf8af4..a51b9830be 100644
--- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
+++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
@@ -9,7 +9,7 @@
   </ItemGroup>
   <PropertyGroup Label="Project">
     <OutputType>WinExe</OutputType>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <TargetFramework>net5.0</TargetFramework>
   </PropertyGroup>
   <ItemGroup Label="Project References">
     <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
index 892f27d27f..d314671bce 100644
--- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
+++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
@@ -9,7 +9,7 @@
   </ItemGroup>
   <PropertyGroup Label="Project">
     <OutputType>WinExe</OutputType>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <TargetFramework>net5.0</TargetFramework>
   </PropertyGroup>
   <ItemGroup Label="Project References">
     <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
index 3639c3616f..b0799bd3f5 100644
--- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
+++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
@@ -9,7 +9,7 @@
   </ItemGroup>
   <PropertyGroup Label="Project">
     <OutputType>WinExe</OutputType>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <TargetFramework>net5.0</TargetFramework>
   </PropertyGroup>
   <ItemGroup Label="Project References">
     <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
index b59f3a4344..d3dbba4bfc 100644
--- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
@@ -9,7 +9,7 @@
   </ItemGroup>
   <PropertyGroup Label="Project">
     <OutputType>WinExe</OutputType>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <TargetFramework>net5.0</TargetFramework>
   </PropertyGroup>
   <ItemGroup Label="Project References">
     <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index c692bcd5e4..34de54411b 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -10,7 +10,7 @@
   </ItemGroup>
   <PropertyGroup Label="Project">
     <OutputType>WinExe</OutputType>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <TargetFramework>net5.0</TargetFramework>
   </PropertyGroup>
   <ItemGroup Label="Project References">
     <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
index 5d55196dcf..d820794980 100644
--- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
+++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
@@ -11,7 +11,7 @@
   </ItemGroup>
   <PropertyGroup Label="Project">
     <OutputType>WinExe</OutputType>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <TargetFramework>net5.0</TargetFramework>
   </PropertyGroup>
   <ItemGroup Label="Project References">
     <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />

From ca0e1c8cee1ffd03b2bb5fe773741012b90bb654 Mon Sep 17 00:00:00 2001
From: Roman Kapustin <TocoToucanMS@gmail.com>
Date: Fri, 20 Nov 2020 10:47:57 +0300
Subject: [PATCH 02/18] Update NuGet packages

---
 osu.Desktop/osu.Desktop.csproj                 | 4 ++--
 osu.Game.Tournament/osu.Game.Tournament.csproj | 2 +-
 osu.Game/osu.Game.csproj                       | 6 +++---
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 7f5154f456..2052c4bc25 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -24,11 +24,11 @@
     <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
   </ItemGroup>
   <ItemGroup Label="Package References">
-    <PackageReference Include="System.IO.Packaging" Version="4.7.0" />
+    <PackageReference Include="System.IO.Packaging" Version="5.0.0" />
     <PackageReference Include="ppy.squirrel.windows" Version="1.9.0.4" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
-    <PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
+    <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
     <PackageReference Include="DiscordRichPresence" Version="1.0.150" />
     <!-- .NET 3.1 SDK seems to cause issues with a runtime specification. This will likely be resolved in .NET 5. -->
     <PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
diff --git a/osu.Game.Tournament/osu.Game.Tournament.csproj b/osu.Game.Tournament/osu.Game.Tournament.csproj
index 9cce40c9d3..b049542bb0 100644
--- a/osu.Game.Tournament/osu.Game.Tournament.csproj
+++ b/osu.Game.Tournament/osu.Game.Tournament.csproj
@@ -9,6 +9,6 @@
     <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
   </ItemGroup>
   <ItemGroup Label="Package References">
-    <PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
+    <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 54f3fcede6..1670bf5b11 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -21,8 +21,8 @@
     <PackageReference Include="Dapper" Version="2.0.35" />
     <PackageReference Include="DiffPlex" Version="1.6.3" />
     <PackageReference Include="Humanizer" Version="2.8.26" />
-    <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.9" />
-    <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="3.1.9" />
+    <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="5.0.0" />
+    <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="5.0.0" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
@@ -31,6 +31,6 @@
     <PackageReference Include="Sentry" Version="2.1.6" />
     <PackageReference Include="SharpCompress" Version="0.26.0" />
     <PackageReference Include="NUnit" Version="3.12.0" />
-    <PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
+    <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
   </ItemGroup>
 </Project>

From f562a7ea0df68baee20551108bb4bd9fd13d5747 Mon Sep 17 00:00:00 2001
From: Roman Kapustin <TocoToucanMS@gmail.com>
Date: Fri, 20 Nov 2020 11:52:17 +0300
Subject: [PATCH 03/18] Fix FileNotFoundException on startup

---
 osu.Desktop/osu.Desktop.csproj | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 2052c4bc25..53b9cdcf92 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -24,6 +24,8 @@
     <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
   </ItemGroup>
   <ItemGroup Label="Package References">
+	  <!-- WorkaroundWorkaround for a System.IO.FileNotFoundException on startup. Required because of old EF Core version. -->
+	  <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
     <PackageReference Include="System.IO.Packaging" Version="5.0.0" />
     <PackageReference Include="ppy.squirrel.windows" Version="1.9.0.4" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />

From 044622036cbc94adae124f68ec5fbff18befb2b4 Mon Sep 17 00:00:00 2001
From: Roman Kapustin <TocoToucanMS@gmail.com>
Date: Fri, 20 Nov 2020 11:53:17 +0300
Subject: [PATCH 04/18] Disable CA1416

---
 osu.Desktop/OsuGameDesktop.cs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index 0feab9a717..e2a06d7877 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -58,8 +58,10 @@ namespace osu.Desktop
 
             try
             {
+#pragma warning disable CA1416 // Validate platform compatibility
                 using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
                     stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString()?.Split('"')[1].Replace("osu!.exe", "");
+#pragma warning restore CA1416 // Validate platform compatibility
 
                 if (checkExists(stableInstallPath))
                     return stableInstallPath;

From 1a676ef0d82c4d4cfd0d7fbac2338da3a763272f Mon Sep 17 00:00:00 2001
From: Roman Kapustin <TocoToucanMS@gmail.com>
Date: Fri, 20 Nov 2020 12:06:08 +0300
Subject: [PATCH 05/18] Resolve CA1416 properly using new API

---
 osu.Desktop/OsuGameDesktop.cs | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index e2a06d7877..f9c932b260 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -5,6 +5,7 @@ using System;
 using System.IO;
 using System.Linq;
 using System.Reflection;
+using System.Runtime.Versioning;
 using System.Threading.Tasks;
 using Microsoft.Win32;
 using osu.Desktop.Overlays;
@@ -56,19 +57,12 @@ namespace osu.Desktop
 
             string stableInstallPath;
 
-            try
+            if (OperatingSystem.IsWindows())
             {
-#pragma warning disable CA1416 // Validate platform compatibility
-                using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
-                    stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString()?.Split('"')[1].Replace("osu!.exe", "");
-#pragma warning restore CA1416 // Validate platform compatibility
-
+                stableInstallPath = getStableInstallPathFromRegistry();
                 if (checkExists(stableInstallPath))
                     return stableInstallPath;
             }
-            catch
-            {
-            }
 
             stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
             if (checkExists(stableInstallPath))
@@ -81,6 +75,13 @@ namespace osu.Desktop
             return null;
         }
 
+        [SupportedOSPlatform("windows")]
+        private string getStableInstallPathFromRegistry()
+        {
+            using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
+                return key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString()?.Split('"')[1].Replace("osu!.exe", "");
+        }
+
         protected override UpdateManager CreateUpdateManager()
         {
             switch (RuntimeInfo.OS)

From 743541649706d9b9165197421860dfd0b3cf264d Mon Sep 17 00:00:00 2001
From: Roman Kapustin <TocoToucanMS@gmail.com>
Date: Fri, 20 Nov 2020 14:13:16 +0300
Subject: [PATCH 06/18] Workaround FileNotFoundException in a test projects

---
 osu.Desktop/osu.Desktop.csproj | 2 --
 osu.Game/osu.Game.csproj       | 2 ++
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 53b9cdcf92..2052c4bc25 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -24,8 +24,6 @@
     <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
   </ItemGroup>
   <ItemGroup Label="Package References">
-	  <!-- WorkaroundWorkaround for a System.IO.FileNotFoundException on startup. Required because of old EF Core version. -->
-	  <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
     <PackageReference Include="System.IO.Packaging" Version="5.0.0" />
     <PackageReference Include="ppy.squirrel.windows" Version="1.9.0.4" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 1670bf5b11..1c6139b519 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -18,6 +18,8 @@
     </None>
   </ItemGroup>
   <ItemGroup Label="Package References">
+    <!-- WorkaroundWorkaround for a System.IO.FileNotFoundException on startup. Required because of old EF Core version. -->
+    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
     <PackageReference Include="Dapper" Version="2.0.35" />
     <PackageReference Include="DiffPlex" Version="1.6.3" />
     <PackageReference Include="Humanizer" Version="2.8.26" />

From 1feda1152da0b15835af3ce83bed31faf5fbed65 Mon Sep 17 00:00:00 2001
From: Roman Kapustin <TocoToucanMS@gmail.com>
Date: Sat, 21 Nov 2020 02:06:20 +0300
Subject: [PATCH 07/18] Fix InspectCode warnings

---
 osu.Desktop/OsuGameDesktop.cs                                | 2 +-
 osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs | 5 +++++
 osu.Game.Tournament/IPC/FileBasedIPC.cs                      | 2 +-
 3 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index f9c932b260..dbbf6d048b 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -79,7 +79,7 @@ namespace osu.Desktop
         private string getStableInstallPathFromRegistry()
         {
             using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
-                return key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString()?.Split('"')[1].Replace("osu!.exe", "");
+                return key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", "");
         }
 
         protected override UpdateManager CreateUpdateManager()
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
index 35473ee76c..58992366ff 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.Specialized;
+using System.Diagnostics;
 using System.Linq;
 using NUnit.Framework;
 using osu.Framework.Allocation;
@@ -73,6 +74,8 @@ namespace osu.Game.Tests.Visual.Gameplay
                 switch (args.Action)
                 {
                     case NotifyCollectionChangedAction.Add:
+                        Debug.Assert(args.NewItems != null, "args.NewItems != null");
+
                         foreach (int user in args.NewItems)
                         {
                             if (user == api.LocalUser.Value.Id)
@@ -82,6 +85,8 @@ namespace osu.Game.Tests.Visual.Gameplay
                         break;
 
                     case NotifyCollectionChangedAction.Remove:
+                        Debug.Assert(args.OldItems != null, "args.OldItems != null");
+
                         foreach (int user in args.OldItems)
                         {
                             if (user == api.LocalUser.Value.Id)
diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs
index 999ce61ac8..99147951b2 100644
--- a/osu.Game.Tournament/IPC/FileBasedIPC.cs
+++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs
@@ -243,7 +243,7 @@ namespace osu.Game.Tournament.IPC
                 string stableInstallPath;
 
                 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", "");
 
                 if (ipcFileExistsInDirectory(stableInstallPath))
                     return stableInstallPath;

From 4cccde9007cdb1646f041da71d77184c51f490d2 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 15 Jan 2021 13:20:46 +0900
Subject: [PATCH 08/18] Update framework

---
 osu.Android.props        | 2 +-
 osu.Game/osu.Game.csproj | 2 +-
 osu.iOS.props            | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/osu.Android.props b/osu.Android.props
index 492c88c7e4..919d83f8db 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
   </ItemGroup>
   <ItemGroup>
     <PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
-    <PackageReference Include="ppy.osu.Framework.Android" Version="2021.106.0" />
+    <PackageReference Include="ppy.osu.Framework.Android" Version="2021.115.0" />
   </ItemGroup>
 </Project>
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index ff016199d3..ac014f2964 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -28,7 +28,7 @@
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
-    <PackageReference Include="ppy.osu.Framework" Version="2021.106.0" />
+    <PackageReference Include="ppy.osu.Framework" Version="2021.115.0" />
     <PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
     <PackageReference Include="Sentry" Version="2.1.8" />
     <PackageReference Include="SharpCompress" Version="0.26.0" />
diff --git a/osu.iOS.props b/osu.iOS.props
index 93be3645ee..799042626b 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
     <Reference Include="System.Net.Http" />
   </ItemGroup>
   <ItemGroup Label="Package References">
-    <PackageReference Include="ppy.osu.Framework.iOS" Version="2021.106.0" />
+    <PackageReference Include="ppy.osu.Framework.iOS" Version="2021.115.0" />
     <PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
   </ItemGroup>
   <!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
@@ -88,7 +88,7 @@
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
-    <PackageReference Include="ppy.osu.Framework" Version="2021.106.0" />
+    <PackageReference Include="ppy.osu.Framework" Version="2021.115.0" />
     <PackageReference Include="SharpCompress" Version="0.26.0" />
     <PackageReference Include="NUnit" Version="3.12.0" />
     <PackageReference Include="SharpRaven" Version="2.4.0" />

From 86f66727de47175b8045523c56f719a8051041a3 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 15 Jan 2021 13:29:37 +0900
Subject: [PATCH 09/18] Update KeyBinding usages in line with interface changes

---
 osu.Game/Database/OsuDbContext.cs                       | 2 ++
 osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs | 2 +-
 osu.Game/Input/Bindings/GlobalActionContainer.cs        | 2 +-
 osu.Game/Input/KeyBindingStore.cs                       | 2 +-
 4 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs
index 2ae07b3cf8..2aae62edea 100644
--- a/osu.Game/Database/OsuDbContext.cs
+++ b/osu.Game/Database/OsuDbContext.cs
@@ -135,6 +135,8 @@ namespace osu.Game.Database
 
             modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => new { b.RulesetID, b.Variant });
             modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction);
+            modelBuilder.Entity<DatabasedKeyBinding>().Ignore(b => b.KeyCombination);
+            modelBuilder.Entity<DatabasedKeyBinding>().Ignore(b => b.Action);
 
             modelBuilder.Entity<DatabasedSetting>().HasIndex(b => new { b.RulesetID, b.Variant });
 
diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs
index 94edc33099..d12eaa10f6 100644
--- a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs
+++ b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Input.Bindings
 
         private KeyBindingStore store;
 
-        public override IEnumerable<KeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0);
+        public override IEnumerable<IKeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0);
 
         /// <summary>
         /// Create a new instance.
diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs
index b8c2fa201f..8ccdb9249e 100644
--- a/osu.Game/Input/Bindings/GlobalActionContainer.cs
+++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Input.Bindings
                 handler = game;
         }
 
-        public override IEnumerable<KeyBinding> DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings).Concat(AudioControlKeyBindings).Concat(EditorKeyBindings);
+        public override IEnumerable<IKeyBinding> DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings).Concat(AudioControlKeyBindings).Concat(EditorKeyBindings);
 
         public IEnumerable<KeyBinding> GlobalKeyBindings => new[]
         {
diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs
index bc73d74d74..b25b00eb84 100644
--- a/osu.Game/Input/KeyBindingStore.cs
+++ b/osu.Game/Input/KeyBindingStore.cs
@@ -49,7 +49,7 @@ namespace osu.Game.Input
             }
         }
 
-        private void insertDefaults(IEnumerable<KeyBinding> defaults, int? rulesetId = null, int? variant = null)
+        private void insertDefaults(IEnumerable<IKeyBinding> defaults, int? rulesetId = null, int? variant = null)
         {
             using (var usage = ContextFactory.GetForWrite())
             {

From 51255033e24c2afbbac85573ef5e7edf610eb920 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 15 Jan 2021 13:41:35 +0900
Subject: [PATCH 10/18] Update some missed usages of KeyBindingContainer in
 tests

---
 osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs    | 2 +-
 osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs   | 2 +-
 osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs
index b2ad7ca5b4..802dbf2021 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs
@@ -244,7 +244,7 @@ namespace osu.Game.Tests.Visual.Gameplay
 
             internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
             {
-                public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
+                public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[]
                 {
                     new KeyBinding(InputKey.MouseLeft, TestAction.Down),
                 };
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs
index 40c4214749..6e338b7202 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs
@@ -179,7 +179,7 @@ namespace osu.Game.Tests.Visual.Gameplay
 
         internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
         {
-            public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
+            public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[]
             {
                 new KeyBinding(InputKey.MouseLeft, TestAction.Down),
             };
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
index b9ff95cb29..8278ff9adf 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
@@ -303,7 +303,7 @@ namespace osu.Game.Tests.Visual.Gameplay
 
             internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
             {
-                public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
+                public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[]
                 {
                     new KeyBinding(InputKey.MouseLeft, TestAction.Down),
                 };

From f42a6270bbd750e2539ddad3ff97c0adb29ee521 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 15 Jan 2021 14:53:55 +0900
Subject: [PATCH 11/18] Update framework (again) for native libs fix

---
 osu.Android.props        | 2 +-
 osu.Game/osu.Game.csproj | 2 +-
 osu.iOS.props            | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/osu.Android.props b/osu.Android.props
index 919d83f8db..db5c933c41 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
   </ItemGroup>
   <ItemGroup>
     <PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
-    <PackageReference Include="ppy.osu.Framework.Android" Version="2021.115.0" />
+    <PackageReference Include="ppy.osu.Framework.Android" Version="2021.115.1" />
   </ItemGroup>
 </Project>
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index ac014f2964..5e9e90c78f 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -28,7 +28,7 @@
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
-    <PackageReference Include="ppy.osu.Framework" Version="2021.115.0" />
+    <PackageReference Include="ppy.osu.Framework" Version="2021.115.1" />
     <PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
     <PackageReference Include="Sentry" Version="2.1.8" />
     <PackageReference Include="SharpCompress" Version="0.26.0" />
diff --git a/osu.iOS.props b/osu.iOS.props
index 799042626b..225cf981f2 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
     <Reference Include="System.Net.Http" />
   </ItemGroup>
   <ItemGroup Label="Package References">
-    <PackageReference Include="ppy.osu.Framework.iOS" Version="2021.115.0" />
+    <PackageReference Include="ppy.osu.Framework.iOS" Version="2021.115.1" />
     <PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
   </ItemGroup>
   <!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
@@ -88,7 +88,7 @@
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
-    <PackageReference Include="ppy.osu.Framework" Version="2021.115.0" />
+    <PackageReference Include="ppy.osu.Framework" Version="2021.115.1" />
     <PackageReference Include="SharpCompress" Version="0.26.0" />
     <PackageReference Include="NUnit" Version="3.12.0" />
     <PackageReference Include="SharpRaven" Version="2.4.0" />

From e0a4a666c8e5fa8224fae065d3597b49fa747103 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 15 Jan 2021 15:01:16 +0900
Subject: [PATCH 12/18] Remove unnecessary workaround (mentioned package is
 pinned by SignalR to a working version)

---
 osu.Game/osu.Game.csproj | 2 --
 1 file changed, 2 deletions(-)

diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 5e9e90c78f..301ee39a61 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -18,8 +18,6 @@
     </None>
   </ItemGroup>
   <ItemGroup Label="Package References">
-    <!-- WorkaroundWorkaround for a System.IO.FileNotFoundException on startup. Required because of old EF Core version. -->
-    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
     <PackageReference Include="Dapper" Version="2.0.78" />
     <PackageReference Include="DiffPlex" Version="1.6.3" />
     <PackageReference Include="Humanizer" Version="2.8.26" />

From 7c612ec5561225b2a618bf11915aa693e20e7cf6 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 15 Jan 2021 15:11:03 +0900
Subject: [PATCH 13/18] Remove global.json

---
 global.json | 10 ----------
 osu.sln     |  1 -
 2 files changed, 11 deletions(-)
 delete mode 100644 global.json

diff --git a/global.json b/global.json
deleted file mode 100644
index f5aaffcd3d..0000000000
--- a/global.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "sdk": {
-    "allowPrerelease": false,
-    "rollForward": "minor",
-    "version": "5.0.100"
-  },
-  "msbuild-sdks": {
-    "Microsoft.Build.Traversal": "3.0.2"
-  }
-}
\ No newline at end of file
diff --git a/osu.sln b/osu.sln
index 1d64f6ff10..c9453359b1 100644
--- a/osu.sln
+++ b/osu.sln
@@ -57,7 +57,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 	ProjectSection(SolutionItems) = preProject
 		.editorconfig = .editorconfig
 		Directory.Build.props = Directory.Build.props
-		global.json = global.json
 		osu.Android.props = osu.Android.props
 		osu.iOS.props = osu.iOS.props
 		CodeAnalysis\osu.ruleset = CodeAnalysis\osu.ruleset

From 3f8834030416aeb80f5746a37999a83c16064d86 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 15 Jan 2021 15:17:38 +0900
Subject: [PATCH 14/18] Restore previous exception handling flow for stable
 path lookup

---
 osu.Desktop/OsuGameDesktop.cs | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index 55e42b160e..d1515acafa 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -59,10 +59,14 @@ namespace osu.Desktop
 
             if (OperatingSystem.IsWindows())
             {
-                stableInstallPath = getStableInstallPathFromRegistry();
+                try
+                {
+                    stableInstallPath = getStableInstallPathFromRegistry();
 
-                if (checkExists(stableInstallPath))
-                    return stableInstallPath;
+                    if (!string.IsNullOrEmpty(stableInstallPath) && checkExists(stableInstallPath))
+                        return stableInstallPath;
+                }
+                catch { }
             }
 
             stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");

From d023ad8ad1a03b500f13b90ca5d1d24a75e182ea Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Fri, 15 Jan 2021 15:18:29 +0900
Subject: [PATCH 15/18] Remove assert messages

---
 osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
index 8278ff9adf..35b3bfc1f8 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
@@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual.Gameplay
                 switch (args.Action)
                 {
                     case NotifyCollectionChangedAction.Add:
-                        Debug.Assert(args.NewItems != null, "args.NewItems != null");
+                        Debug.Assert(args.NewItems != null);
 
                         foreach (int user in args.NewItems)
                         {
@@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.Gameplay
                         break;
 
                     case NotifyCollectionChangedAction.Remove:
-                        Debug.Assert(args.OldItems != null, "args.OldItems != null");
+                        Debug.Assert(args.OldItems != null);
 
                         foreach (int user in args.OldItems)
                         {

From b8c85ef017184b8672adb80401375bd3c8c8e224 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Tue, 12 Jan 2021 16:03:12 +0900
Subject: [PATCH 16/18] Revert polling changes to fix participant list display

It turns out this polling was necessary to get extra data that isn't
included in the main listing request. It was removed deemed useless, and
in order to fix the order of rooms changing when selecting a room.
Weirdly, I can't reproduce this happening any more, and on close
inspection of the code can't see how it could happen in the first place.

For now, let's revert this change and iterate from there, if/when the
same issue arises again.

I've discussed avoiding this second poll by potentially including more
data (just `user_id`s?) in the main listing request, but not 100% sure
on this - even if the returned data is minimal it's an extra join
server-side, which could cause performance issues for large numbers of
rooms.
---
 .../TestSceneMultiplayerRoomManager.cs        |  1 +
 .../OnlinePlay/Multiplayer/Multiplayer.cs     |  5 +++-
 .../Multiplayer/MultiplayerRoomManager.cs     | 28 ++++++++++++++++++-
 3 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs
index 80d1acd145..7a3845cbf3 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs
@@ -143,6 +143,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
                 RoomManager =
                 {
                     TimeBetweenListingPolls = { Value = 1 },
+                    TimeBetweenSelectionPolls = { Value = 1 }
                 }
             };
 
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs
index 310617a0bc..76f5c74433 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs
@@ -33,6 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
             if (!this.IsCurrentScreen())
             {
                 multiplayerRoomManager.TimeBetweenListingPolls.Value = 0;
+                multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0;
             }
             else
             {
@@ -40,16 +41,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
                 {
                     case LoungeSubScreen _:
                         multiplayerRoomManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000;
+                        multiplayerRoomManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000;
                         break;
 
                     // Don't poll inside the match or anywhere else.
                     default:
                         multiplayerRoomManager.TimeBetweenListingPolls.Value = 0;
+                        multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0;
                         break;
                 }
             }
 
-            Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value})");
+            Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})");
         }
 
         protected override Room CreateNewRoom()
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
index 5c327266a3..3cb263298f 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
         private StatefulMultiplayerClient multiplayerClient { get; set; }
 
         public readonly Bindable<double> TimeBetweenListingPolls = new Bindable<double>();
-
+        public readonly Bindable<double> TimeBetweenSelectionPolls = new Bindable<double>();
         private readonly IBindable<bool> isConnected = new Bindable<bool>();
         private readonly Bindable<bool> allowPolling = new Bindable<bool>();
 
@@ -119,6 +119,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
                 TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls },
                 AllowPolling = { BindTarget = allowPolling }
             },
+            new MultiplayerSelectionPollingComponent
+            {
+                TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls },
+                AllowPolling = { BindTarget = allowPolling }
+            }
         };
 
         private class MultiplayerListingPollingComponent : ListingPollingComponent
@@ -141,5 +146,26 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
 
             protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll();
         }
+
+        private class MultiplayerSelectionPollingComponent : SelectionPollingComponent
+        {
+            public readonly IBindable<bool> AllowPolling = new Bindable<bool>();
+
+            protected override void LoadComplete()
+            {
+                base.LoadComplete();
+
+                AllowPolling.BindValueChanged(allowPolling =>
+                {
+                    if (!allowPolling.NewValue)
+                        return;
+
+                    if (IsLoaded)
+                        PollImmediately();
+                });
+            }
+
+            protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll();
+        }
     }
 }

From ede5abdba4eb0f88806c38790c7a622546f70af9 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Tue, 12 Jan 2021 18:05:29 +0900
Subject: [PATCH 17/18] Fix unstable multiplayer room ordering when selection
 is made

---
 .../OnlinePlay/Components/SelectionPollingComponent.cs       | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
index 0eec155060..dcf3c94b76 100644
--- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
@@ -2,6 +2,7 @@
 // See the LICENCE file in the repository root for full licence text.
 
 using System.Collections.Generic;
+using System.Linq;
 using System.Threading.Tasks;
 using osu.Framework.Allocation;
 using osu.Framework.Bindables;
@@ -47,9 +48,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
 
             pollReq.Success += result =>
             {
-                var rooms = new List<Room>(roomManager.Rooms);
+                // existing rooms need to be ordered by their position because the received of NotifyRoomsReceives expects to be able to sort them based on this order.
+                var rooms = new List<Room>(roomManager.Rooms.OrderBy(r => r.Position.Value));
 
                 int index = rooms.FindIndex(r => r.RoomID.Value == result.RoomID.Value);
+
                 if (index < 0)
                     return;
 

From 2b578e97e532fc0bb215566b8bd34df25d1d4625 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Thu, 14 Jan 2021 18:25:32 +0900
Subject: [PATCH 18/18] Fix deadlock scenario when calculating fallback
 difficulty

The previous code would run a calcaulation for the beatmap's own ruleset
if the current one failed. While this does make sense, with the current
way we use this component (and the implementation flow) it is quite unsafe.

The to the call on `.Result` in the `catch` block, this would 100%
deadlock due to the thread concurrency of the `ThreadedTaskScheduler`
being 1. Even if the nested run could be run inline (it should be), the
task scheduler won't even get to the point of checking whether this is
feasible due to it being saturated by the already running task.

I'm not sure if we still need this fallback lookup logic. After removing
it, it's feasible that 0 stars will be returned during the scenario that
previously caused a deadlock, but I don't necessarily think this is
incorrect. There may be another reason for this needing to exist which
I'm not aware of (diffcalc?) but if that's the case we may want to move
the try-catch handling to the point of usage.

To reproduce the deadlock scenario with 100% success (the repro
instructions in the linked issue aren't that simple and require some
patience and good timing), the main portion of the lookup can be changed
to randomly trigger a nested lookup:

```
if (RNG.NextSingle() > 0.5f)
    return GetAsync(new
DifficultyCacheLookup(key.Beatmap, key.Beatmap.Ruleset,
key.OrderedMods)).Result;
else
    return new StarDifficulty(attributes);
```

After switching beatmap once or twice, pausing debug and viewing the
state of threads should show exactly what is going on.
---
 osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
index 3b58062add..37d262abe5 100644
--- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
+++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
@@ -260,17 +260,10 @@ namespace osu.Game.Beatmaps
             }
             catch (BeatmapInvalidForRulesetException e)
             {
-                // Conversion has failed for the given ruleset, so return the difficulty in the beatmap's default ruleset.
-
-                // Ensure the beatmap's default ruleset isn't the one already being converted to.
-                // This shouldn't happen as it means something went seriously wrong, but if it does an endless loop should be avoided.
                 if (rulesetInfo.Equals(beatmapInfo.Ruleset))
-                {
                     Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineBeatmapID} to the beatmap's default ruleset ({beatmapInfo.Ruleset}).");
-                    return new StarDifficulty();
-                }
 
-                return GetAsync(new DifficultyCacheLookup(key.Beatmap, key.Beatmap.Ruleset, key.OrderedMods)).Result;
+                return new StarDifficulty();
             }
             catch
             {