From 1f3e1b2d974f5e4c2fd1383963b744408f99ed9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Jun 2022 18:07:04 +0900 Subject: [PATCH] Combine `BeatmapManager` and `BeatmapModelManager` into one class --- ...eneOnlinePlayBeatmapAvailabilityTracker.cs | 8 +- osu.Game/Beatmaps/BeatmapManager.cs | 192 ++++++++++++++---- osu.Game/Beatmaps/BeatmapModelManager.cs | 149 -------------- .../Database/RealmArchiveModelImporter.cs | 2 +- osu.Game/Tests/Visual/EditorTestScene.cs | 4 +- 5 files changed, 154 insertions(+), 201 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapModelManager.cs diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 4f255ddd00..915ad50d2b 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -210,16 +210,16 @@ public TestBeatmapManager(Storage storage, RealmAccess realm, RulesetStore rules { } - protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue) + protected override BeatmapImporter CreateBeatmapImporter(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue) { - return new TestBeatmapModelManager(this, storage, realm, onlineLookupQueue); + return new TestBeatmapImporter(this, storage, realm, onlineLookupQueue); } - internal class TestBeatmapModelManager : BeatmapModelManager + internal class TestBeatmapImporter : BeatmapImporter { private readonly TestBeatmapManager testBeatmapManager; - public TestBeatmapModelManager(TestBeatmapManager testBeatmapManager, Storage storage, RealmAccess databaseAccess, BeatmapOnlineLookupQueue beatmapOnlineLookupQueue) + public TestBeatmapImporter(TestBeatmapManager testBeatmapManager, Storage storage, RealmAccess databaseAccess, BeatmapOnlineLookupQueue beatmapOnlineLookupQueue) : base(databaseAccess, storage, beatmapOnlineLookupQueue) { this.testBeatmapManager = testBeatmapManager; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 9397c2f3b2..63bd7dc710 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -1,19 +1,26 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; +using System.Text; using System.Threading; using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Audio.Track; +using osu.Framework.Extensions; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Game.Beatmaps.Formats; using osu.Game.Database; +using osu.Game.Extensions; using osu.Game.IO.Archives; using osu.Game.Models; using osu.Game.Online.API; @@ -23,8 +30,6 @@ using osu.Game.Skinning; using osu.Game.Utils; -#nullable enable - namespace osu.Game.Beatmaps { /// @@ -35,14 +40,15 @@ public class BeatmapManager : IModelManager, IModelFileManager gameResources, GameHost? host = null, WorkingBeatmap? defaultBeatmap = null, bool performOnlineLookups = false) + public BeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, IAPIProvider? api, AudioManager audioManager, IResourceStore gameResources, GameHost? host = null, + WorkingBeatmap? defaultBeatmap = null, bool performOnlineLookups = false) { this.realm = realm; @@ -58,19 +64,18 @@ public BeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapTrackStore = audioManager.GetTrackStore(userResources); - beatmapModelManager = CreateBeatmapModelManager(storage, realm, rulesets, onlineBeatmapLookupQueue); + beatmapImporter = CreateBeatmapImporter(storage, realm, rulesets, onlineBeatmapLookupQueue); workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host); - - beatmapModelManager.WorkingBeatmapCache = workingBeatmapCache; } - protected virtual WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap? defaultBeatmap, GameHost? host) + protected virtual WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap? defaultBeatmap, + GameHost? host) { return new WorkingBeatmapCache(BeatmapTrackStore, audioManager, resources, storage, defaultBeatmap, host); } - protected virtual BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue? onlineLookupQueue) => - new BeatmapModelManager(realm, storage, onlineLookupQueue); + protected virtual BeatmapImporter CreateBeatmapImporter(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue? onlineLookupQueue) => + new BeatmapImporter(realm, storage, onlineLookupQueue); /// /// Create a new beatmap set, backed by a model, @@ -99,7 +104,7 @@ public WorkingBeatmap CreateNew(RulesetInfo ruleset, APIUser user) foreach (BeatmapInfo b in beatmapSet.Beatmaps) b.BeatmapSet = beatmapSet; - var imported = beatmapModelManager.Import(beatmapSet); + var imported = beatmapImporter.Import(beatmapSet); if (imported == null) throw new InvalidOperationException("Failed to import new beatmap"); @@ -169,12 +174,12 @@ public virtual WorkingBeatmap CopyExistingDifficulty(BeatmapSetInfo targetBeatma private WorkingBeatmap addDifficultyToSet(BeatmapSetInfo targetBeatmapSet, IBeatmap newBeatmap, ISkin beatmapSkin) { // populate circular beatmap set info <-> beatmap info references manually. - // several places like `BeatmapModelManager.Save()` or `GetWorkingBeatmap()` + // several places like `BeatmapImporter.Save()` or `GetWorkingBeatmap()` // rely on them being freely traversable in both directions for correct operation. targetBeatmapSet.Beatmaps.Add(newBeatmap.BeatmapInfo); newBeatmap.BeatmapInfo.BeatmapSet = targetBeatmapSet; - beatmapModelManager.Save(newBeatmap.BeatmapInfo, newBeatmap, beatmapSkin); + Save(newBeatmap.BeatmapInfo, newBeatmap, beatmapSkin); workingBeatmapCache.Invalidate(targetBeatmapSet); return GetWorkingBeatmap(newBeatmap.BeatmapInfo); @@ -255,23 +260,14 @@ public List GetAllUsableBeatmapSets() return realm.Run(r => r.All().FirstOrDefault(query)?.ToLive(realm)); } - #region Delegation to BeatmapModelManager (methods which previously existed locally). + #region Delegation to BeatmapImporter (methods which previously existed locally). /// /// Perform a lookup query on available s. /// /// The query. /// The first result for the provided query, or null if no results were found. - public BeatmapInfo? QueryBeatmap(Expression> query) => beatmapModelManager.QueryBeatmap(query)?.Detach(); - - /// - /// Saves an file against a given . - /// - /// The to save the content against. The file referenced by will be replaced. - /// The content to write. - /// The beatmap content to write, null if to be omitted. - public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin? beatmapSkin = null) => - beatmapModelManager.Save(info, beatmapContent, beatmapSkin); + public BeatmapInfo? QueryBeatmap(Expression> query) => realm.Run(r => r.All().FirstOrDefault(query)?.Detach()); /// /// A default representation of a WorkingBeatmap to use when no beatmap is available. @@ -281,9 +277,10 @@ public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin? beatm /// /// Fired when a notification should be presented to the user. /// - public Action PostNotification + public Action? PostNotification { - set => beatmapModelManager.PostNotification = value; + get => beatmapImporter.PostNotification; + set => beatmapImporter.PostNotification = value; } #endregion @@ -292,17 +289,17 @@ public Action PostNotification public bool IsAvailableLocally(BeatmapSetInfo model) { - return beatmapModelManager.IsAvailableLocally(model); + return beatmapImporter.IsAvailableLocally(model); } public bool Delete(BeatmapSetInfo item) { - return beatmapModelManager.Delete(item); + return beatmapImporter.Delete(item); } public void Delete(List items, bool silent = false) { - beatmapModelManager.Delete(items, silent); + beatmapImporter.Delete(items, silent); } public void Delete(Expression>? filter = null, bool silent = false) @@ -314,51 +311,156 @@ public void Delete(Expression>? filter = null, bool s if (filter != null) items = items.Where(filter); - beatmapModelManager.Delete(items.ToList(), silent); + beatmapImporter.Delete(items.ToList(), silent); }); } + /// + /// Saves an file against a given . + /// + /// The to save the content against. The file referenced by will be replaced. + /// The content to write. + /// The beatmap content to write, null if to be omitted. + public virtual void Save(BeatmapInfo beatmapInfo, IBeatmap beatmapContent, ISkin? beatmapSkin = null) + { + var setInfo = beatmapInfo.BeatmapSet; + Debug.Assert(setInfo != null); + + // Difficulty settings must be copied first due to the clone in `Beatmap<>.BeatmapInfo_Set`. + // This should hopefully be temporary, assuming said clone is eventually removed. + + // Warning: The directionality here is important. Changes have to be copied *from* beatmapContent (which comes from editor and is being saved) + // *to* the beatmapInfo (which is a database model and needs to receive values without the taiko slider velocity multiplier for correct operation). + // CopyTo() will undo such adjustments, while CopyFrom() will not. + beatmapContent.Difficulty.CopyTo(beatmapInfo.Difficulty); + + // All changes to metadata are made in the provided beatmapInfo, so this should be copied to the `IBeatmap` before encoding. + beatmapContent.BeatmapInfo = beatmapInfo; + + using (var stream = new MemoryStream()) + { + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw); + + stream.Seek(0, SeekOrigin.Begin); + + // AddFile generally handles updating/replacing files, but this is a case where the filename may have also changed so let's delete for simplicity. + var existingFileInfo = setInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, beatmapInfo.Path, StringComparison.OrdinalIgnoreCase)); + string targetFilename = createBeatmapFilenameFromMetadata(beatmapInfo); + + // ensure that two difficulties from the set don't point at the same beatmap file. + if (setInfo.Beatmaps.Any(b => b.ID != beatmapInfo.ID && string.Equals(b.Path, targetFilename, StringComparison.OrdinalIgnoreCase))) + throw new InvalidOperationException($"{setInfo.GetDisplayString()} already has a difficulty with the name of '{beatmapInfo.DifficultyName}'."); + + if (existingFileInfo != null) + DeleteFile(setInfo, existingFileInfo); + + beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); + beatmapInfo.Hash = stream.ComputeSHA2Hash(); + + AddFile(setInfo, stream, createBeatmapFilenameFromMetadata(beatmapInfo)); + + realm.Write(r => setInfo.CopyChangesToRealm(r.Find(setInfo.ID))); + } + + workingBeatmapCache.Invalidate(beatmapInfo); + + static string createBeatmapFilenameFromMetadata(BeatmapInfo beatmapInfo) + { + var metadata = beatmapInfo.Metadata; + return $"{metadata.Artist} - {metadata.Title} ({metadata.Author.Username}) [{beatmapInfo.DifficultyName}].osu".GetValidArchiveContentFilename(); + } + } + public void DeleteAllVideos() { realm.Write(r => { var items = r.All().Where(s => !s.DeletePending && !s.Protected); - beatmapModelManager.DeleteVideos(items.ToList()); + DeleteVideos(items.ToList()); }); } + /// + /// Delete videos from a list of beatmaps. + /// This will post notifications tracking progress. + /// + public void DeleteVideos(List items, bool silent = false) + { + if (items.Count == 0) return; + + var notification = new ProgressNotification + { + Progress = 0, + Text = $"Preparing to delete all {beatmapImporter.HumanisedModelName} videos...", + CompletionText = "No videos found to delete!", + State = ProgressNotificationState.Active, + }; + + if (!silent) + PostNotification?.Invoke(notification); + + int i = 0; + int deleted = 0; + + foreach (var b in items) + { + if (notification.State == ProgressNotificationState.Cancelled) + // user requested abort + return; + + var video = b.Files.FirstOrDefault(f => OsuGameBase.VIDEO_EXTENSIONS.Any(ex => f.Filename.EndsWith(ex, StringComparison.Ordinal))); + + if (video != null) + { + DeleteFile(b, video); + deleted++; + notification.CompletionText = $"Deleted {deleted} {beatmapImporter.HumanisedModelName} video(s)!"; + } + + notification.Text = $"Deleting videos from {beatmapImporter.HumanisedModelName}s ({deleted} deleted)"; + + notification.Progress = (float)++i / items.Count; + } + + notification.State = ProgressNotificationState.Completed; + } + public void UndeleteAll() { - realm.Run(r => beatmapModelManager.Undelete(r.All().Where(s => s.DeletePending).ToList())); + realm.Run(r => beatmapImporter.Undelete(r.All().Where(s => s.DeletePending).ToList())); } public void Undelete(List items, bool silent = false) { - beatmapModelManager.Undelete(items, silent); + beatmapImporter.Undelete(items, silent); } public void Undelete(BeatmapSetInfo item) { - beatmapModelManager.Undelete(item); + beatmapImporter.Undelete(item); } #endregion #region Implementation of ICanAcceptFiles - public Task Import(params string[] paths) => beatmapModelManager.Import(paths); + public Task Import(params string[] paths) => beatmapImporter.Import(paths); - public Task Import(params ImportTask[] tasks) => beatmapModelManager.Import(tasks); + public Task Import(params ImportTask[] tasks) => beatmapImporter.Import(tasks); - public Task>> Import(ProgressNotification notification, params ImportTask[] tasks) => beatmapModelManager.Import(notification, tasks); + public Task>> Import(ProgressNotification notification, params ImportTask[] tasks) => beatmapImporter.Import(notification, tasks); - public Task?> Import(ImportTask task, bool batchImport = false, CancellationToken cancellationToken = default) => beatmapModelManager.Import(task, batchImport, cancellationToken); + public Task?> Import(ImportTask task, bool batchImport = false, CancellationToken cancellationToken = default) => + beatmapImporter.Import(task, batchImport, cancellationToken); - public Task?> Import(ArchiveReader archive, bool batchImport = false, CancellationToken cancellationToken = default) => beatmapModelManager.Import(archive, batchImport, cancellationToken); + public Task?> Import(ArchiveReader archive, bool batchImport = false, CancellationToken cancellationToken = default) => + beatmapImporter.Import(archive, batchImport, cancellationToken); - public Live? Import(BeatmapSetInfo item, ArchiveReader? archive = null, CancellationToken cancellationToken = default) => beatmapModelManager.Import(item, archive, false, cancellationToken); + public Live? Import(BeatmapSetInfo item, ArchiveReader? archive = null, CancellationToken cancellationToken = default) => + beatmapImporter.Import(item, archive, false, cancellationToken); - public IEnumerable HandledExtensions => beatmapModelManager.HandledExtensions; + public IEnumerable HandledExtensions => beatmapImporter.HandledExtensions; #endregion @@ -400,17 +502,17 @@ public WorkingBeatmap GetWorkingBeatmap(Live? importedBeatmap) public void ReplaceFile(BeatmapSetInfo model, RealmNamedFileUsage file, Stream contents) { - beatmapModelManager.ReplaceFile(model, file, contents); + beatmapImporter.ReplaceFile(model, file, contents); } public void DeleteFile(BeatmapSetInfo model, RealmNamedFileUsage file) { - beatmapModelManager.DeleteFile(model, file); + beatmapImporter.DeleteFile(model, file); } public void AddFile(BeatmapSetInfo model, Stream contents, string filename) { - beatmapModelManager.AddFile(model, contents, filename); + beatmapImporter.AddFile(model, contents, filename); } #endregion @@ -428,7 +530,7 @@ public void Dispose() public Action>>? PostImport { - set => beatmapModelManager.PostImport = value; + set => beatmapImporter.PostImport = value; } #endregion diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs deleted file mode 100644 index 768bed6d67..0000000000 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) ppy Pty Ltd . 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.Diagnostics; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using osu.Framework.Extensions; -using osu.Framework.Platform; -using osu.Framework.Testing; -using osu.Game.Beatmaps.Formats; -using osu.Game.Database; -using osu.Game.Extensions; -using osu.Game.Skinning; -using osu.Game.Overlays.Notifications; - -#nullable enable - -namespace osu.Game.Beatmaps -{ - [ExcludeFromDynamicCompile] - public class BeatmapModelManager : BeatmapImporter - { - /// - /// The game working beatmap cache, used to invalidate entries on changes. - /// - public IWorkingBeatmapCache? WorkingBeatmapCache { private get; set; } - - public BeatmapModelManager(RealmAccess realm, Storage storage, BeatmapOnlineLookupQueue? onlineLookupQueue = null) - : base(realm, storage, onlineLookupQueue) - { - } - - /// - /// Saves an file against a given . - /// - /// The to save the content against. The file referenced by will be replaced. - /// The content to write. - /// The beatmap content to write, null if to be omitted. - public void Save(BeatmapInfo beatmapInfo, IBeatmap beatmapContent, ISkin? beatmapSkin = null) - { - var setInfo = beatmapInfo.BeatmapSet; - Debug.Assert(setInfo != null); - - // Difficulty settings must be copied first due to the clone in `Beatmap<>.BeatmapInfo_Set`. - // This should hopefully be temporary, assuming said clone is eventually removed. - - // Warning: The directionality here is important. Changes have to be copied *from* beatmapContent (which comes from editor and is being saved) - // *to* the beatmapInfo (which is a database model and needs to receive values without the taiko slider velocity multiplier for correct operation). - // CopyTo() will undo such adjustments, while CopyFrom() will not. - beatmapContent.Difficulty.CopyTo(beatmapInfo.Difficulty); - - // All changes to metadata are made in the provided beatmapInfo, so this should be copied to the `IBeatmap` before encoding. - beatmapContent.BeatmapInfo = beatmapInfo; - - using (var stream = new MemoryStream()) - { - using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw); - - stream.Seek(0, SeekOrigin.Begin); - - // AddFile generally handles updating/replacing files, but this is a case where the filename may have also changed so let's delete for simplicity. - var existingFileInfo = setInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, beatmapInfo.Path, StringComparison.OrdinalIgnoreCase)); - string targetFilename = createBeatmapFilenameFromMetadata(beatmapInfo); - - // ensure that two difficulties from the set don't point at the same beatmap file. - if (setInfo.Beatmaps.Any(b => b.ID != beatmapInfo.ID && string.Equals(b.Path, targetFilename, StringComparison.OrdinalIgnoreCase))) - throw new InvalidOperationException($"{setInfo.GetDisplayString()} already has a difficulty with the name of '{beatmapInfo.DifficultyName}'."); - - if (existingFileInfo != null) - DeleteFile(setInfo, existingFileInfo); - - beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); - beatmapInfo.Hash = stream.ComputeSHA2Hash(); - - AddFile(setInfo, stream, createBeatmapFilenameFromMetadata(beatmapInfo)); - - Realm.Write(r => setInfo.CopyChangesToRealm(r.Find(setInfo.ID))); - } - - WorkingBeatmapCache?.Invalidate(beatmapInfo); - - static string createBeatmapFilenameFromMetadata(BeatmapInfo beatmapInfo) - { - var metadata = beatmapInfo.Metadata; - return $"{metadata.Artist} - {metadata.Title} ({metadata.Author.Username}) [{beatmapInfo.DifficultyName}].osu".GetValidArchiveContentFilename(); - } - } - - /// - /// Perform a lookup query on available s. - /// - /// The query. - /// The first result for the provided query, or null if no results were found. - public BeatmapInfo? QueryBeatmap(Expression> query) - { - return Realm.Run(realm => realm.All().FirstOrDefault(query)?.Detach()); - } - - /// - /// Delete videos from a list of beatmaps. - /// This will post notifications tracking progress. - /// - public void DeleteVideos(List items, bool silent = false) - { - if (items.Count == 0) return; - - var notification = new ProgressNotification - { - Progress = 0, - Text = $"Preparing to delete all {HumanisedModelName} videos...", - CompletionText = "No videos found to delete!", - State = ProgressNotificationState.Active, - }; - - if (!silent) - PostNotification?.Invoke(notification); - - int i = 0; - int deleted = 0; - - foreach (var b in items) - { - if (notification.State == ProgressNotificationState.Cancelled) - // user requested abort - return; - - var video = b.Files.FirstOrDefault(f => OsuGameBase.VIDEO_EXTENSIONS.Any(ex => f.Filename.EndsWith(ex, StringComparison.Ordinal))); - - if (video != null) - { - DeleteFile(b, video); - deleted++; - notification.CompletionText = $"Deleted {deleted} {HumanisedModelName} video(s)!"; - } - - notification.Text = $"Deleting videos from {HumanisedModelName}s ({deleted} deleted)"; - - notification.Progress = (float)++i / items.Count; - } - - notification.State = ProgressNotificationState.Completed; - } - } -} diff --git a/osu.Game/Database/RealmArchiveModelImporter.cs b/osu.Game/Database/RealmArchiveModelImporter.cs index 4153f421ef..32e71c5521 100644 --- a/osu.Game/Database/RealmArchiveModelImporter.cs +++ b/osu.Game/Database/RealmArchiveModelImporter.cs @@ -72,7 +72,7 @@ public abstract class RealmArchiveModelImporter : IModelImporter /// /// Set an endpoint for notifications to be posted to. /// - public Action? PostNotification { protected get; set; } + public Action? PostNotification { get; set; } protected RealmArchiveModelImporter(Storage storage, RealmAccess realm) { diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 8e9bb6ff78..fbb5209513 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -145,9 +145,9 @@ public TestBeatmapManager(Storage storage, RealmAccess realm, RulesetStore rules { } - protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue) + protected override BeatmapImporter CreateBeatmapImporter(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue) { - return new BeatmapModelManager(realm, storage, onlineLookupQueue); + return new BeatmapImporter(realm, storage, onlineLookupQueue); } protected override WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap defaultBeatmap, GameHost host)