mirror of
https://github.com/ppy/osu.git
synced 2025-02-13 15:03:13 +08:00
Merge branch 'beatmap-refactor/get-and-present' into beatmap-refactor/beatmap-overlays
This commit is contained in:
commit
8ad33d43d0
@ -8,6 +8,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -88,33 +89,27 @@ namespace osu.Game.Tests.Online
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserialiseScoreInfoWithEmptyMods()
|
||||
public void TestDeserialiseSubmittableScoreWithEmptyMods()
|
||||
{
|
||||
var score = new ScoreInfo { Ruleset = new OsuRuleset().RulesetInfo };
|
||||
var score = new SubmittableScore(new ScoreInfo { Ruleset = new OsuRuleset().RulesetInfo });
|
||||
|
||||
var deserialised = JsonConvert.DeserializeObject<ScoreInfo>(JsonConvert.SerializeObject(score));
|
||||
|
||||
if (deserialised != null)
|
||||
deserialised.Ruleset = new OsuRuleset().RulesetInfo;
|
||||
var deserialised = JsonConvert.DeserializeObject<SubmittableScore>(JsonConvert.SerializeObject(score));
|
||||
|
||||
Assert.That(deserialised?.Mods.Length, Is.Zero);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserialiseScoreInfoWithCustomModSetting()
|
||||
public void TestDeserialiseSubmittableScoreWithCustomModSetting()
|
||||
{
|
||||
var score = new ScoreInfo
|
||||
var score = new SubmittableScore(new ScoreInfo
|
||||
{
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
Mods = new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 2 } } }
|
||||
};
|
||||
});
|
||||
|
||||
var deserialised = JsonConvert.DeserializeObject<ScoreInfo>(JsonConvert.SerializeObject(score));
|
||||
var deserialised = JsonConvert.DeserializeObject<SubmittableScore>(JsonConvert.SerializeObject(score));
|
||||
|
||||
if (deserialised != null)
|
||||
deserialised.Ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
Assert.That(((OsuModDoubleTime)deserialised?.Mods[0])?.SpeedChange.Value, Is.EqualTo(2));
|
||||
Assert.That((deserialised?.Mods[0])?.Settings["speed_change"], Is.EqualTo(2));
|
||||
}
|
||||
|
||||
private class TestRuleset : Ruleset
|
||||
|
@ -132,9 +132,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private ScoreInfo getScoreInfo(bool replayAvailable)
|
||||
{
|
||||
return new APILegacyScoreInfo
|
||||
return new APIScoreInfo
|
||||
{
|
||||
OnlineScoreID = 2553163309,
|
||||
OnlineID = 2553163309,
|
||||
OnlineRulesetID = 0,
|
||||
Replay = replayAvailable,
|
||||
User = new User
|
||||
|
@ -43,11 +43,11 @@ namespace osu.Game.Tests.Visual.Online
|
||||
}
|
||||
};
|
||||
|
||||
var allScores = new APILegacyScores
|
||||
var allScores = new APIScoresCollection
|
||||
{
|
||||
Scores = new List<APILegacyScoreInfo>
|
||||
Scores = new List<APIScoreInfo>
|
||||
{
|
||||
new APILegacyScoreInfo
|
||||
new APIScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
TotalScore = 1234567890,
|
||||
Accuracy = 1,
|
||||
},
|
||||
new APILegacyScoreInfo
|
||||
new APIScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
TotalScore = 1234789,
|
||||
Accuracy = 0.9997,
|
||||
},
|
||||
new APILegacyScoreInfo
|
||||
new APIScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
@ -119,7 +119,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
TotalScore = 12345678,
|
||||
Accuracy = 0.9854,
|
||||
},
|
||||
new APILegacyScoreInfo
|
||||
new APIScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
@ -141,7 +141,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
TotalScore = 1234567,
|
||||
Accuracy = 0.8765,
|
||||
},
|
||||
new APILegacyScoreInfo
|
||||
new APIScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
@ -162,9 +162,9 @@ namespace osu.Game.Tests.Visual.Online
|
||||
}
|
||||
};
|
||||
|
||||
var myBestScore = new APILegacyUserTopScoreInfo
|
||||
var myBestScore = new APIScoreWithPosition
|
||||
{
|
||||
Score = new APILegacyScoreInfo
|
||||
Score = new APIScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
@ -185,9 +185,9 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Position = 1337,
|
||||
};
|
||||
|
||||
var myBestScoreWithNullPosition = new APILegacyUserTopScoreInfo
|
||||
var myBestScoreWithNullPosition = new APIScoreWithPosition
|
||||
{
|
||||
Score = new APILegacyScoreInfo
|
||||
Score = new APIScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
@ -208,11 +208,11 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Position = null,
|
||||
};
|
||||
|
||||
var oneScore = new APILegacyScores
|
||||
var oneScore = new APIScoresCollection
|
||||
{
|
||||
Scores = new List<APILegacyScoreInfo>
|
||||
Scores = new List<APIScoreInfo>
|
||||
{
|
||||
new APILegacyScoreInfo
|
||||
new APIScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
@ -273,7 +273,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
|
||||
private class TestScoresContainer : ScoresContainer
|
||||
{
|
||||
public new APILegacyScores Scores
|
||||
public new APIScoresCollection Scores
|
||||
{
|
||||
set => base.Scores = value;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// A single beatmap difficulty.
|
||||
/// </summary>
|
||||
public interface IBeatmapInfo : IHasOnlineID
|
||||
public interface IBeatmapInfo : IHasOnlineID<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// The user-specified name given to this beatmap.
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// A representation of a collection of beatmap difficulties, generally packaged as an ".osz" archive.
|
||||
/// </summary>
|
||||
public interface IBeatmapSetInfo : IHasOnlineID
|
||||
public interface IBeatmapSetInfo : IHasOnlineID<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// The date when this beatmap was imported.
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
public interface IHasOnlineID
|
||||
public interface IHasOnlineID<out T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The server-side ID representing this instance, if one exists. Any value 0 or less denotes a missing ID.
|
||||
@ -14,6 +14,6 @@ namespace osu.Game.Database
|
||||
/// Generally we use -1 when specifying "missing" in code, but values of 0 are also considered missing as the online source
|
||||
/// is generally a MySQL autoincrement value, which can never be 0.
|
||||
/// </remarks>
|
||||
int OnlineID { get; }
|
||||
T OnlineID { get; }
|
||||
}
|
||||
}
|
||||
|
@ -9,21 +9,20 @@ using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class GetScoresRequest : APIRequest<APILegacyScores>
|
||||
public class GetScoresRequest : APIRequest<APIScoresCollection>
|
||||
{
|
||||
private readonly BeatmapInfo beatmapInfo;
|
||||
private readonly IBeatmapInfo beatmapInfo;
|
||||
private readonly BeatmapLeaderboardScope scope;
|
||||
private readonly RulesetInfo ruleset;
|
||||
private readonly IRulesetInfo ruleset;
|
||||
private readonly IEnumerable<IMod> mods;
|
||||
|
||||
public GetScoresRequest(BeatmapInfo beatmapInfo, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable<IMod> mods = null)
|
||||
public GetScoresRequest(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable<IMod> mods = null)
|
||||
{
|
||||
if (!beatmapInfo.OnlineBeatmapID.HasValue)
|
||||
throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}.");
|
||||
if (beatmapInfo.OnlineID <= 0)
|
||||
throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(IBeatmapInfo.OnlineID)}.");
|
||||
|
||||
if (scope == BeatmapLeaderboardScope.Local)
|
||||
throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard");
|
||||
@ -32,30 +31,9 @@ namespace osu.Game.Online.API.Requests
|
||||
this.scope = scope;
|
||||
this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset));
|
||||
this.mods = mods ?? Array.Empty<IMod>();
|
||||
|
||||
Success += onSuccess;
|
||||
}
|
||||
|
||||
private void onSuccess(APILegacyScores r)
|
||||
{
|
||||
Debug.Assert(ruleset.ID != null, "ruleset.ID != null");
|
||||
|
||||
foreach (APILegacyScoreInfo score in r.Scores)
|
||||
{
|
||||
score.BeatmapInfo = beatmapInfo;
|
||||
score.OnlineRulesetID = ruleset.ID.Value;
|
||||
}
|
||||
|
||||
var userScore = r.UserScore;
|
||||
|
||||
if (userScore != null)
|
||||
{
|
||||
userScore.Score.BeatmapInfo = beatmapInfo;
|
||||
userScore.Score.OnlineRulesetID = ruleset.ID.Value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override string Target => $@"beatmaps/{beatmapInfo.OnlineBeatmapID}/scores{createQueryParameters()}";
|
||||
protected override string Target => $@"beatmaps/{beatmapInfo.OnlineID}/scores{createQueryParameters()}";
|
||||
|
||||
private string createQueryParameters()
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class GetUserScoresRequest : PaginatedAPIRequest<List<APILegacyScoreInfo>>
|
||||
public class GetUserScoresRequest : PaginatedAPIRequest<List<APIScoreInfo>>
|
||||
{
|
||||
private readonly long userId;
|
||||
private readonly ScoreType type;
|
||||
|
@ -1,35 +0,0 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public class APILegacyScores
|
||||
{
|
||||
[JsonProperty(@"scores")]
|
||||
public List<APILegacyScoreInfo> Scores;
|
||||
|
||||
[JsonProperty(@"userScore")]
|
||||
public APILegacyUserTopScoreInfo UserScore;
|
||||
}
|
||||
|
||||
public class APILegacyUserTopScoreInfo
|
||||
{
|
||||
[JsonProperty(@"position")]
|
||||
public int? Position;
|
||||
|
||||
[JsonProperty(@"score")]
|
||||
public APILegacyScoreInfo Score;
|
||||
|
||||
public ScoreInfo CreateScoreInfo(RulesetStore rulesets)
|
||||
{
|
||||
var score = Score.CreateScoreInfo(rulesets);
|
||||
score.Position = Position;
|
||||
return score;
|
||||
}
|
||||
}
|
||||
}
|
@ -15,9 +15,68 @@ using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public class APILegacyScoreInfo
|
||||
public class APIScoreInfo : IScoreInfo
|
||||
{
|
||||
public ScoreInfo CreateScoreInfo(RulesetStore rulesets)
|
||||
[JsonProperty(@"score")]
|
||||
public long TotalScore { get; set; }
|
||||
|
||||
[JsonProperty(@"max_combo")]
|
||||
public int MaxCombo { get; set; }
|
||||
|
||||
[JsonProperty(@"user")]
|
||||
public User User { get; set; }
|
||||
|
||||
public bool HasReplay { get; set; }
|
||||
|
||||
[JsonProperty(@"id")]
|
||||
public long OnlineID { get; set; }
|
||||
|
||||
[JsonProperty(@"replay")]
|
||||
public bool Replay { get; set; }
|
||||
|
||||
[JsonProperty(@"created_at")]
|
||||
public DateTimeOffset Date { get; set; }
|
||||
|
||||
[JsonProperty(@"beatmap")]
|
||||
public APIBeatmap Beatmap { get; set; }
|
||||
|
||||
[JsonProperty("accuracy")]
|
||||
public double Accuracy { get; set; }
|
||||
|
||||
[JsonProperty(@"pp")]
|
||||
public double? PP { get; set; }
|
||||
|
||||
[JsonProperty(@"beatmapset")]
|
||||
public APIBeatmapSet BeatmapSet
|
||||
{
|
||||
set
|
||||
{
|
||||
// in the deserialisation case we need to ferry this data across.
|
||||
if (Beatmap is APIBeatmap apiBeatmap)
|
||||
apiBeatmap.BeatmapSet = value;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("statistics")]
|
||||
public Dictionary<string, int> Statistics { get; set; }
|
||||
|
||||
[JsonProperty(@"mode_int")]
|
||||
public int OnlineRulesetID { get; set; }
|
||||
|
||||
[JsonProperty(@"mods")]
|
||||
public string[] Mods { get; set; }
|
||||
|
||||
[JsonProperty("rank")]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public ScoreRank Rank { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="ScoreInfo"/> from an API score instance.
|
||||
/// </summary>
|
||||
/// <param name="rulesets">A ruleset store, used to populate a ruleset instance in the returned score.</param>
|
||||
/// <param name="beatmap">An optional beatmap, copied into the returned score (for cases where the API does not populate the beatmap).</param>
|
||||
/// <returns></returns>
|
||||
public ScoreInfo CreateScoreInfo(RulesetStore rulesets, BeatmapInfo beatmap = null)
|
||||
{
|
||||
var ruleset = rulesets.GetRuleset(OnlineRulesetID);
|
||||
|
||||
@ -34,10 +93,9 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
MaxCombo = MaxCombo,
|
||||
User = User,
|
||||
Accuracy = Accuracy,
|
||||
OnlineScoreID = OnlineScoreID,
|
||||
OnlineScoreID = OnlineID,
|
||||
Date = Date,
|
||||
PP = PP,
|
||||
BeatmapInfo = BeatmapInfo,
|
||||
RulesetID = OnlineRulesetID,
|
||||
Hash = Replay ? "online" : string.Empty, // todo: temporary?
|
||||
Rank = Rank,
|
||||
@ -45,6 +103,9 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
Mods = mods,
|
||||
};
|
||||
|
||||
if (beatmap != null)
|
||||
scoreInfo.BeatmapInfo = beatmap;
|
||||
|
||||
if (Statistics != null)
|
||||
{
|
||||
foreach (var kvp in Statistics)
|
||||
@ -81,57 +142,8 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
return scoreInfo;
|
||||
}
|
||||
|
||||
[JsonProperty(@"score")]
|
||||
public int TotalScore { get; set; }
|
||||
public IRulesetInfo Ruleset => new RulesetInfo { ID = OnlineRulesetID };
|
||||
|
||||
[JsonProperty(@"max_combo")]
|
||||
public int MaxCombo { get; set; }
|
||||
|
||||
[JsonProperty(@"user")]
|
||||
public User User { get; set; }
|
||||
|
||||
[JsonProperty(@"id")]
|
||||
public long OnlineScoreID { get; set; }
|
||||
|
||||
[JsonProperty(@"replay")]
|
||||
public bool Replay { get; set; }
|
||||
|
||||
[JsonProperty(@"created_at")]
|
||||
public DateTimeOffset Date { get; set; }
|
||||
|
||||
[JsonProperty(@"beatmap")]
|
||||
public BeatmapInfo BeatmapInfo { get; set; }
|
||||
|
||||
[JsonProperty("accuracy")]
|
||||
public double Accuracy { get; set; }
|
||||
|
||||
[JsonProperty(@"pp")]
|
||||
public double? PP { get; set; }
|
||||
|
||||
[JsonProperty(@"beatmapset")]
|
||||
public BeatmapMetadata Metadata
|
||||
{
|
||||
set
|
||||
{
|
||||
// extract the set ID to its correct place.
|
||||
BeatmapInfo.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = value.ID };
|
||||
value.ID = 0;
|
||||
|
||||
BeatmapInfo.Metadata = value;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty(@"statistics")]
|
||||
public Dictionary<string, int> Statistics { get; set; }
|
||||
|
||||
[JsonProperty(@"mode_int")]
|
||||
public int OnlineRulesetID { get; set; }
|
||||
|
||||
[JsonProperty(@"mods")]
|
||||
public string[] Mods { get; set; }
|
||||
|
||||
[JsonProperty("rank")]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public ScoreRank Rank { get; set; }
|
||||
IBeatmapInfo IScoreInfo.Beatmap => Beatmap;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public class APIScoreWithPosition
|
||||
{
|
||||
[JsonProperty(@"position")]
|
||||
public int? Position;
|
||||
|
||||
[JsonProperty(@"score")]
|
||||
public APIScoreInfo Score;
|
||||
|
||||
public ScoreInfo CreateScoreInfo(RulesetStore rulesets, BeatmapInfo beatmap = null)
|
||||
|
||||
{
|
||||
var score = Score.CreateScoreInfo(rulesets, beatmap);
|
||||
score.Position = Position;
|
||||
return score;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public class APIScoresCollection
|
||||
{
|
||||
[JsonProperty(@"scores")]
|
||||
public List<APIScoreInfo> Scores;
|
||||
|
||||
[JsonProperty(@"userScore")]
|
||||
public APIScoreWithPosition UserScore;
|
||||
}
|
||||
}
|
@ -16,13 +16,13 @@ namespace osu.Game.Online.Solo
|
||||
|
||||
private readonly int beatmapId;
|
||||
|
||||
private readonly ScoreInfo scoreInfo;
|
||||
private readonly SubmittableScore score;
|
||||
|
||||
public SubmitSoloScoreRequest(int beatmapId, long scoreId, ScoreInfo scoreInfo)
|
||||
{
|
||||
this.beatmapId = beatmapId;
|
||||
this.scoreId = scoreId;
|
||||
this.scoreInfo = scoreInfo;
|
||||
score = new SubmittableScore(scoreInfo);
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
@ -32,7 +32,7 @@ namespace osu.Game.Online.Solo
|
||||
req.ContentType = "application/json";
|
||||
req.Method = HttpMethod.Put;
|
||||
|
||||
req.AddRaw(JsonConvert.SerializeObject(scoreInfo, new JsonSerializerSettings
|
||||
req.AddRaw(JsonConvert.SerializeObject(score, new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||
}));
|
||||
|
75
osu.Game/Online/Solo/SubmittableScore.cs
Normal file
75
osu.Game/Online/Solo/SubmittableScore.cs
Normal file
@ -0,0 +1,75 @@
|
||||
// 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.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.Solo
|
||||
{
|
||||
/// <summary>
|
||||
/// A class specifically for sending scores to the API during score submission.
|
||||
/// This is used instead of <see cref="APIScoreInfo"/> due to marginally different serialisation naming requirements.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class SubmittableScore
|
||||
{
|
||||
[JsonProperty("rank")]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public ScoreRank Rank { get; set; }
|
||||
|
||||
[JsonProperty("total_score")]
|
||||
public long TotalScore { get; set; }
|
||||
|
||||
[JsonProperty("accuracy")]
|
||||
public double Accuracy { get; set; }
|
||||
|
||||
[JsonProperty(@"pp")]
|
||||
public double? PP { get; set; }
|
||||
|
||||
[JsonProperty("max_combo")]
|
||||
public int MaxCombo { get; set; }
|
||||
|
||||
[JsonProperty("ruleset_id")]
|
||||
public int RulesetID { get; set; }
|
||||
|
||||
[JsonProperty("passed")]
|
||||
public bool Passed { get; set; }
|
||||
|
||||
// Used for API serialisation/deserialisation.
|
||||
[JsonProperty("mods")]
|
||||
public APIMod[] Mods { get; set; }
|
||||
|
||||
[JsonProperty("user")]
|
||||
public User User { get; set; }
|
||||
|
||||
[JsonProperty("statistics")]
|
||||
public Dictionary<HitResult, int> Statistics { get; set; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public SubmittableScore()
|
||||
{
|
||||
}
|
||||
|
||||
public SubmittableScore(ScoreInfo score)
|
||||
{
|
||||
Rank = score.Rank;
|
||||
TotalScore = score.TotalScore;
|
||||
Accuracy = score.Accuracy;
|
||||
PP = score.PP;
|
||||
MaxCombo = score.MaxCombo;
|
||||
RulesetID = score.RulesetID;
|
||||
Passed = score.Passed;
|
||||
Mods = score.APIMods;
|
||||
User = score.User;
|
||||
Statistics = score.Statistics;
|
||||
}
|
||||
}
|
||||
}
|
@ -436,11 +436,15 @@ namespace osu.Game
|
||||
/// <item>first beatmap from any ruleset.</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public void PresentBeatmap(BeatmapSetInfo beatmap, Predicate<BeatmapInfo> difficultyCriteria = null)
|
||||
public void PresentBeatmap(IBeatmapSetInfo beatmap, Predicate<BeatmapInfo> difficultyCriteria = null)
|
||||
{
|
||||
var databasedSet = beatmap.OnlineBeatmapSetID != null
|
||||
? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID)
|
||||
: BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash);
|
||||
BeatmapSetInfo databasedSet = null;
|
||||
|
||||
if (beatmap.OnlineID > 0)
|
||||
databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineID);
|
||||
|
||||
if (beatmap is BeatmapSetInfo localBeatmap)
|
||||
databasedSet ??= BeatmapManager.QueryBeatmapSet(s => s.Hash == localBeatmap.Hash);
|
||||
|
||||
if (databasedSet == null)
|
||||
{
|
||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
|
||||
private CancellationTokenSource loadCancellationSource;
|
||||
|
||||
protected APILegacyScores Scores
|
||||
protected APIScoresCollection Scores
|
||||
{
|
||||
set => Schedule(() =>
|
||||
{
|
||||
@ -66,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
if (value?.Scores.Any() != true)
|
||||
return;
|
||||
|
||||
scoreManager.OrderByTotalScoreAsync(value.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToArray(), loadCancellationSource.Token)
|
||||
scoreManager.OrderByTotalScoreAsync(value.Scores.Select(s => s.CreateScoreInfo(rulesets, Beatmap.Value)).ToArray(), loadCancellationSource.Token)
|
||||
.ContinueWith(ordered => Schedule(() =>
|
||||
{
|
||||
if (loadCancellationSource.IsCancellationRequested)
|
||||
@ -78,7 +78,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
scoreTable.Show();
|
||||
|
||||
var userScore = value.UserScore;
|
||||
var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets);
|
||||
var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets, Beatmap.Value);
|
||||
|
||||
topScoresContainer.Add(new DrawableTopScore(topScore));
|
||||
|
||||
|
@ -15,7 +15,7 @@ using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
{
|
||||
public class PaginatedScoreContainer : PaginatedProfileSubsection<APILegacyScoreInfo>
|
||||
public class PaginatedScoreContainer : PaginatedProfileSubsection<APIScoreInfo>
|
||||
{
|
||||
private readonly ScoreType type;
|
||||
|
||||
@ -51,7 +51,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnItemsReceived(List<APILegacyScoreInfo> items)
|
||||
protected override void OnItemsReceived(List<APIScoreInfo> items)
|
||||
{
|
||||
if (VisiblePages == 0)
|
||||
drawableItemIndex = 0;
|
||||
@ -59,12 +59,12 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
base.OnItemsReceived(items);
|
||||
}
|
||||
|
||||
protected override APIRequest<List<APILegacyScoreInfo>> CreateRequest() =>
|
||||
protected override APIRequest<List<APIScoreInfo>> CreateRequest() =>
|
||||
new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage);
|
||||
|
||||
private int drawableItemIndex;
|
||||
|
||||
protected override Drawable CreateDrawableItem(APILegacyScoreInfo model)
|
||||
protected override Drawable CreateDrawableItem(APIScoreInfo model)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Rulesets
|
||||
/// <summary>
|
||||
/// A representation of a ruleset's metadata.
|
||||
/// </summary>
|
||||
public interface IRulesetInfo : IHasOnlineID
|
||||
public interface IRulesetInfo : IHasOnlineID<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// The user-exposed name of this ruleset.
|
||||
|
39
osu.Game/Scoring/IScoreInfo.cs
Normal file
39
osu.Game/Scoring/IScoreInfo.cs
Normal file
@ -0,0 +1,39 @@
|
||||
// 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 osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Scoring
|
||||
{
|
||||
public interface IScoreInfo : IHasOnlineID<long>
|
||||
{
|
||||
User User { get; }
|
||||
|
||||
long TotalScore { get; }
|
||||
|
||||
int MaxCombo { get; }
|
||||
|
||||
double Accuracy { get; }
|
||||
|
||||
bool HasReplay { get; }
|
||||
|
||||
DateTimeOffset Date { get; }
|
||||
|
||||
double? PP { get; }
|
||||
|
||||
IBeatmapInfo Beatmap { get; }
|
||||
|
||||
IRulesetInfo Ruleset { get; }
|
||||
|
||||
public ScoreRank Rank { get; }
|
||||
|
||||
// Mods is currently missing from this interface as the `IMod` class has properties which can't be fulfilled by `APIMod`,
|
||||
// but also doesn't expose `Settings`. We can consider how to implement this in the future if required.
|
||||
|
||||
// Statistics is also missing. This can be reconsidered once changes in serialisation have been completed.
|
||||
}
|
||||
}
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
@ -19,47 +18,36 @@ using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Scoring
|
||||
{
|
||||
public class ScoreInfo : IHasFiles<ScoreFileInfo>, IHasPrimaryKey, ISoftDelete, IEquatable<ScoreInfo>, IDeepCloneable<ScoreInfo>
|
||||
public class ScoreInfo : IScoreInfo, IHasFiles<ScoreFileInfo>, IHasPrimaryKey, ISoftDelete, IEquatable<ScoreInfo>, IDeepCloneable<ScoreInfo>
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
[JsonProperty("rank")]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public ScoreRank Rank { get; set; }
|
||||
|
||||
[JsonProperty("total_score")]
|
||||
public long TotalScore { get; set; }
|
||||
|
||||
[JsonProperty("accuracy")]
|
||||
[Column(TypeName = "DECIMAL(1,4)")] // TODO: This data type is wrong (should contain more precision). But at the same time, we probably don't need to be storing this in the database.
|
||||
public double Accuracy { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public LocalisableString DisplayAccuracy => Accuracy.FormatAccuracy();
|
||||
|
||||
[JsonProperty(@"pp")]
|
||||
public double? PP { get; set; }
|
||||
|
||||
[JsonProperty("max_combo")]
|
||||
public int MaxCombo { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public int Combo { get; set; } // Todo: Shouldn't exist in here
|
||||
|
||||
[JsonProperty("ruleset_id")]
|
||||
public int RulesetID { get; set; }
|
||||
|
||||
[JsonProperty("passed")]
|
||||
[NotMapped]
|
||||
public bool Passed { get; set; } = true;
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual RulesetInfo Ruleset { get; set; }
|
||||
public RulesetInfo Ruleset { get; set; }
|
||||
|
||||
private APIMod[] localAPIMods;
|
||||
|
||||
private Mod[] mods;
|
||||
|
||||
[JsonIgnore]
|
||||
[NotMapped]
|
||||
public Mod[] Mods
|
||||
{
|
||||
@ -74,7 +62,7 @@ namespace osu.Game.Scoring
|
||||
if (mods != null)
|
||||
scoreMods = mods;
|
||||
else if (localAPIMods != null)
|
||||
scoreMods = apiMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
|
||||
scoreMods = APIMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
|
||||
|
||||
return scoreMods;
|
||||
}
|
||||
@ -86,9 +74,8 @@ namespace osu.Game.Scoring
|
||||
}
|
||||
|
||||
// Used for API serialisation/deserialisation.
|
||||
[JsonProperty("mods")]
|
||||
[NotMapped]
|
||||
private APIMod[] apiMods
|
||||
public APIMod[] APIMods
|
||||
{
|
||||
get
|
||||
{
|
||||
@ -110,19 +97,16 @@ namespace osu.Game.Scoring
|
||||
}
|
||||
|
||||
// Used for database serialisation/deserialisation.
|
||||
[JsonIgnore]
|
||||
[Column("Mods")]
|
||||
public string ModsJson
|
||||
{
|
||||
get => JsonConvert.SerializeObject(apiMods);
|
||||
set => apiMods = JsonConvert.DeserializeObject<APIMod[]>(value);
|
||||
get => JsonConvert.SerializeObject(APIMods);
|
||||
set => APIMods = JsonConvert.DeserializeObject<APIMod[]>(value);
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
[JsonProperty("user")]
|
||||
public User User { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[Column("User")]
|
||||
public string UserString
|
||||
{
|
||||
@ -134,7 +118,6 @@ namespace osu.Game.Scoring
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
[Column("UserID")]
|
||||
public int? UserID
|
||||
{
|
||||
@ -146,23 +129,18 @@ namespace osu.Game.Scoring
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public int BeatmapInfoID { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[Column("Beatmap")]
|
||||
public virtual BeatmapInfo BeatmapInfo { get; set; }
|
||||
public BeatmapInfo BeatmapInfo { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public long? OnlineScoreID { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public DateTimeOffset Date { get; set; }
|
||||
|
||||
[JsonProperty("statistics")]
|
||||
public Dictionary<HitResult, int> Statistics = new Dictionary<HitResult, int>();
|
||||
[NotMapped]
|
||||
public Dictionary<HitResult, int> Statistics { get; set; } = new Dictionary<HitResult, int>();
|
||||
|
||||
[JsonIgnore]
|
||||
[Column("Statistics")]
|
||||
public string StatisticsJson
|
||||
{
|
||||
@ -180,29 +158,23 @@ namespace osu.Game.Scoring
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
[JsonIgnore]
|
||||
public List<HitEvent> HitEvents { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public List<ScoreFileInfo> Files { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string Hash { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool DeletePending { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The position of this score, starting at 1.
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
[JsonProperty("position")]
|
||||
public int? Position { get; set; }
|
||||
public int? Position { get; set; } // TODO: remove after all calls to `CreateScoreInfo` are gone.
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="ScoreInfo"/> represents a legacy (osu!stable) score.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
[NotMapped]
|
||||
public bool IsLegacyScore => Mods.OfType<ModClassic>().Any();
|
||||
|
||||
@ -271,5 +243,11 @@ namespace osu.Game.Scoring
|
||||
|
||||
return ReferenceEquals(this, other);
|
||||
}
|
||||
|
||||
public long OnlineID => OnlineScoreID ?? -1;
|
||||
|
||||
IBeatmapInfo IScoreInfo.Beatmap => BeatmapInfo;
|
||||
IRulesetInfo IScoreInfo.Ruleset => Ruleset;
|
||||
bool IScoreInfo.HasReplay => Files.Any();
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Screens.Ranking
|
||||
return null;
|
||||
|
||||
getScoreRequest = new GetScoresRequest(Score.BeatmapInfo, Score.Ruleset);
|
||||
getScoreRequest.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineScoreID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets)));
|
||||
getScoreRequest.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets, Beatmap.Value.BeatmapInfo)));
|
||||
return getScoreRequest;
|
||||
}
|
||||
|
||||
|
@ -192,14 +192,14 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
|
||||
req.Success += r =>
|
||||
{
|
||||
scoreManager.OrderByTotalScoreAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToArray(), loadCancellationSource.Token)
|
||||
scoreManager.OrderByTotalScoreAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets, BeatmapInfo)).ToArray(), loadCancellationSource.Token)
|
||||
.ContinueWith(ordered => Schedule(() =>
|
||||
{
|
||||
if (loadCancellationSource.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
scoresCallback?.Invoke(ordered.Result);
|
||||
TopScore = r.UserScore?.CreateScoreInfo(rulesets);
|
||||
TopScore = r.UserScore?.CreateScoreInfo(rulesets, BeatmapInfo);
|
||||
}), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user