diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 965cc43815..34780082da 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -42,6 +42,8 @@ namespace osu.Game.Beatmaps private readonly WorkingBeatmapCache workingBeatmapCache; + private readonly LegacyBeatmapExporter beatmapExporter; + public Action<(BeatmapSetInfo beatmapSet, bool isBatch)>? ProcessBeatmap { private get; set; } public BeatmapManager(Storage storage, RealmAccess realm, IAPIProvider? api, AudioManager audioManager, IResourceStore gameResources, GameHost? host = null, @@ -66,6 +68,11 @@ namespace osu.Game.Beatmaps beatmapImporter.PostNotification = obj => PostNotification?.Invoke(obj); workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host); + + beatmapExporter = new LegacyBeatmapExporter(storage, realm) + { + PostNotification = obj => PostNotification?.Invoke(obj) + }; } protected virtual WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap? defaultBeatmap, @@ -446,6 +453,8 @@ namespace osu.Game.Beatmaps public Task?> ImportAsUpdate(ProgressNotification notification, ImportTask importTask, BeatmapSetInfo original) => beatmapImporter.ImportAsUpdate(notification, importTask, original); + public void Export(BeatmapSetInfo beatmap) => Task.Run(() => beatmapExporter.ExportAsync(beatmap)); + private void updateHashAndMarkDirty(BeatmapSetInfo setInfo) { setInfo.Hash = beatmapImporter.ComputeHash(setInfo); diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index 22bccbcda6..6349ebde2c 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -3,14 +3,13 @@ using osu.Framework.Platform; using osu.Game.Beatmaps; -using osu.Game.Overlays; namespace osu.Game.Database { public class LegacyBeatmapExporter : LegacyModelExporter { - public LegacyBeatmapExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null) - : base(storage, realm, notifications) + public LegacyBeatmapExporter(Storage storage, RealmAccess realm) + : base(storage, realm) { } diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs index 18e840fedd..5150651b15 100644 --- a/osu.Game/Database/LegacyModelExporter.cs +++ b/osu.Game/Database/LegacyModelExporter.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Extensions; -using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using osu.Game.Utils; using Realms; @@ -33,26 +32,16 @@ namespace osu.Game.Database protected RealmAccess RealmAccess; - private readonly ProgressNotification notification; - private bool canCancel = true; - private readonly INotificationOverlay? notifications; + private string filename = string.Empty; + public Action? PostNotification { get; set; } - protected LegacyModelExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null) + protected LegacyModelExporter(Storage storage, RealmAccess realm) { exportStorage = storage.GetStorageForDirectory(@"exports"); UserFileStorage = storage.GetStorageForDirectory(@"files"); RealmAccess = realm; - - this.notifications = notifications; - notification = new ProgressNotification - { - State = ProgressNotificationState.Active, - Text = "Exporting...", - CompletionText = "Export completed" - }; - notification.CancelRequested += () => canCancel; } /// @@ -62,13 +51,9 @@ namespace osu.Game.Database /// public async Task ExportAsync(TModel model) { - notifications?.Post(notification); - string itemFilename = model.GetDisplayString().GetValidFilename(); IEnumerable existingExports = exportStorage.GetFiles("", $"{itemFilename}*{FileExtension}"); - string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); - - notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename); + filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); using (var stream = exportStorage.CreateFileSafely(filename)) { @@ -77,22 +62,50 @@ namespace osu.Game.Database } /// - /// Export te model corresponding to model to given stream. + /// Export model to stream. /// /// The medel which have . /// The stream to export. /// public async Task ExportToStreamAsync(TModel model, Stream stream) { + ProgressNotification notification = new ProgressNotification + { + State = ProgressNotificationState.Active, + Text = "Exporting...", + CompletionText = "Export completed" + }; + notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename); + notification.CancelRequested += () => canCancel; + PostNotification?.Invoke(notification); + canCancel = true; + Guid id = model.ID; await Task.Run(() => { RealmAccess.Run(r => { TModel refetchModel = r.Find(id); - ExportToStream(refetchModel, stream); + ExportToStream(refetchModel, stream, notification); }); - }).ContinueWith(onComplete); + }).ContinueWith(t => + { + if (t.IsFaulted) + { + notification.State = ProgressNotificationState.Cancelled; + Logger.Error(t.Exception, "An error occurred while exporting"); + + return; + } + + if (notification.CancellationToken.IsCancellationRequested) + { + return; + } + + notification.CompletionText = "Export Complete, Click to open the folder"; + notification.State = ProgressNotificationState.Completed; + }); } /// @@ -101,14 +114,16 @@ namespace osu.Game.Database /// /// The item to export. /// The output stream to export to. - protected virtual void ExportToStream(TModel model, Stream outputStream) => exportZipArchive(model, outputStream); + /// The notification will displayed to the user + protected virtual void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification) => exportZipArchive(model, outputStream, notification); /// /// Exports an item to Stream as a legacy (.zip based) package. /// /// The item to export. /// The output stream to export to. - private void exportZipArchive(TModel model, Stream outputStream) + /// The notification will displayed to the user + private void exportZipArchive(TModel model, Stream outputStream, ProgressNotification notification) { using (var archive = ZipArchive.Create()) { @@ -129,24 +144,5 @@ namespace osu.Game.Database archive.SaveTo(outputStream); } } - - private void onComplete(Task t) - { - if (t.IsFaulted) - { - notification.State = ProgressNotificationState.Cancelled; - Logger.Error(t.Exception, "An error occurred while exporting"); - - return; - } - - if (notification.CancellationToken.IsCancellationRequested) - { - return; - } - - notification.CompletionText = "Export Complete, Click to open the folder"; - notification.State = ProgressNotificationState.Completed; - } } } diff --git a/osu.Game/Database/LegacyScoreExporter.cs b/osu.Game/Database/LegacyScoreExporter.cs index eb46bc6db2..1a30d823e1 100644 --- a/osu.Game/Database/LegacyScoreExporter.cs +++ b/osu.Game/Database/LegacyScoreExporter.cs @@ -5,21 +5,21 @@ using System.IO; using System.Linq; using osu.Framework.Platform; using osu.Game.Extensions; -using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; using osu.Game.Scoring; namespace osu.Game.Database { public class LegacyScoreExporter : LegacyModelExporter { - public LegacyScoreExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null) - : base(storage, realm, notifications) + public LegacyScoreExporter(Storage storage, RealmAccess realm) + : base(storage, realm) { } protected override string FileExtension => ".osr"; - protected override void ExportToStream(ScoreInfo model, Stream stream) + protected override void ExportToStream(ScoreInfo model, Stream stream, ProgressNotification notification) { var file = model.Files.SingleOrDefault(); if (file == null) diff --git a/osu.Game/Database/LegacySkinExporter.cs b/osu.Game/Database/LegacySkinExporter.cs index 6e65963f29..52d0e56cbf 100644 --- a/osu.Game/Database/LegacySkinExporter.cs +++ b/osu.Game/Database/LegacySkinExporter.cs @@ -2,15 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Platform; -using osu.Game.Overlays; using osu.Game.Skinning; namespace osu.Game.Database { public class LegacySkinExporter : LegacyModelExporter { - public LegacySkinExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null) - : base(storage, realm, notifications) + public LegacySkinExporter(Storage storage, RealmAccess realm) + : base(storage, realm) { } diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index f0457de2dc..e5edf90d1a 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -18,7 +17,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Localisation; -using osu.Game.Database; using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -34,7 +32,6 @@ using osuTK.Graphics; using osu.Game.Online.API; using osu.Game.Resources.Localisation.Web; using osu.Game.Utils; -using osu.Framework.Platform; namespace osu.Game.Online.Leaderboards { @@ -73,18 +70,11 @@ namespace osu.Game.Online.Leaderboards [Resolved(canBeNull: true)] private SongSelect songSelect { get; set; } - [Resolved] - private Storage storage { get; set; } - - [Resolved] - private RealmAccess realm { get; set; } - - [Resolved(canBeNull: true)] - private INotificationOverlay notifications { get; set; } - public ITooltip GetCustomTooltip() => new LeaderboardScoreTooltip(); public virtual ScoreInfo TooltipContent => Score; + private ScoreManager scoreManager = null!; + public LeaderboardScore(ScoreInfo score, int? rank, bool isOnlineScope = true) { Score = score; @@ -103,6 +93,8 @@ namespace osu.Game.Online.Leaderboards statisticsLabels = GetStatistics(Score).Select(s => new ScoreComponentLabel(s)).ToList(); + this.scoreManager = scoreManager; + ClickableAvatar innerAvatar; Children = new Drawable[] @@ -434,7 +426,7 @@ namespace osu.Game.Online.Leaderboards if (Score.Files.Count > 0) { - items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => Task.Run(() => new LegacyScoreExporter(storage, realm, notifications).ExportAsync(Score)))); + items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => scoreManager.Export(Score))); items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score)))); } diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 9400e803c8..6d338df2fe 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Logging; -using osu.Framework.Platform; using osu.Game.Database; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; @@ -138,15 +137,6 @@ namespace osu.Game.Overlays.Settings.Sections [Resolved] private SkinManager skins { get; set; } - [Resolved] - private Storage storage { get; set; } - - [Resolved] - private RealmAccess realm { get; set; } - - [Resolved(canBeNull: true)] - private INotificationOverlay notifications { get; set; } - private Bindable currentSkin; [BackgroundDependencyLoader] @@ -168,7 +158,7 @@ namespace osu.Game.Overlays.Settings.Sections { try { - currentSkin.Value.SkinInfo.PerformRead(s => new LegacySkinExporter(storage, realm, notifications).ExportAsync(s)); + skins.ExporCurrenttSkin(); } catch (Exception e) { diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index b2944ad219..525ff58778 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -27,6 +27,7 @@ namespace osu.Game.Scoring { private readonly OsuConfigManager configManager; private readonly ScoreImporter scoreImporter; + private readonly LegacyScoreExporter scoreExporter; public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, RealmAccess realm, IAPIProvider api, OsuConfigManager configManager = null) @@ -38,6 +39,11 @@ namespace osu.Game.Scoring { PostNotification = obj => PostNotification?.Invoke(obj) }; + + scoreExporter = new LegacyScoreExporter(storage, realm) + { + PostNotification = obj => PostNotification?.Invoke(obj) + }; } public Score GetScore(ScoreInfo score) => scoreImporter.GetScore(score); @@ -177,6 +183,8 @@ namespace osu.Game.Scoring public Task>> Import(ProgressNotification notification, params ImportTask[] tasks) => scoreImporter.Import(notification, tasks); + public void Export(ScoreInfo score) => Task.Run(() => scoreExporter.ExportAsync(score)); + public Task> ImportAsUpdate(ProgressNotification notification, ImportTask task, ScoreInfo original) => scoreImporter.ImportAsUpdate(notification, task, original); public Live Import(ScoreInfo item, ArchiveReader archive = null, bool batchImport = false, CancellationToken cancellationToken = default) => diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 342ee2e219..69bcda69c1 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework; using osu.Framework.Allocation; @@ -21,7 +20,6 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Logging; -using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Framework.Threading; @@ -30,7 +28,6 @@ using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; -using osu.Game.Database; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; @@ -89,12 +86,6 @@ namespace osu.Game.Screens.Edit [Resolved] private RulesetStore rulesets { get; set; } - [Resolved] - private Storage storage { get; set; } - - [Resolved] - private RealmAccess realm { get; set; } - [Resolved(canBeNull: true)] private IDialogOverlay dialogOverlay { get; set; } @@ -958,7 +949,7 @@ namespace osu.Game.Screens.Edit private void exportBeatmap() { Save(); - Task.Run(() => new LegacyBeatmapExporter(storage, realm, notifications).ExportAsync(Beatmap.Value.BeatmapSetInfo)); + beatmapManager.Export(Beatmap.Value.BeatmapSetInfo); } /// diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 4a5277f3bf..65a602b764 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -58,6 +58,8 @@ namespace osu.Game.Skinning private readonly SkinImporter skinImporter; + private readonly LegacySkinExporter skinExporter; + private readonly IResourceStore userFiles; private Skin argonSkin { get; } @@ -109,6 +111,11 @@ namespace osu.Game.Skinning SourceChanged?.Invoke(); }; + + skinExporter = new LegacySkinExporter(storage, realm) + { + PostNotification = obj => PostNotification?.Invoke(obj) + }; } public void SelectRandomSkin() @@ -280,6 +287,8 @@ namespace osu.Game.Skinning public Task> Import(ImportTask task, bool batchImport = false, CancellationToken cancellationToken = default) => skinImporter.Import(task, batchImport, cancellationToken); + public void ExporCurrenttSkin() => CurrentSkinInfo.Value.PerformRead(s => skinExporter.ExportAsync(s)); + #endregion public void Delete([CanBeNull] Expression> filter = null, bool silent = false)