diff --git a/global.json b/global.json
index 6858d4044d..0223dc7330 100644
--- a/global.json
+++ b/global.json
@@ -5,6 +5,6 @@
"version": "3.1.100"
},
"msbuild-sdks": {
- "Microsoft.Build.Traversal": "2.0.24"
+ "Microsoft.Build.Traversal": "2.0.32"
}
}
\ No newline at end of file
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index abb3f8ac42..6542866936 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,19 @@ namespace osu.Game.Beatmaps
validateOnlineIds(beatmapSet);
- return updateQueue.UpdateAsync(beatmapSet, cancellationToken);
+ bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0);
+
+ await updateQueue.UpdateAsync(beatmapSet, cancellationToken);
+
+ // 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)
@@ -447,12 +459,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.
///
/// Type of the response (used for deserialisation).
- public abstract class APIRequest : APIRequest
+ public abstract class APIRequest : APIRequest where T : class
{
protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest(Uri);
- public T Result => ((OsuJsonWebRequest)WebRequest).ResponseObject;
+ public T Result => ((OsuJsonWebRequest)WebRequest)?.ResponseObject;
protected APIRequest()
{
diff --git a/osu.Game/Online/API/Requests/GetRankingsRequest.cs b/osu.Game/Online/API/Requests/GetRankingsRequest.cs
index 941691c4c1..ddc3298ca7 100644
--- a/osu.Game/Online/API/Requests/GetRankingsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetRankingsRequest.cs
@@ -6,7 +6,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Online.API.Requests
{
- public abstract class GetRankingsRequest : APIRequest
+ public abstract class GetRankingsRequest : APIRequest 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 : APIRequest
+ public abstract class PaginatedAPIRequest : APIRequest where T : class
{
private readonly int page;
private readonly int itemsPerPage;
diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs
index 74c853340d..0d2ddb7b01 100644
--- a/osu.Game/Screens/Play/ReplayPlayer.cs
+++ b/osu.Game/Screens/Play/ReplayPlayer.cs
@@ -3,6 +3,7 @@
using osu.Framework.Screens;
using osu.Game.Scoring;
+using osu.Game.Screens.Ranking;
namespace osu.Game.Screens.Play
{
@@ -29,6 +30,8 @@ namespace osu.Game.Screens.Play
this.Push(CreateResults(DrawableRuleset.ReplayScore.ScoreInfo));
}
+ protected override ResultsScreen CreateResults(ScoreInfo score) => new ResultsScreen(score, false);
+
protected override ScoreInfo CreateScore() => score.ScoreInfo;
}
}
diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs
index d063d8749f..cfba1e6e3e 100644
--- a/osu.Game/Screens/Ranking/ResultsScreen.cs
+++ b/osu.Game/Screens/Ranking/ResultsScreen.cs
@@ -33,16 +33,21 @@ namespace osu.Game.Screens.Ranking
public readonly ScoreInfo Score;
+ private readonly bool allowRetry;
+
private Drawable bottomPanel;
- public ResultsScreen(ScoreInfo score)
+ public ResultsScreen(ScoreInfo score, bool allowRetry = true)
{
Score = score;
+ this.allowRetry = allowRetry;
}
[BackgroundDependencyLoader]
private void load()
{
+ FillFlowContainer buttons;
+
InternalChildren = new[]
{
new ResultsScrollContainer
@@ -68,7 +73,7 @@ namespace osu.Game.Screens.Ranking
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#333")
},
- new FillFlowContainer
+ buttons = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -78,15 +83,16 @@ namespace osu.Game.Screens.Ranking
Children = new Drawable[]
{
new ReplayDownloadButton(Score) { Width = 300 },
- new RetryButton { Width = 300 },
}
}
}
}
};
- if (player != null)
+ if (player != null && allowRetry)
{
+ buttons.Add(new RetryButton { Width = 300 });
+
AddInternal(new HotkeyRetryOverlay
{
Action = () =>