Add basic realm models

Only the file related ones are really required outside of tests, but
seems like as good an opportunity as ever to get the rest of the models
into the game project.
This commit is contained in:
Dean Herbert 2021-10-11 15:25:00 +09:00
parent f43badabf4
commit 6ca415da9f
9 changed files with 438 additions and 0 deletions

View File

@ -0,0 +1,18 @@
// 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 osu.Game.Models;
namespace osu.Game.Database
{
/// <summary>
/// A model that contains a list of files it is responsible for.
/// </summary>
public interface IHasRealmFiles
{
IList<RealmNamedFileUsage> Files { get; }
string Hash { get; set; }
}
}

View File

@ -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 osu.Game.Models;
namespace osu.Game.Database
{
/// <summary>
/// Represent a join model which gives a filename and scope to a <see cref="File"/>.
/// </summary>
public interface INamedFile
{
public string Filename { get; set; }
public RealmFile File { get; set; }
}
}

View File

@ -0,0 +1,122 @@
// 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 JetBrains.Annotations;
using Newtonsoft.Json;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets;
using Realms;
#nullable enable
namespace osu.Game.Models
{
/// <summary>
/// A single beatmap difficulty.
/// </summary>
[ExcludeFromDynamicCompile]
[Serializable]
[MapTo("Beatmap")]
public class RealmBeatmap : RealmObject, IHasGuidPrimaryKey, IBeatmapInfo
{
[PrimaryKey]
public Guid ID { get; set; } = Guid.NewGuid();
public string DifficultyName { get; set; } = string.Empty;
public RealmRuleset Ruleset { get; set; } = null!;
public RealmBeatmapDifficulty Difficulty { get; set; } = null!;
public RealmBeatmapMetadata Metadata { get; set; } = null!;
public RealmBeatmapSet? BeatmapSet { get; set; }
public BeatmapSetOnlineStatus Status
{
get => (BeatmapSetOnlineStatus)StatusInt;
set => StatusInt = (int)value;
}
[MapTo(nameof(Status))]
public int StatusInt { get; set; }
public int? OnlineID { get; set; }
public double Length { get; set; }
public double BPM { get; set; }
public string Hash { get; set; } = string.Empty;
public double StarRating { get; set; }
public string MD5Hash { get; set; } = string.Empty;
[JsonIgnore]
public bool Hidden { get; set; }
public RealmBeatmap(RealmRuleset ruleset, RealmBeatmapDifficulty difficulty, RealmBeatmapMetadata metadata)
{
Ruleset = ruleset;
Difficulty = difficulty;
Metadata = metadata;
}
[UsedImplicitly]
private RealmBeatmap()
{
}
#region Properties we may not want persisted (but also maybe no harm?)
public double AudioLeadIn { get; set; }
public float StackLeniency { get; set; } = 0.7f;
public bool SpecialStyle { get; set; }
public bool LetterboxInBreaks { get; set; }
public bool WidescreenStoryboard { get; set; }
public bool EpilepsyWarning { get; set; }
public bool SamplesMatchPlaybackRate { get; set; }
public double DistanceSpacing { get; set; }
public int BeatDivisor { get; set; }
public int GridSize { get; set; }
public double TimelineZoom { get; set; }
#endregion
/// <summary>
/// Returns a shallow-clone of this <see cref="RealmBeatmap"/>.
/// </summary>
public RealmBeatmap Clone() => (RealmBeatmap)MemberwiseClone();
public bool AudioEquals(RealmBeatmap? other) => other != null
&& BeatmapSet != null
&& other.BeatmapSet != null
&& BeatmapSet.Hash == other.BeatmapSet.Hash
&& Metadata.AudioFile == other.Metadata.AudioFile;
public bool BackgroundEquals(RealmBeatmap? other) => other != null
&& BeatmapSet != null
&& other.BeatmapSet != null
&& BeatmapSet.Hash == other.BeatmapSet.Hash
&& Metadata.BackgroundFile == other.Metadata.BackgroundFile;
IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata;
IBeatmapSetInfo? IBeatmapInfo.BeatmapSet => BeatmapSet;
IRulesetInfo IBeatmapInfo.Ruleset => Ruleset;
IBeatmapDifficultyInfo IBeatmapInfo.Difficulty => Difficulty;
}
}

View File

