Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new read-only interfaces for all remaining model types #14917

Merged
merged 20 commits into from
Oct 6, 2021
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
619dfe0
Add new interface base types for models
peppy Oct 1, 2021
d309636
Update all EF based models to implement new read only interfaces
peppy Oct 1, 2021
8595eb2
Switch `BeatmapDifficulty` usages to use interface type
peppy Oct 1, 2021
05996cc
Add changes that got forgotted in branch surgery
peppy Oct 1, 2021
00e33a1
Fix incorrect `OnlineID` mappings
peppy Oct 1, 2021
9dae92e
Add missing backlink to `BeatmapSet` from `Beatmap` and fix non-expli…
peppy Oct 1, 2021
d6618a9
Redirect more methods to interface implementations
peppy Oct 1, 2021
0daf893
Add missing xmldoc
peppy Oct 1, 2021
dcd7d7a
Add `JsonIgnore` rule for `StoryboardFile`
peppy Oct 1, 2021
8bfdfe3
Add literal string marker
peppy Oct 4, 2021
4df5f93
Inline single usage of `StoryboardFile` to avoid interface default me…
peppy Oct 4, 2021
fd6b106
Add TODO reminder about ruleset reference transfer quirk
peppy Oct 4, 2021
51b7dce
Remove reference to `osu-web-10`
peppy Oct 4, 2021
f293e00
Move `BeatmapInfo`'s `SearchableTerms` implementation to interface
peppy Oct 4, 2021
1d99bc2
Merge branch 'master' into new-interfaces
peppy Oct 4, 2021
d3b9660
Move common interface implementations to extension methods
peppy Oct 5, 2021
a5aa328
Remove null check suppression and add non-null fallback
peppy Oct 5, 2021
4d56969
Remove unnecessary access modifier in interface
peppy Oct 5, 2021
4f59fc1
Mark `BeatmapSet` as nullable for the time being
peppy Oct 5, 2021
4bbff2e
Merge branch 'master' into new-interfaces
smoogipoo Oct 6, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat
return new CatchDifficultyAttributes { Mods = mods, Skills = skills };

// this is the same as osu!, so there's potential to share the implementation... maybe
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;

return new CatchDifficultyAttributes
{
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, B
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);

TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);

Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public DrawableCatchRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod
: base(ruleset, beatmap, mods)
{
Direction.Value = ScrollingDirection.Down;
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
}

protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat

double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0;

double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;

int maxCombo = beatmap.HitObjects.Count;
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, B
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);

TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN);
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN);

// Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above.
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Osu/Objects/Spinner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, B

double secondsDuration = Duration / 1000;

double minimumRotationsPerSecond = stable_matching_fudge * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5);
double minimumRotationsPerSecond = stable_matching_fudge * IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5);

SpinsRequired = (int)(secondsDuration * minimumRotationsPerSecond);
MaximumBonusSpins = (int)((maximum_rotations_per_second - minimumRotationsPerSecond) * secondsDuration);
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ protected override IEnumerable<TaikoHitObject> ConvertHitObject(HitObject obj, I

case IHasDuration endTimeData:
{
double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
double hitMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;

yield return new Swell
{
Expand Down
4 changes: 2 additions & 2 deletions osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);

hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType<Hit>().Count()) * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType<Hit>().Count()) * IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
hpMissMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
}

protected override double GetHealthIncreaseFor(JudgementResult result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void TestRomanisation()
Title = "Romanised title",
TitleUnicode = "Unicode Title"
};
var romanisableString = metadata.ToRomanisableString();
var romanisableString = metadata.GetDisplayTitleRomanisable();

Assert.AreEqual(metadata.ToString(), romanisableString.Romanised);
Assert.AreEqual($"{metadata.ArtistUnicode} - {metadata.TitleUnicode}", romanisableString.Original);
Expand All @@ -33,7 +33,7 @@ public void TestRomanisationNoUnicode()
Artist = "Romanised Artist",
Title = "Romanised title"
};
var romanisableString = metadata.ToRomanisableString();
var romanisableString = metadata.GetDisplayTitleRomanisable();

Assert.AreEqual(romanisableString.Romanised, romanisableString.Original);
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Tournament/Components/SongBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ private void update()
if ((mods & LegacyMods.DoubleTime) > 0)
{
// temporary local calculation (taken from OsuDifficultyCalculator)
double preempt = (int)BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / 1.5;
double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(ar, 1800, 1200, 450) / 1.5;
ar = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5);

