mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 12:57:36 +08:00
Replace EF ScoreInfo
with realm version
May contain errors.
This commit is contained in:
parent
c5e401d678
commit
3da762e145
@ -26,7 +26,7 @@ namespace osu.Game.Database
|
|||||||
public DbSet<FileInfo> FileInfo { get; set; }
|
public DbSet<FileInfo> FileInfo { get; set; }
|
||||||
public DbSet<EFRulesetInfo> RulesetInfo { get; set; }
|
public DbSet<EFRulesetInfo> RulesetInfo { get; set; }
|
||||||
public DbSet<EFSkinInfo> SkinInfo { get; set; }
|
public DbSet<EFSkinInfo> SkinInfo { get; set; }
|
||||||
public DbSet<ScoreInfo> ScoreInfo { get; set; }
|
public DbSet<EFScoreInfo> ScoreInfo { get; set; }
|
||||||
|
|
||||||
// migrated to realm
|
// migrated to realm
|
||||||
public DbSet<DatabasedSetting> DatabasedSetting { get; set; }
|
public DbSet<DatabasedSetting> DatabasedSetting { get; set; }
|
||||||
|
@ -1,68 +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;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Framework.Testing;
|
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Database;
|
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Scoring;
|
|
||||||
using osu.Game.Users;
|
|
||||||
using Realms;
|
|
||||||
|
|
||||||
#nullable enable
|
|
||||||
|
|
||||||
namespace osu.Game.Models
|
|
||||||
{
|
|
||||||
[ExcludeFromDynamicCompile]
|
|
||||||
[MapTo("Score")]
|
|
||||||
public class RealmScore : RealmObject, IHasGuidPrimaryKey, IHasRealmFiles, ISoftDelete, IEquatable<RealmScore>, IScoreInfo
|
|
||||||
{
|
|
||||||
[PrimaryKey]
|
|
||||||
public Guid ID { get; set; } = Guid.NewGuid();
|
|
||||||
|
|
||||||
public IList<RealmNamedFileUsage> Files { get; } = null!;
|
|
||||||
|
|
||||||
public string Hash { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
public bool DeletePending { get; set; }
|
|
||||||
|
|
||||||
public bool Equals(RealmScore other) => other.ID == ID;
|
|
||||||
|
|
||||||
[Indexed]
|
|
||||||
public long OnlineID { get; set; } = -1;
|
|
||||||
|
|
||||||
public RealmUser User { get; set; } = null!;
|
|
||||||
|
|
||||||
public long TotalScore { get; set; }
|
|
||||||
|
|
||||||
public int MaxCombo { get; set; }
|
|
||||||
|
|
||||||
public double Accuracy { get; set; }
|
|
||||||
|
|
||||||
public bool HasReplay { get; set; }
|
|
||||||
|
|
||||||
public DateTimeOffset Date { get; set; }
|
|
||||||
|
|
||||||
public double? PP { get; set; } = null;
|
|
||||||
|
|
||||||
public RealmBeatmap Beatmap { get; set; } = null!;
|
|
||||||
|
|
||||||
public RealmRuleset Ruleset { get; set; } = null!;
|
|
||||||
|
|
||||||
public ScoreRank Rank
|
|
||||||
{
|
|
||||||
get => (ScoreRank)RankInt;
|
|
||||||
set => RankInt = (int)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MapTo(nameof(Rank))]
|
|
||||||
public int RankInt { get; set; }
|
|
||||||
|
|
||||||
IRulesetInfo IScoreInfo.Ruleset => Ruleset;
|
|
||||||
IBeatmapInfo IScoreInfo.Beatmap => Beatmap;
|
|
||||||
IUser IScoreInfo.User => User;
|
|
||||||
IEnumerable<INamedFileUsage> IHasNamedFiles.Files => Files;
|
|
||||||
}
|
|
||||||
}
|
|
265
osu.Game/Scoring/EFScoreInfo.cs
Normal file
265
osu.Game/Scoring/EFScoreInfo.cs
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
// 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 System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Database;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Scoring
|
||||||
|
{
|
||||||
|
public class EFScoreInfo : IScoreInfo, IHasFiles<ScoreFileInfo>, IHasPrimaryKey, ISoftDelete, IEquatable<EFScoreInfo>, IDeepCloneable<EFScoreInfo>
|
||||||
|
{
|
||||||
|
public int ID { get; set; }
|
||||||
|
|
||||||
|
public ScoreRank Rank { get; set; }
|
||||||
|
|
||||||
|
public long TotalScore { get; set; }
|
||||||
|
|
||||||
|
[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; }
|
||||||
|
|
||||||
|
public LocalisableString DisplayAccuracy => Accuracy.FormatAccuracy();
|
||||||
|
|
||||||
|
public double? PP { get; set; }
|
||||||
|
|
||||||
|
public int MaxCombo { get; set; }
|
||||||
|
|
||||||
|
public int Combo { get; set; } // Todo: Shouldn't exist in here
|
||||||
|
|
||||||
|
public int RulesetID { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public bool Passed { get; set; } = true;
|
||||||
|
|
||||||
|
public RulesetInfo Ruleset { get; set; }
|
||||||
|
|
||||||
|
private APIMod[] localAPIMods;
|
||||||
|
|
||||||
|
private Mod[] mods;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public Mod[] Mods
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var rulesetInstance = Ruleset?.CreateInstance();
|
||||||
|
if (rulesetInstance == null)
|
||||||
|
return mods ?? Array.Empty<Mod>();
|
||||||
|
|
||||||
|
Mod[] scoreMods = Array.Empty<Mod>();
|
||||||
|
|
||||||
|
if (mods != null)
|
||||||
|
scoreMods = mods;
|
||||||
|
else if (localAPIMods != null)
|
||||||
|
scoreMods = APIMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
|
||||||
|
|
||||||
|
return scoreMods;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
localAPIMods = null;
|
||||||
|
mods = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for API serialisation/deserialisation.
|
||||||
|
[NotMapped]
|
||||||
|
public APIMod[] APIMods
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (localAPIMods != null)
|
||||||
|
return localAPIMods;
|
||||||
|
|
||||||
|
if (mods == null)
|
||||||
|
return Array.Empty<APIMod>();
|
||||||
|
|
||||||
|
return localAPIMods = mods.Select(m => new APIMod(m)).ToArray();
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
localAPIMods = value;
|
||||||
|
|
||||||
|
// We potentially can't update this yet due to Ruleset being late-bound, so instead update on read as necessary.
|
||||||
|
mods = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for database serialisation/deserialisation.
|
||||||
|
[Column("Mods")]
|
||||||
|
public string ModsJson
|
||||||
|
{
|
||||||
|
get => JsonConvert.SerializeObject(APIMods);
|
||||||
|
set => APIMods = JsonConvert.DeserializeObject<APIMod[]>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public APIUser User { get; set; }
|
||||||
|
|
||||||
|
[Column("User")]
|
||||||
|
public string UserString
|
||||||
|
{
|
||||||
|
get => User?.Username;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
User ??= new APIUser();
|
||||||
|
User.Username = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("UserID")]
|
||||||
|
public int? UserID
|
||||||
|
{
|
||||||
|
get => User?.Id ?? 1;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
User ??= new APIUser();
|
||||||
|
User.Id = value ?? 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int BeatmapInfoID { get; set; }
|
||||||
|
|
||||||
|
[Column("Beatmap")]
|
||||||
|
public BeatmapInfo BeatmapInfo { get; set; }
|
||||||
|
|
||||||
|
public long? OnlineScoreID { get; set; }
|
||||||
|
|
||||||
|
public DateTimeOffset Date { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public Dictionary<HitResult, int> Statistics { get; set; } = new Dictionary<HitResult, int>();
|
||||||
|
|
||||||
|
[Column("Statistics")]
|
||||||
|
public string StatisticsJson
|
||||||
|
{
|
||||||
|
get => JsonConvert.SerializeObject(Statistics);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
Statistics.Clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Statistics = JsonConvert.DeserializeObject<Dictionary<HitResult, int>>(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public List<HitEvent> HitEvents { get; set; }
|
||||||
|
|
||||||
|
public List<ScoreFileInfo> Files { get; } = new List<ScoreFileInfo>();
|
||||||
|
|
||||||
|
public string Hash { get; set; }
|
||||||
|
|
||||||
|
public bool DeletePending { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The position of this score, starting at 1.
|
||||||
|
/// </summary>
|
||||||
|
[NotMapped]
|
||||||
|
public int? Position { get; set; } // TODO: remove after all calls to `CreateScoreInfo` are gone.
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this <see cref="EFScoreInfo"/> represents a legacy (osu!stable) score.
|
||||||
|
/// </summary>
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsLegacyScore => Mods.OfType<ModClassic>().Any();
|
||||||
|
|
||||||
|
public IEnumerable<HitResultDisplayStatistic> GetStatisticsForDisplay()
|
||||||
|
{
|
||||||
|
foreach (var r in Ruleset.CreateInstance().GetHitResults())
|
||||||
|
{
|
||||||
|
int value = Statistics.GetValueOrDefault(r.result);
|
||||||
|
|
||||||
|
switch (r.result)
|
||||||
|
{
|
||||||
|
case HitResult.SmallTickHit:
|
||||||
|
{
|
||||||
|
int total = value + Statistics.GetValueOrDefault(HitResult.SmallTickMiss);
|
||||||
|
if (total > 0)
|
||||||
|
yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HitResult.LargeTickHit:
|
||||||
|
{
|
||||||
|
int total = value + Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
|
||||||
|
if (total > 0)
|
||||||
|
yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HitResult.SmallTickMiss:
|
||||||
|
case HitResult.LargeTickMiss:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
yield return new HitResultDisplayStatistic(r.result, value, null, r.displayName);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EFScoreInfo DeepClone()
|
||||||
|
{
|
||||||
|
var clone = (EFScoreInfo)MemberwiseClone();
|
||||||
|
|
||||||
|
clone.Statistics = new Dictionary<HitResult, int>(clone.Statistics);
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => this.GetDisplayTitle();
|
||||||
|
|
||||||
|
public bool Equals(EFScoreInfo other)
|
||||||
|
{
|
||||||
|
if (other == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ID != 0 && other.ID != 0)
|
||||||
|
return ID == other.ID;
|
||||||
|
|
||||||
|
if (OnlineScoreID.HasValue && other.OnlineScoreID.HasValue)
|
||||||
|
return OnlineScoreID == other.OnlineScoreID;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(Hash) && !string.IsNullOrEmpty(other.Hash))
|
||||||
|
return Hash == other.Hash;
|
||||||
|
|
||||||
|
return ReferenceEquals(this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Implementation of IHasOnlineID
|
||||||
|
|
||||||
|
public long OnlineID => OnlineScoreID ?? -1;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of IScoreInfo
|
||||||
|
|
||||||
|
IBeatmapInfo IScoreInfo.Beatmap => BeatmapInfo;
|
||||||
|
IRulesetInfo IScoreInfo.Ruleset => Ruleset;
|
||||||
|
IUser IScoreInfo.User => User;
|
||||||
|
bool IScoreInfo.HasReplay => Files.Any();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
IEnumerable<INamedFileUsage> IHasNamedFiles.Files => Files;
|
||||||
|
}
|
||||||
|
}
|
@ -3,266 +3,66 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using osu.Framework.Testing;
|
||||||
using System.Linq;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using osu.Framework.Localisation;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Models;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osu.Game.Utils;
|
using Realms;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
namespace osu.Game.Scoring
|
namespace osu.Game.Scoring
|
||||||
{
|
{
|
||||||
public class ScoreInfo : IScoreInfo, IHasFiles<ScoreFileInfo>, IHasPrimaryKey, ISoftDelete, IEquatable<ScoreInfo>, IDeepCloneable<ScoreInfo>
|
[ExcludeFromDynamicCompile]
|
||||||
|
[MapTo("Score")]
|
||||||
|
public class ScoreInfo : RealmObject, IHasGuidPrimaryKey, IHasRealmFiles, ISoftDelete, IEquatable<ScoreInfo>, IScoreInfo
|
||||||
{
|
{
|
||||||
public int ID { get; set; }
|
[PrimaryKey]
|
||||||
|
public Guid ID { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
public bool IsManaged => ID > 0;
|
public IList<RealmNamedFileUsage> Files { get; } = null!;
|
||||||
|
|
||||||
public ScoreRank Rank { get; set; }
|
public string Hash { get; set; } = string.Empty;
|
||||||
|
|
||||||
public long TotalScore { get; set; }
|
|
||||||
|
|
||||||
[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; }
|
|
||||||
|
|
||||||
public LocalisableString DisplayAccuracy => Accuracy.FormatAccuracy();
|
|
||||||
|
|
||||||
public double? PP { get; set; }
|
|
||||||
|
|
||||||
public int MaxCombo { get; set; }
|
|
||||||
|
|
||||||
public int Combo { get; set; } // Todo: Shouldn't exist in here
|
|
||||||
|
|
||||||
public int RulesetID { get; set; }
|
|
||||||
|
|
||||||
[NotMapped]
|
|
||||||
public bool Passed { get; set; } = true;
|
|
||||||
|
|
||||||
public RulesetInfo Ruleset { get; set; }
|
|
||||||
|
|
||||||
private APIMod[] localAPIMods;
|
|
||||||
|
|
||||||
private Mod[] mods;
|
|
||||||
|
|
||||||
[NotMapped]
|
|
||||||
public Mod[] Mods
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var rulesetInstance = Ruleset?.CreateInstance();
|
|
||||||
if (rulesetInstance == null)
|
|
||||||
return mods ?? Array.Empty<Mod>();
|
|
||||||
|
|
||||||
Mod[] scoreMods = Array.Empty<Mod>();
|
|
||||||
|
|
||||||
if (mods != null)
|
|
||||||
scoreMods = mods;
|
|
||||||
else if (localAPIMods != null)
|
|
||||||
scoreMods = APIMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
|
|
||||||
|
|
||||||
return scoreMods;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
localAPIMods = null;
|
|
||||||
mods = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used for API serialisation/deserialisation.
|
|
||||||
[NotMapped]
|
|
||||||
public APIMod[] APIMods
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (localAPIMods != null)
|
|
||||||
return localAPIMods;
|
|
||||||
|
|
||||||
if (mods == null)
|
|
||||||
return Array.Empty<APIMod>();
|
|
||||||
|
|
||||||
return localAPIMods = mods.Select(m => new APIMod(m)).ToArray();
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
localAPIMods = value;
|
|
||||||
|
|
||||||
// We potentially can't update this yet due to Ruleset being late-bound, so instead update on read as necessary.
|
|
||||||
mods = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used for database serialisation/deserialisation.
|
|
||||||
[Column("Mods")]
|
|
||||||
public string ModsJson
|
|
||||||
{
|
|
||||||
get => JsonConvert.SerializeObject(APIMods);
|
|
||||||
set => APIMods = JsonConvert.DeserializeObject<APIMod[]>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[NotMapped]
|
|
||||||
public APIUser User { get; set; }
|
|
||||||
|
|
||||||
[Column("User")]
|
|
||||||
public string UserString
|
|
||||||
{
|
|
||||||
get => User?.Username;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
User ??= new APIUser();
|
|
||||||
User.Username = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Column("UserID")]
|
|
||||||
public int? UserID
|
|
||||||
{
|
|
||||||
get => User?.Id ?? 1;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
User ??= new APIUser();
|
|
||||||
User.Id = value ?? 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int BeatmapInfoID { get; set; }
|
|
||||||
|
|
||||||
[Column("Beatmap")]
|
|
||||||
public BeatmapInfo BeatmapInfo { get; set; }
|
|
||||||
|
|
||||||
private long? onlineID;
|
|
||||||
|
|
||||||
[Column("OnlineScoreID")]
|
|
||||||
public long? OnlineID
|
|
||||||
{
|
|
||||||
get => onlineID;
|
|
||||||
set => onlineID = value > 0 ? value : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DateTimeOffset Date { get; set; }
|
|
||||||
|
|
||||||
[NotMapped]
|
|
||||||
public Dictionary<HitResult, int> Statistics { get; set; } = new Dictionary<HitResult, int>();
|
|
||||||
|
|
||||||
[Column("Statistics")]
|
|
||||||
public string StatisticsJson
|
|
||||||
{
|
|
||||||
get => JsonConvert.SerializeObject(Statistics);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
Statistics.Clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Statistics = JsonConvert.DeserializeObject<Dictionary<HitResult, int>>(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[NotMapped]
|
|
||||||
public List<HitEvent> HitEvents { get; set; }
|
|
||||||
|
|
||||||
public List<ScoreFileInfo> Files { get; } = new List<ScoreFileInfo>();
|
|
||||||
|
|
||||||
public string Hash { get; set; }
|
|
||||||
|
|
||||||
public bool DeletePending { get; set; }
|
public bool DeletePending { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
public bool Equals(ScoreInfo other) => other.ID == ID;
|
||||||
/// The position of this score, starting at 1.
|
|
||||||
/// </summary>
|
|
||||||
[NotMapped]
|
|
||||||
public int? Position { get; set; } // TODO: remove after all calls to `CreateScoreInfo` are gone.
|
|
||||||
|
|
||||||
/// <summary>
|
[Indexed]
|
||||||
/// Whether this <see cref="ScoreInfo"/> represents a legacy (osu!stable) score.
|
public long OnlineID { get; set; } = -1;
|
||||||
/// </summary>
|
|
||||||
[NotMapped]
|
|
||||||
public bool IsLegacyScore => Mods.OfType<ModClassic>().Any();
|
|
||||||
|
|
||||||
public IEnumerable<HitResultDisplayStatistic> GetStatisticsForDisplay()
|
public RealmUser User { get; set; } = null!;
|
||||||
{
|
|
||||||
foreach (var r in Ruleset.CreateInstance().GetHitResults())
|
|
||||||
{
|
|
||||||
int value = Statistics.GetValueOrDefault(r.result);
|
|
||||||
|
|
||||||
switch (r.result)
|
public long TotalScore { get; set; }
|
||||||
{
|
|
||||||
case HitResult.SmallTickHit:
|
|
||||||
{
|
|
||||||
int total = value + Statistics.GetValueOrDefault(HitResult.SmallTickMiss);
|
|
||||||
if (total > 0)
|
|
||||||
yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName);
|
|
||||||
|
|
||||||
break;
|
public int MaxCombo { get; set; }
|
||||||
|
|
||||||
|
public double Accuracy { get; set; }
|
||||||
|
|
||||||
|
public bool HasReplay { get; set; }
|
||||||
|
|
||||||
|
public DateTimeOffset Date { get; set; }
|
||||||
|
|
||||||
|
public double? PP { get; set; }
|
||||||
|
|
||||||
|
public RealmBeatmap Beatmap { get; set; } = null!;
|
||||||
|
|
||||||
|
public RealmRuleset Ruleset { get; set; } = null!;
|
||||||
|
|
||||||
|
public ScoreRank Rank
|
||||||
|
{
|
||||||
|
get => (ScoreRank)RankInt;
|
||||||
|
set => RankInt = (int)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
case HitResult.LargeTickHit:
|
[MapTo(nameof(Rank))]
|
||||||
{
|
public int RankInt { get; set; }
|
||||||
int total = value + Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
|
|
||||||
if (total > 0)
|
|
||||||
yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case HitResult.SmallTickMiss:
|
|
||||||
case HitResult.LargeTickMiss:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
yield return new HitResultDisplayStatistic(r.result, value, null, r.displayName);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScoreInfo DeepClone()
|
|
||||||
{
|
|
||||||
var clone = (ScoreInfo)MemberwiseClone();
|
|
||||||
|
|
||||||
clone.Statistics = new Dictionary<HitResult, int>(clone.Statistics);
|
|
||||||
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() => this.GetDisplayTitle();
|
|
||||||
|
|
||||||
public bool Equals(ScoreInfo other)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(this, other)) return true;
|
|
||||||
if (other == null) return false;
|
|
||||||
|
|
||||||
if (ID != 0 && other.ID != 0)
|
|
||||||
return ID == other.ID;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Implementation of IHasOnlineID
|
|
||||||
|
|
||||||
long IHasOnlineID<long>.OnlineID => OnlineID ?? -1;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Implementation of IScoreInfo
|
|
||||||
|
|
||||||
IBeatmapInfo IScoreInfo.Beatmap => BeatmapInfo;
|
|
||||||
IRulesetInfo IScoreInfo.Ruleset => Ruleset;
|
IRulesetInfo IScoreInfo.Ruleset => Ruleset;
|
||||||
|
IBeatmapInfo IScoreInfo.Beatmap => Beatmap;
|
||||||
IUser IScoreInfo.User => User;
|
IUser IScoreInfo.User => User;
|
||||||
bool IScoreInfo.HasReplay => Files.Any();
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
IEnumerable<INamedFileUsage> IHasNamedFiles.Files => Files;
|
IEnumerable<INamedFileUsage> IHasNamedFiles.Files => Files;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.IO.Archives;
|
using osu.Game.IO.Archives;
|
||||||
using osu.Game.Models;
|
using osu.Game.Models;
|
||||||
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Scoring.Legacy;
|
using osu.Game.Scoring.Legacy;
|
||||||
using Realms;
|
using Realms;
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ namespace osu.Game.Stores
|
|||||||
/// Handles the storage and retrieval of Scores/WorkingScores.
|
/// Handles the storage and retrieval of Scores/WorkingScores.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ExcludeFromDynamicCompile]
|
[ExcludeFromDynamicCompile]
|
||||||
public class ScoreImporter : RealmArchiveModelImporter<RealmScore>
|
public class ScoreImporter : RealmArchiveModelImporter<ScoreInfo>
|
||||||
{
|
{
|
||||||
private readonly RealmRulesetStore rulesets;
|
private readonly RealmRulesetStore rulesets;
|
||||||
private readonly BeatmapManager beatmaps;
|
private readonly BeatmapManager beatmaps;
|
||||||
@ -40,7 +41,7 @@ namespace osu.Game.Stores
|
|||||||
this.beatmaps = beatmaps;
|
this.beatmaps = beatmaps;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override RealmScore? CreateModel(ArchiveReader archive)
|
protected override ScoreInfo? CreateModel(ArchiveReader archive)
|
||||||
{
|
{
|
||||||
using (var stream = archive.GetStream(archive.Filenames.First(f => f.EndsWith(".osr", StringComparison.OrdinalIgnoreCase))))
|
using (var stream = archive.GetStream(archive.Filenames.First(f => f.EndsWith(".osr", StringComparison.OrdinalIgnoreCase))))
|
||||||
{
|
{
|
||||||
@ -48,7 +49,7 @@ namespace osu.Game.Stores
|
|||||||
{
|
{
|
||||||
// TODO: make work.
|
// TODO: make work.
|
||||||
// return new DatabasedLegacyScoreDecoder(rulesets, beatmaps).Parse(stream).ScoreInfo;
|
// return new DatabasedLegacyScoreDecoder(rulesets, beatmaps).Parse(stream).ScoreInfo;
|
||||||
return new RealmScore();
|
return new ScoreInfo();
|
||||||
}
|
}
|
||||||
catch (LegacyScoreDecoder.BeatmapNotFoundException e)
|
catch (LegacyScoreDecoder.BeatmapNotFoundException e)
|
||||||
{
|
{
|
||||||
@ -58,7 +59,7 @@ namespace osu.Game.Stores
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task Populate(RealmScore model, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken = default)
|
protected override Task Populate(ScoreInfo model, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken = default)
|
||||||
=> Task.CompletedTask;
|
=> Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user