mirror of
https://github.com/ppy/osu
synced 2025-01-19 04:20:59 +00:00
Merge pull request #12152 from peppy/solo-score-submission
Add solo score submission flow
This commit is contained in:
commit
cfc65d5226
@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
public void TestPerformAtSongSelectFromPlayerLoader()
|
||||
{
|
||||
PushAndConfirm(() => new PlaySongSelect());
|
||||
PushAndConfirm(() => new PlayerLoader(() => new Player()));
|
||||
PushAndConfirm(() => new PlayerLoader(() => new SoloPlayer()));
|
||||
|
||||
AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(PlaySongSelect) }));
|
||||
AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
|
||||
@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
public void TestPerformAtMenuFromPlayerLoader()
|
||||
{
|
||||
PushAndConfirm(() => new PlaySongSelect());
|
||||
PushAndConfirm(() => new PlayerLoader(() => new Player()));
|
||||
PushAndConfirm(() => new PlayerLoader(() => new SoloPlayer()));
|
||||
|
||||
AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true));
|
||||
AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is MainMenu);
|
||||
|
32
osu.Game/Online/Solo/CreateSoloScoreRequest.cs
Normal file
32
osu.Game/Online/Solo/CreateSoloScoreRequest.cs
Normal file
@ -0,0 +1,32 @@
|
||||
// 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.Net.Http;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Online.Solo
|
||||
{
|
||||
public class CreateSoloScoreRequest : APIRequest<APIScoreToken>
|
||||
{
|
||||
private readonly int beatmapId;
|
||||
private readonly string versionHash;
|
||||
|
||||
public CreateSoloScoreRequest(int beatmapId, string versionHash)
|
||||
{
|
||||
this.beatmapId = beatmapId;
|
||||
this.versionHash = versionHash;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
req.Method = HttpMethod.Post;
|
||||
req.AddParameter("version_hash", versionHash);
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => $@"solo/{beatmapId}/scores";
|
||||
}
|
||||
}
|
45
osu.Game/Online/Solo/SubmitSoloScoreRequest.cs
Normal file
45
osu.Game/Online/Solo/SubmitSoloScoreRequest.cs
Normal file
@ -0,0 +1,45 @@
|
||||
// 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.Net.Http;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Online.Solo
|
||||
{
|
||||
public class SubmitSoloScoreRequest : APIRequest<MultiplayerScore>
|
||||
{
|
||||
private readonly long scoreId;
|
||||
|
||||
private readonly int beatmapId;
|
||||
|
||||
private readonly ScoreInfo scoreInfo;
|
||||
|
||||
public SubmitSoloScoreRequest(int beatmapId, long scoreId, ScoreInfo scoreInfo)
|
||||
{
|
||||
this.beatmapId = beatmapId;
|
||||
this.scoreId = scoreId;
|
||||
this.scoreInfo = scoreInfo;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
|
||||
req.ContentType = "application/json";
|
||||
req.Method = HttpMethod.Put;
|
||||
|
||||
req.AddRaw(JsonConvert.SerializeObject(scoreInfo, new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||
}));
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => $@"solo/{beatmapId}/scores/{scoreId}";
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Ranking;
|
||||
@ -19,8 +18,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
// Todo: The "room" part of PlaylistsPlayer should be split out into an abstract player class to be inherited instead.
|
||||
public class MultiplayerPlayer : PlaylistsPlayer
|
||||
public class MultiplayerPlayer : RoomSubmittingPlayer
|
||||
{
|
||||
protected override bool PauseOnFocusLost => false;
|
||||
|
||||
@ -63,9 +61,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add);
|
||||
|
||||
HUDOverlay.Add(loadingDisplay = new LoadingLayer(true) { Depth = float.MaxValue });
|
||||
}
|
||||
|
||||
if (Token == null)
|
||||
return; // Todo: Somehow handle token retrieval failure.
|
||||
protected override void LoadAsyncComplete()
|
||||
{
|
||||
base.LoadAsyncComplete();
|
||||
|
||||
if (!ValidForResume)
|
||||
return; // token retrieval may have failed.
|
||||
|
||||
client.MatchStarted += onMatchStarted;
|
||||
client.ResultsReady += onResultsReady;
|
||||
@ -135,9 +138,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
private void onResultsReady() => resultsReady.SetResult(true);
|
||||
|
||||
protected override async Task SubmitScore(Score score)
|
||||
protected override async Task PrepareScoreForResultsAsync(Score score)
|
||||
{
|
||||
await base.SubmitScore(score).ConfigureAwait(false);
|
||||
await base.PrepareScoreForResultsAsync(score).ConfigureAwait(false);
|
||||
|
||||
await client.ChangeState(MultiplayerUserState.FinishedPlay).ConfigureAwait(false);
|
||||
|
||||
|
@ -4,13 +4,9 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Scoring;
|
||||
@ -19,36 +15,18 @@ using osu.Game.Screens.Ranking;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
public class PlaylistsPlayer : Player
|
||||
public class PlaylistsPlayer : RoomSubmittingPlayer
|
||||
{
|
||||
public Action Exited;
|
||||
|
||||
[Resolved(typeof(Room), nameof(Room.RoomID))]
|
||||
protected Bindable<long?> RoomId { get; private set; }
|
||||
|
||||
protected readonly PlaylistItem PlaylistItem;
|
||||
|
||||
protected long? Token { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||
|
||||
public PlaylistsPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null)
|
||||
: base(configuration)
|
||||
: base(playlistItem, configuration)
|
||||
{
|
||||
PlaylistItem = playlistItem;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(IBindable<RulesetInfo> ruleset)
|
||||
{
|
||||
Token = null;
|
||||
|
||||
bool failed = false;
|
||||
|
||||
// Sanity checks to ensure that PlaylistsPlayer matches the settings for the current PlaylistItem
|
||||
if (Beatmap.Value.BeatmapInfo.OnlineBeatmapID != PlaylistItem.Beatmap.Value.OnlineBeatmapID)
|
||||
throw new InvalidOperationException("Current Beatmap does not match PlaylistItem's Beatmap");
|
||||
@ -58,29 +36,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
|
||||
if (!PlaylistItem.RequiredMods.All(m => Mods.Value.Any(m.Equals)))
|
||||
throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods");
|
||||
|
||||
var req = new CreateRoomScoreRequest(RoomId.Value ?? 0, PlaylistItem.ID, Game.VersionHash);
|
||||
req.Success += r => Token = r.ID;
|
||||
req.Failure += e =>
|
||||
{
|
||||
failed = true;
|
||||
|
||||
if (string.IsNullOrEmpty(e.Message))
|
||||
Logger.Error(e, "Failed to retrieve a score submission token.");
|
||||
else
|
||||
Logger.Log($"You are not able to submit a score: {e.Message}", level: LogLevel.Important);
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
ValidForResume = false;
|
||||
this.Exit();
|
||||
});
|
||||
};
|
||||
|
||||
api.Queue(req);
|
||||
|
||||
while (!failed && !Token.HasValue)
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
@ -106,31 +61,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
return score;
|
||||
}
|
||||
|
||||
protected override async Task SubmitScore(Score score)
|
||||
{
|
||||
await base.SubmitScore(score).ConfigureAwait(false);
|
||||
|
||||
Debug.Assert(Token != null);
|
||||
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
var request = new SubmitRoomScoreRequest(Token.Value, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo);
|
||||
|
||||
request.Success += s =>
|
||||
{
|
||||
score.ScoreInfo.OnlineScoreID = s.ID;
|
||||
tcs.SetResult(true);
|
||||
};
|
||||
|
||||
request.Failure += e =>
|
||||
{
|
||||
Logger.Error(e, "Failed to submit score");
|
||||
tcs.SetResult(false);
|
||||
};
|
||||
|
||||
api.Queue(request);
|
||||
await tcs.Task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
[Cached]
|
||||
[Cached(typeof(ISamplePlaybackDisabler))]
|
||||
public class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler
|
||||
public abstract class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler
|
||||
{
|
||||
/// <summary>
|
||||
/// The delay upon completion of the beatmap before displaying the results screen.
|
||||
@ -135,7 +135,7 @@ namespace osu.Game.Screens.Play
|
||||
/// <summary>
|
||||
/// Create a new player instance.
|
||||
/// </summary>
|
||||
public Player(PlayerConfiguration configuration = null)
|
||||
protected Player(PlayerConfiguration configuration = null)
|
||||
{
|
||||
Configuration = configuration ?? new PlayerConfiguration();
|
||||
}
|
||||
@ -559,7 +559,7 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
|
||||
private ScheduledDelegate completionProgressDelegate;
|
||||
private Task<ScoreInfo> scoreSubmissionTask;
|
||||
private Task<ScoreInfo> prepareScoreForDisplayTask;
|
||||
|
||||
private void updateCompletionState(ValueChangedEvent<bool> completionState)
|
||||
{
|
||||
@ -586,17 +586,17 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
if (!Configuration.ShowResults) return;
|
||||
|
||||
scoreSubmissionTask ??= Task.Run(async () =>
|
||||
prepareScoreForDisplayTask ??= Task.Run(async () =>
|
||||
{
|
||||
var score = CreateScore();
|
||||
|
||||
try
|
||||
{
|
||||
await SubmitScore(score).ConfigureAwait(false);
|
||||
await PrepareScoreForResultsAsync(score).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "Score submission failed!");
|
||||
Logger.Error(ex, "Score preparation failed!");
|
||||
}
|
||||
|
||||
try
|
||||
@ -617,7 +617,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private void scheduleCompletion() => completionProgressDelegate = Schedule(() =>
|
||||
{
|
||||
if (!scoreSubmissionTask.IsCompleted)
|
||||
if (!prepareScoreForDisplayTask.IsCompleted)
|
||||
{
|
||||
scheduleCompletion();
|
||||
return;
|
||||
@ -625,7 +625,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
// screen may be in the exiting transition phase.
|
||||
if (this.IsCurrentScreen())
|
||||
this.Push(CreateResults(scoreSubmissionTask.Result));
|
||||
this.Push(CreateResults(prepareScoreForDisplayTask.Result));
|
||||
});
|
||||
|
||||
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value;
|
||||
@ -895,11 +895,11 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submits the player's <see cref="Score"/>.
|
||||
/// Prepare the <see cref="Score"/> for display at results.
|
||||
/// </summary>
|
||||
/// <param name="score">The <see cref="Score"/> to submit.</param>
|
||||
/// <returns>The submitted score.</returns>
|
||||
protected virtual Task SubmitScore(Score score) => Task.CompletedTask;
|
||||
/// <param name="score">The <see cref="Score"/> to prepare.</param>
|
||||
/// <returns>A task that prepares the provided score. On completion, the score is assumed to be ready for display.</returns>
|
||||
protected virtual Task PrepareScoreForResultsAsync(Score score) => Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="ResultsScreen"/> for a <see cref="ScoreInfo"/>.
|
||||
|
38
osu.Game/Screens/Play/RoomSubmittingPlayer.cs
Normal file
38
osu.Game/Screens/Play/RoomSubmittingPlayer.cs
Normal file
@ -0,0 +1,38 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
/// <summary>
|
||||
/// A player instance which submits to a room backing. This is generally used by playlists and multiplayer.
|
||||
/// </summary>
|
||||
public abstract class RoomSubmittingPlayer : SubmittingPlayer
|
||||
{
|
||||
[Resolved(typeof(Room), nameof(Room.RoomID))]
|
||||
protected Bindable<long?> RoomId { get; private set; }
|
||||
|
||||
protected readonly PlaylistItem PlaylistItem;
|
||||
|
||||
protected RoomSubmittingPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null)
|
||||
: base(configuration)
|
||||
{
|
||||
PlaylistItem = playlistItem;
|
||||
}
|
||||
|
||||
protected override APIRequest<APIScoreToken> CreateTokenRequest()
|
||||
{
|
||||
if (!(RoomId.Value is long roomId))
|
||||
return null;
|
||||
|
||||
return new CreateRoomScoreRequest(roomId, PlaylistItem.ID, Game.VersionHash);
|
||||
}
|
||||
|
||||
protected override APIRequest<MultiplayerScore> CreateSubmissionRequest(Score score, long token) => new SubmitRoomScoreRequest(token, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo);
|
||||
}
|
||||
}
|
34
osu.Game/Screens/Play/SoloPlayer.cs
Normal file
34
osu.Game/Screens/Play/SoloPlayer.cs
Normal 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 System;
|
||||
using System.Diagnostics;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class SoloPlayer : SubmittingPlayer
|
||||
{
|
||||
protected override APIRequest<APIScoreToken> CreateTokenRequest()
|
||||
{
|
||||
if (!(Beatmap.Value.BeatmapInfo.OnlineBeatmapID is int beatmapId))
|
||||
return null;
|
||||
|
||||
return new CreateSoloScoreRequest(beatmapId, Game.VersionHash);
|
||||
}
|
||||
|
||||
protected override bool HandleTokenRetrievalFailure(Exception exception) => false;
|
||||
|
||||
protected override APIRequest<MultiplayerScore> CreateSubmissionRequest(Score score, long token)
|
||||
{
|
||||
Debug.Assert(Beatmap.Value.BeatmapInfo.OnlineBeatmapID != null);
|
||||
|
||||
int beatmapId = Beatmap.Value.BeatmapInfo.OnlineBeatmapID.Value;
|
||||
|
||||
return new SubmitSoloScoreRequest(beatmapId, token, score.ScoreInfo);
|
||||
}
|
||||
}
|
||||
}
|
141
osu.Game/Screens/Play/SubmittingPlayer.cs
Normal file
141
osu.Game/Screens/Play/SubmittingPlayer.cs
Normal file
@ -0,0 +1,141 @@
|
||||
// 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;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
/// <summary>
|
||||
/// A player instance which supports submitting scores to an online store.
|
||||
/// </summary>
|
||||
public abstract class SubmittingPlayer : Player
|
||||
{
|
||||
/// <summary>
|
||||
/// The token to be used for the current submission. This is fetched via a request created by <see cref="CreateTokenRequest"/>.
|
||||
/// </summary>
|
||||
private long? token;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
protected SubmittingPlayer(PlayerConfiguration configuration = null)
|
||||
: base(configuration)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadAsyncComplete()
|
||||
{
|
||||
if (!handleTokenRetrieval()) return;
|
||||
|
||||
base.LoadAsyncComplete();
|
||||
}
|
||||
|
||||
private bool handleTokenRetrieval()
|
||||
{
|
||||
// Token request construction should happen post-load to allow derived classes to potentially prepare DI backings that are used to create the request.
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
|
||||
if (!api.IsLoggedIn)
|
||||
{
|
||||
handleTokenFailure(new InvalidOperationException("API is not online."));
|
||||
return false;
|
||||
}
|
||||
|
||||
var req = CreateTokenRequest();
|
||||
|
||||
if (req == null)
|
||||
{
|
||||
handleTokenFailure(new InvalidOperationException("Request could not be constructed."));
|
||||
return false;
|
||||
}
|
||||
|
||||
req.Success += r =>
|
||||
{
|
||||
token = r.ID;
|
||||
tcs.SetResult(true);
|
||||
};
|
||||
req.Failure += handleTokenFailure;
|
||||
|
||||
api.Queue(req);
|
||||
|
||||
tcs.Task.Wait();
|
||||
return true;
|
||||
|
||||
void handleTokenFailure(Exception exception)
|
||||
{
|
||||
if (HandleTokenRetrievalFailure(exception))
|
||||
{
|
||||
if (string.IsNullOrEmpty(exception.Message))
|
||||
Logger.Error(exception, "Failed to retrieve a score submission token.");
|
||||
else
|
||||
Logger.Log($"You are not able to submit a score: {exception.Message}", level: LogLevel.Important);
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
ValidForResume = false;
|
||||
this.Exit();
|
||||
});
|
||||
}
|
||||
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a token could not be retrieved for submission.
|
||||
/// </summary>
|
||||
/// <param name="exception">The error causing the failure.</param>
|
||||
/// <returns>Whether gameplay should be immediately exited as a result. Returning false allows the gameplay session to continue. Defaults to true.</returns>
|
||||
protected virtual bool HandleTokenRetrievalFailure(Exception exception) => true;
|
||||
|
||||
protected override async Task PrepareScoreForResultsAsync(Score score)
|
||||
{
|
||||
await base.PrepareScoreForResultsAsync(score).ConfigureAwait(false);
|
||||
|
||||
// token may be null if the request failed but gameplay was still allowed (see HandleTokenRetrievalFailure).
|
||||
if (token == null)
|
||||
return;
|
||||
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
var request = CreateSubmissionRequest(score, token.Value);
|
||||
|
||||
request.Success += s =>
|
||||
{
|
||||
score.ScoreInfo.OnlineScoreID = s.ID;
|
||||
tcs.SetResult(true);
|
||||
};
|
||||
|
||||
request.Failure += e =>
|
||||
{
|
||||
Logger.Error(e, "Failed to submit score");
|
||||
tcs.SetResult(false);
|
||||
};
|
||||
|
||||
api.Queue(request);
|
||||
await tcs.Task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a request to be used for retrieval of the score token.
|
||||
/// Can return null, at which point <see cref="HandleTokenRetrievalFailure"/> will be fired.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
protected abstract APIRequest<APIScoreToken> CreateTokenRequest();
|
||||
|
||||
/// <summary>
|
||||
/// Construct a request to submit the score.
|
||||
/// Will only be invoked if the request constructed via <see cref="CreateTokenRequest"/> was successful.
|
||||
/// </summary>
|
||||
/// <param name="score">The score to be submitted.</param>
|
||||
/// <param name="token">The submission token.</param>
|
||||
protected abstract APIRequest<MultiplayerScore> CreateSubmissionRequest(Score score, long token);
|
||||
}
|
||||
}
|
@ -106,7 +106,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
SampleConfirm?.Play();
|
||||
|
||||
this.Push(player = new PlayerLoader(() => new Player()));
|
||||
this.Push(player = new PlayerLoader(() => new SoloPlayer()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user