@ -0,0 +1,43 @@
// 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.Game.Beatmaps;
using Realms;
#nullable enable
namespace osu.Game.Models
{
[MapTo("BeatmapDifficulty")]
public class RealmBeatmapDifficulty : EmbeddedObject, IBeatmapDifficultyInfo
{
public float DrainRate { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY;
public float CircleSize { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY;
public float OverallDifficulty { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY;
public float ApproachRate { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY;
public double SliderMultiplier { get; set; } = 1;
public double SliderTickRate { get; set; } = 1;
/// <summary>
/// Returns a shallow-clone of this <see cref="RealmBeatmapDifficulty"/>.
/// </summary>
public RealmBeatmapDifficulty Clone()
{
var diff = new RealmBeatmapDifficulty();
CopyTo(diff);
return diff;
}
public void CopyTo(RealmBeatmapDifficulty difficulty)
{
difficulty.ApproachRate = ApproachRate;
difficulty.DrainRate = DrainRate;
difficulty.CircleSize = CircleSize;
difficulty.OverallDifficulty = OverallDifficulty;
difficulty.SliderMultiplier = SliderMultiplier;
difficulty.SliderTickRate = SliderTickRate;
}
}
}

View 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;
using Newtonsoft.Json;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using Realms;
#nullable enable
namespace osu.Game.Models
{
[ExcludeFromDynamicCompile]
[Serializable]
[MapTo("BeatmapMetadata")]
public class RealmBeatmapMetadata : RealmObject, IBeatmapMetadataInfo
{
public string Title { get; set; } = string.Empty;
[JsonProperty("title_unicode")]
public string TitleUnicode { get; set; } = string.Empty;
public string Artist { get; set; } = string.Empty;
[JsonProperty("artist_unicode")]
public string ArtistUnicode { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty; // eventually should be linked to a persisted User. = string.Empty;
public string Source { get; set; } = string.Empty;
[JsonProperty(@"tags")]
public string Tags { get; set; } = string.Empty;
/// <summary>
/// The time in milliseconds to begin playing the track for preview purposes.
/// If -1, the track should begin playing at 40% of its length.
/// </summary>
public int PreviewTime { get; set; }
public string AudioFile { get; set; } = string.Empty;
public string BackgroundFile { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,78 @@
// 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.Linq;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using Realms;
#nullable enable
namespace osu.Game.Models
{
[ExcludeFromDynamicCompile]
[MapTo("BeatmapSet")]
public class RealmBeatmapSet : RealmObject, IHasGuidPrimaryKey, IHasRealmFiles, ISoftDelete, IEquatable<RealmBeatmapSet>, IBeatmapSetInfo
{
[PrimaryKey]
public Guid ID { get; set; } = Guid.NewGuid();
public int? OnlineID { get; set; }
public DateTimeOffset DateAdded { get; set; }
public IBeatmapMetadataInfo? Metadata => Beatmaps.FirstOrDefault()?.Metadata;
public IList<RealmBeatmap> Beatmaps { get; } = null!;
public IList<RealmNamedFileUsage> Files { get; } = null!;
public bool DeletePending { get; set; }
public string Hash { get; set; } = string.Empty;
/// <summary>
/// Whether deleting this beatmap set should be prohibited (due to it being a system requirement to be present).
/// </summary>
public bool Protected { get; set; }
public double MaxStarDifficulty => Beatmaps.Max(b => b.StarRating);
public double MaxLength => Beatmaps.Max(b => b.Length);
public double MaxBPM => Beatmaps.Max(b => b.BPM);
/// <summary>
/// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null.
/// The path returned is relative to the user file storage.
/// </summary>
/// <param name="filename">The name of the file to get the storage path of.</param>
public string? GetPathForFile(string filename) => Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.File.StoragePath;
public override string ToString() => Metadata?.ToString() ?? base.ToString();
public bool Equals(RealmBeatmapSet? other)
{
if (other == null)
return false;
if (IsManaged && other.IsManaged)
return ID == other.ID;
if (OnlineID.HasValue && other.OnlineID.HasValue)
return OnlineID == other.OnlineID;
if (!string.IsNullOrEmpty(Hash) && !string.IsNullOrEmpty(other.Hash))
return Hash == other.Hash;
return ReferenceEquals(this, other);
}
IEnumerable<IBeatmapInfo> IBeatmapSetInfo.Beatmaps => Beatmaps;
IEnumerable<INamedFileUsage> IBeatmapSetInfo.Files => Files;
}
}

View File

@ -0,0 +1,20 @@
// 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.IO;
using osu.Game.IO;
using Realms;
#nullable enable
namespace osu.Game.Models
{
[MapTo("File")]
public class RealmFile : RealmObject, IFileInfo
{
[PrimaryKey]
public string Hash { get; set; } = string.Empty;
public string StoragePath => Path.Combine(Hash.Remove(1), Hash.Remove(2), Hash);
}
}

View 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 JetBrains.Annotations;
using osu.Game.Database;
using osu.Game.IO;
using Realms;
#nullable enable
namespace osu.Game.Models
{
public class RealmNamedFileUsage : EmbeddedObject, INamedFile, INamedFileUsage
{
public RealmFile File { get; set; } = null!;
public string Filename { get; set; } = null!;
public RealmNamedFileUsage(RealmFile file, string filename)
{
File = file;
Filename = filename;
}
[UsedImplicitly]
private RealmNamedFileUsage()
{
}
IFileInfo INamedFileUsage.File => File;
}
}

View File

@ -0,0 +1,63 @@
// 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 JetBrains.Annotations;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using Realms;
#nullable enable
namespace osu.Game.Models
{
[ExcludeFromDynamicCompile]
[MapTo("Ruleset")]
public class RealmRuleset : RealmObject, IEquatable<RealmRuleset>, IRulesetInfo
{
[PrimaryKey]
public string ShortName { get; set; } = string.Empty;
public int? OnlineID { get; set; }
public string Name { get; set; } = string.Empty;
public string InstantiationInfo { get; set; } = string.Empty;
public RealmRuleset(string shortName, string name, string instantiationInfo, int? onlineID = null)
{
ShortName = shortName;
Name = name;
InstantiationInfo = instantiationInfo;
OnlineID = onlineID;
}
[UsedImplicitly]
private RealmRuleset()
{
}
public RealmRuleset(int? onlineID, string name, string shortName, bool available)
{
OnlineID = onlineID;
Name = name;
ShortName = shortName;
Available = available;
}
public bool Available { get; set; }
public bool Equals(RealmRuleset? other) => other != null && OnlineID == other.OnlineID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo;
public override string ToString() => Name;
public RealmRuleset Clone() => new RealmRuleset
{
OnlineID = OnlineID,
Name = Name,
ShortName = ShortName,
InstantiationInfo = InstantiationInfo,
Available = Available
};
}
}