From ce660b6d67499f74908cc26f61a8431787c17be8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Nov 2018 19:01:22 +0900 Subject: [PATCH] Add skin de-duplication --- osu.Game/Beatmaps/BeatmapManager.cs | 14 +----- osu.Game/Database/ArchiveModelManager.cs | 6 +++ osu.Game/Database/IHasFiles.cs | 2 + osu.Game/Skinning/SkinInfo.cs | 6 ++- osu.Game/Skinning/SkinManager.cs | 64 +++++++++++------------- 5 files changed, 43 insertions(+), 49 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 24c68d392b..c4cbce6430 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -129,18 +129,8 @@ private void validateOnlineIds(List beatmaps) beatmaps.ForEach(b => b.OnlineBeatmapID = null); } - protected override BeatmapSetInfo CheckForExisting(BeatmapSetInfo model) - { - // check if this beatmap has already been imported and exit early if so - var existingHashMatch = beatmaps.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash); - if (existingHashMatch != null) - { - Undelete(existingHashMatch); - return existingHashMatch; - } - - return null; - } + protected override BeatmapSetInfo CheckForExisting(BeatmapSetInfo model) => + beatmaps.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash); /// /// Downloads a beatmap. diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 3686b702c4..2d5cbac8f1 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -235,6 +235,7 @@ public TModel Import(TModel item, ArchiveReader archive = null) if (existing != null) { + Undelete(existing); Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database); return existing; } @@ -471,6 +472,11 @@ protected virtual void Populate(TModel model, [CanBeNull] ArchiveReader archive) { } + /// + /// Check whether an existing model already exists for a new import item. + /// + /// The new model proposed for import. Note that has not yet been run on this model. + /// An existing model which matches the criteria to skip importing, else null. protected virtual TModel CheckForExisting(TModel model) => null; private DbSet queryModel() => ContextFactory.Get().Set(); diff --git a/osu.Game/Database/IHasFiles.cs b/osu.Game/Database/IHasFiles.cs index 3aaba37efc..f6044cd7e5 100644 --- a/osu.Game/Database/IHasFiles.cs +++ b/osu.Game/Database/IHasFiles.cs @@ -14,5 +14,7 @@ public interface IHasFiles { List Files { get; set; } + + string Hash { get; set; } } } diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 47dad60d88..0014f8b3a0 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -15,16 +15,20 @@ public class SkinInfo : IHasFiles, IEquatable, IHasPrima public string Name { get; set; } + public string Hash { get; set; } + public string Creator { get; set; } public List Files { get; set; } public bool DeletePending { get; set; } + public string FullName => $"\"{Name}\" by {Creator}"; + public static SkinInfo Default { get; } = new SkinInfo { Name = "osu!lazer", Creator = "team osu!" }; public bool Equals(SkinInfo other) => other != null && ID == other.ID; - public override string ToString() => $"\"{Name}\" by {Creator}"; + public override string ToString() => FullName; } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index bd694e443a..b41b424661 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -28,6 +28,28 @@ public class SkinManager : ArchiveModelManager, ISkinSou protected override string ImportFromStablePath => "Skins"; + public SkinManager(Storage storage, DatabaseContextFactory contextFactory, IIpcHost importHost, AudioManager audio) + : base(storage, contextFactory, new SkinStore(contextFactory, storage), importHost) + { + this.audio = audio; + + ItemRemoved += removedInfo => + { + // check the removed skin is not the current user choice. if it is, switch back to default. + if (removedInfo.ID == CurrentSkinInfo.Value.ID) + CurrentSkinInfo.Value = SkinInfo.Default; + }; + + CurrentSkinInfo.ValueChanged += info => CurrentSkin.Value = getSkin(info); + CurrentSkin.ValueChanged += skin => + { + if (skin.SkinInfo != CurrentSkinInfo.Value) + throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead."); + + SourceChanged?.Invoke(); + }; + } + /// /// Returns a list of all usable s. Includes the special default skin plus all skins from . /// @@ -45,24 +67,16 @@ public List GetAllUsableSkins() /// A list of available . public List GetAllUserSkins() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); - protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo - { - Name = archive.Name - }; + protected override SkinInfo CheckForExisting(SkinInfo model) + => ModelStore.ConsumableItems.FirstOrDefault(s => s.Name == model.Name && s.Creator == model.Creator); + + protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name }; protected override void Populate(SkinInfo model, ArchiveReader archive) { base.Populate(model, archive); - populate(model); - } - /// - /// Populate a from its (if possible). - /// - /// - private void populate(SkinInfo model) - { - Skin reference = GetSkin(model); + Skin reference = getSkin(model); if (!string.IsNullOrEmpty(reference.Configuration.SkinInfo.Name)) { model.Name = reference.Configuration.SkinInfo.Name; @@ -80,7 +94,7 @@ private void populate(SkinInfo model) /// /// The skin to lookup. /// A instance correlating to the provided . - public Skin GetSkin(SkinInfo skinInfo) + private Skin getSkin(SkinInfo skinInfo) { if (skinInfo == SkinInfo.Default) return new DefaultSkin(); @@ -88,28 +102,6 @@ public Skin GetSkin(SkinInfo skinInfo) return new LegacySkin(skinInfo, Files.Store, audio); } - public SkinManager(Storage storage, DatabaseContextFactory contextFactory, IIpcHost importHost, AudioManager audio) - : base(storage, contextFactory, new SkinStore(contextFactory, storage), importHost) - { - this.audio = audio; - - ItemRemoved += removedInfo => - { - // check the removed skin is not the current user choice. if it is, switch back to default. - if (removedInfo.ID == CurrentSkinInfo.Value.ID) - CurrentSkinInfo.Value = SkinInfo.Default; - }; - - CurrentSkinInfo.ValueChanged += info => CurrentSkin.Value = GetSkin(info); - CurrentSkin.ValueChanged += skin => - { - if (skin.SkinInfo != CurrentSkinInfo.Value) - throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead."); - - SourceChanged?.Invoke(); - }; - } - /// /// Perform a lookup query on available s. ///