bpm *= 1.5f;
Expand Down
44 changes: 1 addition & 43 deletions osu.Game/Beatmaps/BeatmapDifficulty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace osu.Game.Beatmaps
{
public class BeatmapDifficulty : IHasPrimaryKey
public class BeatmapDifficulty : IHasPrimaryKey, IBeatmapDifficultyInfo
{
/// <summary>
/// The default value used for all difficulty settings except <see cref="SliderMultiplier"/> and <see cref="SliderTickRate"/>.
Expand Down Expand Up @@ -49,47 +49,5 @@ public void CopyTo(BeatmapDifficulty difficulty)
difficulty.SliderMultiplier = SliderMultiplier;
difficulty.SliderTickRate = SliderTickRate;
}

/// <summary>
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.
/// </summary>
/// <param name="difficulty">The difficulty value to be mapped.</param>
/// <param name="min">Minimum of the resulting range which will be achieved by a difficulty value of 0.</param>
/// <param name="mid">Midpoint of the resulting range which will be achieved by a difficulty value of 5.</param>
/// <param name="max">Maximum of the resulting range which will be achieved by a difficulty value of 10.</param>
/// <returns>Value to which the difficulty value maps in the specified range.</returns>
public static double DifficultyRange(double difficulty, double min, double mid, double max)
{
if (difficulty > 5)
return mid + (max - mid) * (difficulty - 5) / 5;
if (difficulty < 5)
return mid - (mid - min) * (5 - difficulty) / 5;

return mid;
}

/// <summary>
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.
/// </summary>
/// <param name="difficulty">The difficulty value to be mapped.</param>
/// <param name="range">The values that define the two linear ranges.
/// <list type="table">
/// <item>
/// <term>od0</term>
/// <description>Minimum of the resulting range which will be achieved by a difficulty value of 0.</description>
/// </item>
/// <item>
/// <term>od5</term>
/// <description>Midpoint of the resulting range which will be achieved by a difficulty value of 5.</description>
/// </item>
/// <item>
/// <term>od10</term>
/// <description>Maximum of the resulting range which will be achieved by a difficulty value of 10.</description>
/// </item>
/// </list>
/// </param>
/// <returns>Value to which the difficulty value maps in the specified range.</returns>
public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range)
=> DifficultyRange(difficulty, range.od0, range.od5, range.od10);
}
}
33 changes: 19 additions & 14 deletions osu.Game/Beatmaps/BeatmapInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Localisation;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Rulesets;
Expand All @@ -17,7 +16,7 @@ namespace osu.Game.Beatmaps
{
[ExcludeFromDynamicCompile]
[Serializable]
public class BeatmapInfo : IEquatable<BeatmapInfo>, IHasPrimaryKey
public class BeatmapInfo : IEquatable<BeatmapInfo>, IHasPrimaryKey, IBeatmapInfo
{
public int ID { get; set; }

Expand Down Expand Up @@ -152,18 +151,7 @@ public string StoredBookmarks
[JsonIgnore]
public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(StarDifficulty);

public string[] SearchableTerms => new[]
{
Version
}.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty<string>()).Where(s => !string.IsNullOrEmpty(s)).ToArray();

public override string ToString() => $"{Metadata ?? BeatmapSet?.Metadata} {versionString}".Trim();

public RomanisableString ToRomanisableString()
{
var metadata = (Metadata ?? BeatmapSet?.Metadata)?.ToRomanisableString() ?? new RomanisableString(null, null);
return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim());
}
public override string ToString() => this.GetDisplayTitle();

public bool Equals(BeatmapInfo other)
{
Expand All @@ -187,5 +175,22 @@ public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet !
/// Returns a shallow-clone of this <see cref="BeatmapInfo"/>.
/// </summary>
public BeatmapInfo Clone() => (BeatmapInfo)MemberwiseClone();

#region Implementation of IHasOnlineID

public int? OnlineID => OnlineBeatmapID;

#endregion

#region Implementation of IBeatmapInfo

string IBeatmapInfo.DifficultyName => Version;
IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata;
IBeatmapDifficultyInfo IBeatmapInfo.Difficulty => BaseDifficulty;
IBeatmapSetInfo IBeatmapInfo.BeatmapSet => BeatmapSet;
IRulesetInfo IBeatmapInfo.Ruleset => Ruleset;
double IBeatmapInfo.StarRating => StarDifficulty;

#endregion
}
}
38 changes: 38 additions & 0 deletions osu.Game/Beatmaps/BeatmapInfoExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Linq;
using osu.Framework.Localisation;

namespace osu.Game.Beatmaps
{
public static class BeatmapInfoExtensions
{
/// <summary>
/// A user-presentable display title representing this beatmap.
/// </summary>
public static string GetDisplayTitle(this IBeatmapInfo beatmapInfo) => $"{getClosestMetadata(beatmapInfo)} {getVersionString(beatmapInfo)}".Trim();

/// <summary>
/// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields.
/// </summary>
public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapInfo beatmapInfo)
{
var metadata = getClosestMetadata(beatmapInfo).GetDisplayTitleRomanisable();
var versionString = getVersionString(beatmapInfo);

return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim());
}

