From 09d860d5f5701edaaa292f0797774d6758123952 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Mon, 30 Mar 2020 11:52:11 +0900
Subject: [PATCH 1/4] Fix imports with no matching beatmap IDs still retaining
 a potentially invalid set ID

---
 osu.Game/Beatmaps/BeatmapManager.cs | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index abb3f8ac42..40ffb40f52 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -87,7 +87,7 @@ namespace osu.Game.Beatmaps
 
         protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz";
 
-        protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
+        protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
         {
             if (archive != null)
                 beatmapSet.Beatmaps = createBeatmapDifficulties(beatmapSet.Files);
@@ -103,7 +103,11 @@ namespace osu.Game.Beatmaps
 
             validateOnlineIds(beatmapSet);
 
-            return updateQueue.UpdateAsync(beatmapSet, cancellationToken);
+            await updateQueue.UpdateAsync(beatmapSet, cancellationToken);
+
+            // ensure at least one beatmap was able to retrieve an online ID, else drop the set ID.
+            if (!beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0))
+                beatmapSet.OnlineBeatmapSetID = null;
         }
 
         protected override void PreImport(BeatmapSetInfo beatmapSet)

From 7ecce713bb736069be7f1037df45770482d6f349 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Mon, 30 Mar 2020 15:05:40 +0900
Subject: [PATCH 2/4] Keep provided IDs where possible if not online

---
 osu.Game/Beatmaps/BeatmapManager.cs                 | 13 ++++++++-----
 osu.Game/Online/API/APIRequest.cs                   |  4 ++--
 osu.Game/Online/API/Requests/GetRankingsRequest.cs  |  3 ++-
 osu.Game/Online/API/Requests/PaginatedAPIRequest.cs |  2 +-
 4 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 40ffb40f52..797a5160c9 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -451,12 +451,15 @@ namespace osu.Game.Beatmaps
 
                     var res = req.Result;
 
-                    beatmap.Status = res.Status;
-                    beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
-                    beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
-                    beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
+                    if (res != null)
+                    {
+                        beatmap.Status = res.Status;
+                        beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
+                        beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
+                        beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
 
-                    LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
+                        LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
+                    }
                 }
                 catch (Exception e)
                 {
diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs
index 30c1018c1e..6a6c7b72a8 100644
--- a/osu.Game/Online/API/APIRequest.cs
+++ b/osu.Game/Online/API/APIRequest.cs
@@ -12,11 +12,11 @@ namespace osu.Game.Online.API
     /// An API request with a well-defined response type.
     /// </summary>
     /// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
-    public abstract class APIRequest<T> : APIRequest
+    public abstract class APIRequest<T> : APIRequest where T : class
     {
         protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest<T>(Uri);
 
-        public T Result => ((OsuJsonWebRequest<T>)WebRequest).ResponseObject;
+        public T Result => ((OsuJsonWebRequest<T>)WebRequest)?.ResponseObject;
 
         protected APIRequest()
         {
diff --git a/osu.Game/Online/API/Requests/GetRankingsRequest.cs b/osu.Game/Online/API/Requests/GetRankingsRequest.cs
index 941691c4c1..1bbaa73bbb 100644
--- a/osu.Game/Online/API/Requests/GetRankingsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetRankingsRequest.cs
@@ -6,7 +6,8 @@ using osu.Game.Rulesets;
 
 namespace osu.Game.Online.API.Requests
 {
-    public abstract class GetRankingsRequest<TModel> : APIRequest<TModel>
+    public abstract class GetRankingsRequest<TModel> : APIRequest<TModel> where TModel : class
+
     {
         private readonly RulesetInfo ruleset;
         private readonly int page;
diff --git a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs
index 52e12f04ee..bddc34a0dc 100644
--- a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs
+++ b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs
@@ -6,7 +6,7 @@ using osu.Framework.IO.Network;
 
 namespace osu.Game.Online.API.Requests
 {
-    public abstract class PaginatedAPIRequest<T> : APIRequest<T>
+    public abstract class PaginatedAPIRequest<T> : APIRequest<T> where T : class
     {
         private readonly int page;
         private readonly int itemsPerPage;

From f71c8cb30ff8c11084cc48bbe40fbfe437dbd089 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Mon, 30 Mar 2020 15:07:56 +0900
Subject: [PATCH 3/4] Only drop online set ID if beatmap IDs were stripped in
 online retrieval

---
 osu.Game/Beatmaps/BeatmapManager.cs | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 797a5160c9..6542866936 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -103,11 +103,19 @@ namespace osu.Game.Beatmaps
 
             validateOnlineIds(beatmapSet);
 
+            bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0);
+
             await updateQueue.UpdateAsync(beatmapSet, cancellationToken);
 
-            // ensure at least one beatmap was able to retrieve an online ID, else drop the set ID.
-            if (!beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0))
-                beatmapSet.OnlineBeatmapSetID = null;
+            // ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID.
+            if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0))
+            {
+                if (beatmapSet.OnlineBeatmapSetID != null)
+                {
+                    beatmapSet.OnlineBeatmapSetID = null;
+                    LogForModel(beatmapSet, "Disassociating beatmap set ID due to loss of all beatmap IDs");
+                }
+            }
         }
 
         protected override void PreImport(BeatmapSetInfo beatmapSet)

From 812583a4cd6e714626f0132a0351b62c1eea99db Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Mon, 30 Mar 2020 16:17:42 +0900
Subject: [PATCH 4/4] Remove stray newline

---
 osu.Game/Online/API/Requests/GetRankingsRequest.cs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/osu.Game/Online/API/Requests/GetRankingsRequest.cs b/osu.Game/Online/API/Requests/GetRankingsRequest.cs
index 1bbaa73bbb..ddc3298ca7 100644
--- a/osu.Game/Online/API/Requests/GetRankingsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetRankingsRequest.cs
@@ -7,7 +7,6 @@ using osu.Game.Rulesets;
 namespace osu.Game.Online.API.Requests
 {
     public abstract class GetRankingsRequest<TModel> : APIRequest<TModel> where TModel : class
-
     {
         private readonly RulesetInfo ruleset;
         private readonly int page;