public static string[] GetSearchableTerms(this IBeatmapInfo beatmapInfo) => new[]
{
beatmapInfo.DifficultyName
}.Concat(getClosestMetadata(beatmapInfo).GetSearchableTerms()).Where(s => !string.IsNullOrEmpty(s)).ToArray();

private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]";

// temporary helper methods until we figure which metadata should be where.
private static IBeatmapMetadataInfo getClosestMetadata(IBeatmapInfo beatmapInfo) =>
beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet?.Metadata ?? new BeatmapMetadata();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure whether this should be a

Suggested change
beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet?.Metadata ?? new BeatmapMetadata();
beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet.Metadata ?? new BeatmapMetadata();

as the inspection suggests, or if BeatmapSet should be marked nullable on the interface. There are several other places that nullcheck the property so probably safest to go with the latter for now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think nullable is the safest option for now, yep. Will require revisiting once things are locked in place.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what it's worth, I made this change due to an actual null ref, so it can happen.

}
}
49 changes: 5 additions & 44 deletions osu.Game/Beatmaps/BeatmapMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Localisation;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Users;
Expand All @@ -15,7 +13,7 @@ namespace osu.Game.Beatmaps
{
[ExcludeFromDynamicCompile]
[Serializable]
public class BeatmapMetadata : IEquatable<BeatmapMetadata>, IHasPrimaryKey
public class BeatmapMetadata : IEquatable<BeatmapMetadata>, IHasPrimaryKey, IBeatmapMetadataInfo
{
public int ID { get; set; }

Expand Down Expand Up @@ -83,50 +81,13 @@ public string AuthorString
public int PreviewTime { get; set; }

public string AudioFile { get; set; }
public string BackgroundFile { get; set; }

public override string ToString()
{
string author = Author == null ? string.Empty : $"({Author})";
return $"{Artist} - {Title} {author}".Trim();
}
public string BackgroundFile { get; set; }

public RomanisableString ToRomanisableString()
{
string author = Author == null ? string.Empty : $"({Author})";
var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode;
var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode;
public bool Equals(BeatmapMetadata other) => ((IBeatmapMetadataInfo)this).Equals(other);

return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim());
}
public override string ToString() => this.GetDisplayTitle();

[JsonIgnore]
public string[] SearchableTerms => new[]
{
Author?.Username,
Artist,
ArtistUnicode,
Title,
TitleUnicode,
Source,
Tags
}.Where(s => !string.IsNullOrEmpty(s)).ToArray();

public bool Equals(BeatmapMetadata other)
{
if (other == null)
return false;

return Title == other.Title
&& TitleUnicode == other.TitleUnicode
&& Artist == other.Artist
&& ArtistUnicode == other.ArtistUnicode
&& AuthorString == other.AuthorString
&& Source == other.Source
&& Tags == other.Tags
&& PreviewTime == other.PreviewTime
&& AudioFile == other.AudioFile
&& BackgroundFile == other.BackgroundFile;
}
string IBeatmapMetadataInfo.Author => AuthorString;
}
}
46 changes: 46 additions & 0 deletions osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Linq;
using osu.Framework.Localisation;

namespace osu.Game.Beatmaps
{
public static class BeatmapMetadataInfoExtensions
{
/// <summary>
/// An array of all searchable terms provided in contained metadata.
/// </summary>
public static string[] GetSearchableTerms(this IBeatmapMetadataInfo metadataInfo) => new[]
{
metadataInfo.Author,
metadataInfo.Artist,
metadataInfo.ArtistUnicode,
metadataInfo.Title,
metadataInfo.TitleUnicode,
metadataInfo.Source,
metadataInfo.Tags
}.Where(s => !string.IsNullOrEmpty(s)).ToArray();

/// <summary>
/// A user-presentable display title representing this metadata.
/// </summary>
public static string GetDisplayTitle(this IBeatmapMetadataInfo metadataInfo)
{
string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})";
return $"{metadataInfo.Artist} - {metadataInfo.Title} {author}".Trim();
}

/// <summary>
/// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields.
/// </summary>
public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo)
{
string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})";
var artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode;
var titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode;

return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{metadataInfo.Artist} - {metadataInfo.Title} {author}".Trim());
}
}
}
4 changes: 3 additions & 1 deletion osu.Game/Beatmaps/BeatmapSetFileInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace osu.Game.Beatmaps
{
public class BeatmapSetFileInfo : INamedFileInfo, IHasPrimaryKey
public class BeatmapSetFileInfo : INamedFileInfo, IHasPrimaryKey, INamedFileUsage
bdach marked this conversation as resolved.
Show resolved Hide resolved
{
public int ID { get; set; }

Expand All @@ -19,5 +19,7 @@ public class BeatmapSetFileInfo : INamedFileInfo, IHasPrimaryKey

[Required]
public string Filename { get; set; }

public IFileInfo File => FileInfo;
}
}
Loading