public void TestImportThenReimportAfterMissingFiles() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapModelManager(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Realm); deleteBeatmapSet(imported, realmFactory.Realm); Assert.IsTrue(imported.DeletePending); // intentionally nuke all files storage.DeleteDirectory("files"); Assert.That(imported.Files.All(f => !storage.GetStorageForDirectory("files").Exists(f.File.GetStoragePath()))); var importedSecondTime = await LoadOszIntoStore(importer, realmFactory.Realm); // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. Assert.IsTrue(imported.ID == importedSecondTime.ID); Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); Assert.IsFalse(imported.DeletePending); Assert.IsFalse(importedSecondTime.DeletePending); // check that the files now exist, even though they were deleted above. Assert.That(importedSecondTime.Files.All(f => storage.GetStorageForDirectory("files").Exists(f.File.GetStoragePath()))); }); }
public void TestImportThenDeleteThenImportNonOptimisedPath() { RunTestWithRealmAsync(async(realm, storage) => { using var importer = new NonOptimisedBeatmapImporter(realm, storage); using var store = new RealmRulesetStore(realm, storage); var imported = await LoadOszIntoStore(importer, realm.Realm); deleteBeatmapSet(imported, realm.Realm); Assert.IsTrue(imported.DeletePending); var originalAddedDate = imported.DateAdded; var importedSecondTime = await LoadOszIntoStore(importer, realm.Realm); // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. Assert.IsTrue(imported.ID == importedSecondTime.ID); Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); Assert.IsFalse(imported.DeletePending); Assert.IsFalse(importedSecondTime.DeletePending); Assert.That(importedSecondTime.DateAdded, Is.GreaterThan(originalAddedDate)); }); }
public void TestModelCreationFailureDoesntReturn() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); var progressNotification = new ImportProgressNotification(); var zipStream = new MemoryStream(); using (var zip = ZipArchive.Create()) zip.SaveTo(zipStream, new ZipWriterOptions(CompressionType.Deflate)); var imported = await importer.Import( progressNotification, new ImportTask(zipStream, string.Empty) ); checkBeatmapSetCount(realmFactory.Context, 0); checkBeatmapCount(realmFactory.Context, 0); Assert.IsEmpty(imported); Assert.AreEqual(ProgressNotificationState.Cancelled, progressNotification.State); }); }
public void TestRollbackOnFailure() { RunTestWithRealmAsync(async(realm, storage) => { int loggedExceptionCount = 0; Logger.NewEntry += l => { if (l.Target == LoggingTarget.Database && l.Exception != null) { Interlocked.Increment(ref loggedExceptionCount); } }; using var importer = new BeatmapModelManager(realm, storage); using var store = new RealmRulesetStore(realm, storage); var imported = await LoadOszIntoStore(importer, realm.Realm); realm.Realm.Write(() => imported.Hash += "-changed"); checkBeatmapSetCount(realm.Realm, 1); checkBeatmapCount(realm.Realm, 12); checkSingleReferencedFileCount(realm.Realm, 18); string?brokenTempFilename = TestResources.GetTestBeatmapForImport(); MemoryStream brokenOsu = new MemoryStream(); MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(brokenTempFilename)); File.Delete(brokenTempFilename); using (var outStream = File.Open(brokenTempFilename, FileMode.CreateNew)) using (var zip = ZipArchive.Open(brokenOsz)) { zip.AddEntry("broken.osu", brokenOsu, false); zip.SaveTo(outStream, CompressionType.Deflate); } // this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu. try { await importer.Import(new ImportTask(brokenTempFilename)); } catch { } EnsureLoaded(realm.Realm); checkBeatmapSetCount(realm.Realm, 1); checkBeatmapCount(realm.Realm, 12); checkSingleReferencedFileCount(realm.Realm, 18); Assert.AreEqual(1, loggedExceptionCount); File.Delete(brokenTempFilename); }); }
public void TestImportWithDuplicateHashes() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); string?temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); try { using (var zip = ZipArchive.Open(temp)) zip.WriteToDirectory(extractedFolder); using (var zip = ZipArchive.Create()) { zip.AddAllFromDirectory(extractedFolder); zip.AddEntry("duplicate.osu", Directory.GetFiles(extractedFolder, "*.osu").First()); zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); } await importer.Import(temp); ensureLoaded(realmFactory.Context); } finally { Directory.Delete(extractedFolder, true); } }); }
public void TestImportThenDeleteThenImportWithOnlineIDsMissing() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Context); realmFactory.Context.Write(() => { foreach (var b in imported.Beatmaps) { b.OnlineID = -1; } }); deleteBeatmapSet(imported, realmFactory.Context); var importedSecondTime = await LoadOszIntoStore(importer, realmFactory.Context); // check the newly "imported" beatmap has been reimported due to mismatch (even though hashes matched) Assert.IsTrue(imported.ID != importedSecondTime.ID); Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID); }); }
public void TestImportDirectoryWithEmptyOsuFiles() { RunTestWithRealmAsync(async(realm, storage) => { using var importer = new BeatmapImporter(storage, realm); using var store = new RealmRulesetStore(realm, storage); string?temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); try { using (var zip = ZipArchive.Open(temp)) zip.WriteToDirectory(extractedFolder); foreach (var file in new DirectoryInfo(extractedFolder).GetFiles("*.osu")) { using (file.Open(FileMode.Create)) { // empty file. } } var imported = await importer.Import(new ImportTask(extractedFolder)); Assert.IsNull(imported); } finally { Directory.Delete(extractedFolder, true); } }); }
public void TestImportCorruptThenImport() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Context); var firstFile = imported.Files.First(); long originalLength; using (var stream = storage.GetStream(firstFile.File.StoragePath)) originalLength = stream.Length; using (var stream = storage.GetStream(firstFile.File.StoragePath, FileAccess.Write, FileMode.Create)) stream.WriteByte(0); var importedSecondTime = await LoadOszIntoStore(importer, realmFactory.Context); using (var stream = storage.GetStream(firstFile.File.StoragePath)) Assert.AreEqual(stream.Length, originalLength, "Corruption was not fixed on second import"); // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. Assert.IsTrue(imported.ID == importedSecondTime.ID); Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); checkBeatmapSetCount(realmFactory.Context, 1); checkSingleReferencedFileCount(realmFactory.Context, 18); }); }
public void TestImportThenDeleteFromStream() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); string?tempPath = TestResources.GetTestBeatmapForImport(); ILive <RealmBeatmapSet>?importedSet; using (var stream = File.OpenRead(tempPath)) { importedSet = await importer.Import(new ImportTask(stream, Path.GetFileName(tempPath))); ensureLoaded(realmFactory.Context); } Assert.NotNull(importedSet); Debug.Assert(importedSet != null); Assert.IsTrue(File.Exists(tempPath), "Stream source file somehow went missing"); File.Delete(tempPath); var imported = realmFactory.Context.All <RealmBeatmapSet>().First(beatmapSet => beatmapSet.ID == importedSet.ID); deleteBeatmapSet(imported, realmFactory.Context); }); }
public void TestImportWhenClosed() { RunTestWithRealmAsync(async(realm, storage) => { using var importer = new BeatmapModelManager(realm, storage); using var store = new RealmRulesetStore(realm, storage); await LoadOszIntoStore(importer, realm.Realm); }); }
public void TestCreateStore() { RunTestWithRealm((realm, storage) => { var rulesets = new RealmRulesetStore(realm, storage); Assert.AreEqual(4, rulesets.AvailableRulesets.Count()); Assert.AreEqual(4, realm.Realm.All <RulesetInfo>().Count()); }); }
public void TestImportWhenClosed() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); await LoadOszIntoStore(importer, realmFactory.Context); }); }
public void TestRetrievedRulesetsAreDetached() { RunTestWithRealm((realm, storage) => { var rulesets = new RealmRulesetStore(realm, storage); Assert.IsFalse(rulesets.AvailableRulesets.First().IsManaged); Assert.IsFalse(rulesets.GetRuleset(0)?.IsManaged); Assert.IsFalse(rulesets.GetRuleset("mania")?.IsManaged); }); }
public void TestRetrievedRulesetsAreDetached() { RunTestWithRealm((realmFactory, storage) => { var rulesets = new RealmRulesetStore(realmFactory, storage); Assert.IsTrue((rulesets.AvailableRulesets.First() as RealmRuleset)?.IsManaged == false); Assert.IsTrue((rulesets.GetRuleset(0) as RealmRuleset)?.IsManaged == false); Assert.IsTrue((rulesets.GetRuleset("mania") as RealmRuleset)?.IsManaged == false); }); }
public void TestImportThenDelete() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Context); deleteBeatmapSet(imported, realmFactory.Context); }); }
public void TestCreateStoreTwiceDoesntAddRulesetsAgain() { RunTestWithRealm((realm, storage) => { var rulesets = new RealmRulesetStore(realm, storage); var rulesets2 = new RealmRulesetStore(realm, storage); Assert.AreEqual(4, rulesets.AvailableRulesets.Count()); Assert.AreEqual(4, rulesets2.AvailableRulesets.Count()); Assert.AreEqual(rulesets.AvailableRulesets.First(), rulesets2.AvailableRulesets.First()); Assert.AreEqual(4, realm.Realm.All <RulesetInfo>().Count()); }); }
public void TestImportThenImportWithChangedHashedFile() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); string?temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); try { var imported = await LoadOszIntoStore(importer, realmFactory.Context); await createScoreForBeatmap(realmFactory.Context, imported.Beatmaps.First()); using (var zip = ZipArchive.Open(temp)) zip.WriteToDirectory(extractedFolder); // arbitrary write to hashed file // this triggers the special BeatmapManager.PreImport deletion/replacement flow. using (var sw = new FileInfo(Directory.GetFiles(extractedFolder, "*.osu").First()).AppendText()) await sw.WriteLineAsync("// changed"); using (var zip = ZipArchive.Create()) { zip.AddAllFromDirectory(extractedFolder); zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); } var importedSecondTime = await importer.Import(new ImportTask(temp)); ensureLoaded(realmFactory.Context); // check the newly "imported" beatmap is not the original. Assert.NotNull(importedSecondTime); Debug.Assert(importedSecondTime != null); Assert.IsTrue(imported.ID != importedSecondTime.ID); Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.PerformRead(s => s.Beatmaps.First().ID)); } finally { Directory.Delete(extractedFolder, true); } }); }
public void TestImportWithIgnoredDirectoryInArchive() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); string?temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; string dataFolder = Path.Combine(extractedFolder, "actual_data"); string resourceForkFolder = Path.Combine(extractedFolder, "__MACOSX"); string resourceForkFilePath = Path.Combine(resourceForkFolder, ".extracted"); Directory.CreateDirectory(dataFolder); Directory.CreateDirectory(resourceForkFolder); using (var resourceForkFile = File.CreateText(resourceForkFilePath)) { await resourceForkFile.WriteLineAsync("adding content so that it's not empty"); } try { using (var zip = ZipArchive.Open(temp)) zip.WriteToDirectory(dataFolder); using (var zip = ZipArchive.Create()) { zip.AddAllFromDirectory(extractedFolder); zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); } var imported = await importer.Import(new ImportTask(temp)); Assert.NotNull(imported); Debug.Assert(imported != null); ensureLoaded(realmFactory.Context); Assert.IsFalse(imported.PerformRead(s => s.Files.Any(f => f.Filename.Contains("__MACOSX"))), "Files contain resource fork folder, which should be ignored"); Assert.IsFalse(imported.PerformRead(s => s.Files.Any(f => f.Filename.Contains("actual_data"))), "Files contain common subfolder"); } finally { Directory.Delete(extractedFolder, true); } }); }
public void TestImportWhenFileOpen() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); string?temp = TestResources.GetTestBeatmapForImport(); using (File.OpenRead(temp)) await importer.Import(temp); ensureLoaded(realmFactory.Context); File.Delete(temp); Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't"); }); }
public void TestAccessFileAfterImport() { RunTestWithRealmAsync(async(realm, storage) => { using var importer = new BeatmapModelManager(realm, storage); using var store = new RealmRulesetStore(realm, storage); var imported = await LoadOszIntoStore(importer, realm.Realm); var beatmap = imported.Beatmaps.First(); var file = beatmap.File; Assert.NotNull(file); Assert.AreEqual(beatmap.Hash, file !.File.Hash); }); }
public void TestImportThenImportWithReZip() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); string?temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); try { var imported = await LoadOszIntoStore(importer, realmFactory.Context); string hashBefore = hashFile(temp); using (var zip = ZipArchive.Open(temp)) zip.WriteToDirectory(extractedFolder); using (var zip = ZipArchive.Create()) { zip.AddAllFromDirectory(extractedFolder); zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); } // zip files differ because different compression or encoder. Assert.AreNotEqual(hashBefore, hashFile(temp)); var importedSecondTime = await importer.Import(new ImportTask(temp)); ensureLoaded(realmFactory.Context); Assert.NotNull(importedSecondTime); Debug.Assert(importedSecondTime != null); // but contents doesn't, so existing should still be used. Assert.IsTrue(imported.ID == importedSecondTime.ID); Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.PerformRead(s => s.Beatmaps.First().ID)); } finally { Directory.Delete(extractedFolder, true); } }); }
public void TestImportWithDuplicateBeatmapIDs() { RunTestWithRealm((realm, storage) => { using var importer = new BeatmapModelManager(realm, storage); using var store = new RealmRulesetStore(realm, storage); var metadata = new BeatmapMetadata { Artist = "SomeArtist", Author = { Username = "******" } }; var ruleset = realm.Realm.All <RulesetInfo>().First(); var toImport = new BeatmapSetInfo { OnlineID = 1, Beatmaps = { new BeatmapInfo(ruleset, new BeatmapDifficulty(), metadata) { OnlineID = 2, }, new BeatmapInfo(ruleset, new BeatmapDifficulty(), metadata) { OnlineID = 2, Status = BeatmapOnlineStatus.Loved, } } }; var imported = importer.Import(toImport); realm.Run(r => r.Refresh()); Assert.NotNull(imported); Debug.Assert(imported != null); Assert.AreEqual(-1, imported.PerformRead(s => s.Beatmaps[0].OnlineID)); Assert.AreEqual(-1, imported.PerformRead(s => s.Beatmaps[1].OnlineID)); }); }
public void TestImportThenImportWithDifferentFilename() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); string?temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); try { var imported = await LoadOszIntoStore(importer, realmFactory.Context); using (var zip = ZipArchive.Open(temp)) zip.WriteToDirectory(extractedFolder); // change filename var firstFile = new FileInfo(Directory.GetFiles(extractedFolder).First()); firstFile.MoveTo(Path.Combine(firstFile.DirectoryName.AsNonNull(), $"{firstFile.Name}-changed{firstFile.Extension}")); using (var zip = ZipArchive.Create()) { zip.AddAllFromDirectory(extractedFolder); zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); } var importedSecondTime = await importer.Import(new ImportTask(temp)); ensureLoaded(realmFactory.Context); Assert.NotNull(importedSecondTime); Debug.Assert(importedSecondTime != null); // check the newly "imported" beatmap is not the original. Assert.IsTrue(imported.ID != importedSecondTime.ID); Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.PerformRead(s => s.Beatmaps.First().ID)); } finally { Directory.Delete(extractedFolder, true); } }); }
public void TestImportThenDeleteThenImport() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); var imported = await LoadOszIntoStore(importer, realmFactory.Context); deleteBeatmapSet(imported, realmFactory.Context); var importedSecondTime = await LoadOszIntoStore(importer, realmFactory.Context); // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. Assert.IsTrue(imported.ID == importedSecondTime.ID); Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); }); }
public void TestImportThenImport() { RunTestWithRealmAsync(async(realm, storage) => { using var importer = new BeatmapModelManager(realm, storage); using var store = new RealmRulesetStore(realm, storage); var imported = await LoadOszIntoStore(importer, realm.Realm); var importedSecondTime = await LoadOszIntoStore(importer, realm.Realm); // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. Assert.IsTrue(imported.ID == importedSecondTime.ID); Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); checkBeatmapSetCount(realm.Realm, 1); checkSingleReferencedFileCount(realm.Realm, 18); }); }
public void TestImportWithDuplicateBeatmapIDs() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); var metadata = new RealmBeatmapMetadata { Artist = "SomeArtist", Author = { Username = "******" } }; var ruleset = realmFactory.Context.All <RealmRuleset>().First(); var toImport = new RealmBeatmapSet { OnlineID = 1, Beatmaps = { new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), metadata) { OnlineID = 2, }, new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), metadata) { OnlineID = 2, Status = BeatmapSetOnlineStatus.Loved, } } }; var imported = await importer.Import(toImport); Assert.NotNull(imported); Debug.Assert(imported != null); Assert.AreEqual(-1, imported.PerformRead(s => s.Beatmaps[0].OnlineID)); Assert.AreEqual(-1, imported.PerformRead(s => s.Beatmaps[1].OnlineID)); }); }
public void TestUpdateBeatmapInfo() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); string?temp = TestResources.GetTestBeatmapForImport(); await importer.Import(temp); // Update via the beatmap, not the beatmap info, to ensure correct linking RealmBeatmapSet setToUpdate = realmFactory.Context.All <RealmBeatmapSet>().First(); var beatmapToUpdate = setToUpdate.Beatmaps.First(); realmFactory.Context.Write(() => beatmapToUpdate.DifficultyName = "updated"); RealmBeatmap updatedInfo = realmFactory.Context.All <RealmBeatmap>().First(b => b.ID == beatmapToUpdate.ID); Assert.That(updatedInfo.DifficultyName, Is.EqualTo("updated")); }); }
public void TestImportNestedStructure() { RunTestWithRealmAsync(async(realmFactory, storage) => { using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); string?temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; string subfolder = Path.Combine(extractedFolder, "subfolder"); Directory.CreateDirectory(subfolder); try { using (var zip = ZipArchive.Open(temp)) zip.WriteToDirectory(subfolder); using (var zip = ZipArchive.Create()) { zip.AddAllFromDirectory(extractedFolder); zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); } var imported = await importer.Import(new ImportTask(temp)); Assert.NotNull(imported); Debug.Assert(imported != null); ensureLoaded(realmFactory.Context); Assert.IsFalse(imported.PerformRead(s => s.Files.Any(f => f.Filename.Contains("subfolder"))), "Files contain common subfolder"); } finally { Directory.Delete(extractedFolder, true); } }); }
private void load(ReadableKeyCombinationProvider keyCombinationProvider) { try { using (var str = File.OpenRead(typeof(OsuGameBase).Assembly.Location)) VersionHash = str.ComputeMD5Hash(); } catch { // special case for android builds, which can't read DLLs from a packed apk. // should eventually be handled in a better way. VersionHash = $"{Version}-{RuntimeInfo.OS}".ComputeMD5Hash(); } Resources.AddStore(new DllResourceStore(OsuResources.ResourceAssembly)); if (Storage.Exists(DatabaseContextFactory.DATABASE_NAME)) { dependencies.Cache(EFContextFactory = new DatabaseContextFactory(Storage)); } dependencies.Cache(realm = new RealmAccess(Storage, "client", EFContextFactory)); dependencies.CacheAs <RulesetStore>(RulesetStore = new RealmRulesetStore(realm, Storage)); dependencies.CacheAs <IRulesetStore>(RulesetStore); Decoder.RegisterDependencies(RulesetStore); // Backup is taken here rather than in EFToRealmMigrator to avoid recycling realm contexts // after initial usages below. It can be moved once a direction is established for handling re-subscription. // See https://github.com/ppy/osu/pull/16547 for more discussion. if (EFContextFactory != null) { const string backup_folder = "backups"; string migration = $"before_final_migration_{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}"; EFContextFactory.CreateBackup(Path.Combine(backup_folder, $"client.{migration}.db")); realm.CreateBackup(Path.Combine(backup_folder, $"client.{migration}.realm")); using (var source = Storage.GetStream("collection.db")) { if (source != null) { using (var destination = Storage.GetStream(Path.Combine(backup_folder, $"collection.{migration}.db"), FileAccess.Write, FileMode.CreateNew)) source.CopyTo(destination); } } } dependencies.CacheAs(Storage); var largeStore = new LargeTextureStore(Host.CreateTextureLoaderStore(new NamespacedResourceStore <byte[]>(Resources, @"Textures"))); largeStore.AddStore(Host.CreateTextureLoaderStore(new OnlineStore())); dependencies.Cache(largeStore); dependencies.CacheAs(this); dependencies.CacheAs(LocalConfig); InitialiseFonts(); Audio.Samples.PlaybackConcurrency = SAMPLE_CONCURRENCY; dependencies.Cache(SkinManager = new SkinManager(Storage, realm, Host, Resources, Audio, Scheduler)); dependencies.CacheAs <ISkinSource>(SkinManager); EndpointConfiguration endpoints = UseDevelopmentServer ? (EndpointConfiguration) new DevelopmentEndpointConfiguration() : new ProductionEndpointConfiguration(); MessageFormatter.WebsiteRootUrl = endpoints.WebsiteRootUrl; dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash)); dependencies.CacheAs(spectatorClient = new OnlineSpectatorClient(endpoints)); dependencies.CacheAs(multiplayerClient = new OnlineMultiplayerClient(endpoints)); var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, realm, Scheduler, () => difficultyCache, LocalConfig)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, realm, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, performOnlineLookups: true)); dependencies.Cache(BeatmapDownloader = new BeatmapModelDownloader(BeatmapManager, API)); dependencies.Cache(ScoreDownloader = new ScoreModelDownloader(ScoreManager, API)); dependencies.Cache(difficultyCache = new BeatmapDifficultyCache()); AddInternal(difficultyCache); dependencies.Cache(userCache = new UserLookupCache()); AddInternal(userCache); dependencies.Cache(beatmapCache = new BeatmapLookupCache()); AddInternal(beatmapCache); var scorePerformanceManager = new ScorePerformanceCache(); dependencies.Cache(scorePerformanceManager); AddInternal(scorePerformanceManager); dependencies.CacheAs <IRulesetConfigCache>(rulesetConfigCache = new RulesetConfigCache(realm, RulesetStore)); var powerStatus = CreateBatteryInfo(); if (powerStatus != null) { dependencies.CacheAs(powerStatus); } dependencies.Cache(SessionStatics = new SessionStatics()); dependencies.Cache(new OsuColour()); RegisterImportHandler(BeatmapManager); RegisterImportHandler(ScoreManager); RegisterImportHandler(SkinManager); // drop track volume game-wide to leave some head-room for UI effects / samples. // this means that for the time being, gameplay sample playback is louder relative to the audio track, compared to stable. // we may want to revisit this if users notice or complain about the difference (consider this a bit of a trial). Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, globalTrackVolumeAdjust); Beatmap = new NonNullableBindable <WorkingBeatmap>(defaultBeatmap); dependencies.CacheAs <IBindable <WorkingBeatmap> >(Beatmap); dependencies.CacheAs(Beatmap); // add api components to hierarchy. if (API is APIAccess apiAccess) { AddInternal(apiAccess); } AddInternal(spectatorClient); AddInternal(multiplayerClient); AddInternal(rulesetConfigCache); GlobalActionContainer globalBindings; base.Content.Add(new SafeAreaContainer { SafeAreaOverrideEdges = SafeAreaOverrideEdges, RelativeSizeAxes = Axes.Both, Child = CreateScalingContainer().WithChildren(new Drawable[] { (MenuCursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }).WithChild(content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }), // to avoid positional input being blocked by children, ensure the GlobalActionContainer is above everything. globalBindings = new GlobalActionContainer(this) }) }); KeyBindingStore = new RealmKeyBindingStore(realm, keyCombinationProvider); KeyBindingStore.Register(globalBindings, RulesetStore.AvailableRulesets); dependencies.Cache(globalBindings); PreviewTrackManager previewTrackManager; dependencies.Cache(previewTrackManager = new PreviewTrackManager(BeatmapManager.BeatmapTrackStore)); Add(previewTrackManager); AddInternal(MusicController = new MusicController()); dependencies.CacheAs(MusicController); Ruleset.BindValueChanged(onRulesetChanged); Beatmap.BindValueChanged(onBeatmapChanged); }
private void load(ReadableKeyCombinationProvider keyCombinationProvider) { try { using (var str = File.OpenRead(typeof(OsuGameBase).Assembly.Location)) VersionHash = str.ComputeMD5Hash(); } catch { // special case for android builds, which can't read DLLs from a packed apk. // should eventually be handled in a better way. VersionHash = $"{Version}-{RuntimeInfo.OS}".ComputeMD5Hash(); } Resources.AddStore(new DllResourceStore(OsuResources.ResourceAssembly)); dependencies.Cache(contextFactory = new DatabaseContextFactory(Storage)); dependencies.Cache(realmFactory = new RealmContextFactory(Storage, "client")); dependencies.CacheAs(Storage); var largeStore = new LargeTextureStore(Host.CreateTextureLoaderStore(new NamespacedResourceStore <byte[]>(Resources, @"Textures"))); largeStore.AddStore(Host.CreateTextureLoaderStore(new OnlineStore())); dependencies.Cache(largeStore); dependencies.CacheAs(this); dependencies.CacheAs(LocalConfig); InitialiseFonts(); Audio.Samples.PlaybackConcurrency = SAMPLE_CONCURRENCY; runMigrations(); dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Resources, Audio)); dependencies.CacheAs <ISkinSource>(SkinManager); // needs to be done here rather than inside SkinManager to ensure thread safety of CurrentSkinInfo. SkinManager.ItemRemoved += item => Schedule(() => { // check the removed skin is not the current user choice. if it is, switch back to default. if (item.ID == SkinManager.CurrentSkinInfo.Value.ID) { SkinManager.CurrentSkinInfo.Value = SkinInfo.Default; } }); EndpointConfiguration endpoints = UseDevelopmentServer ? (EndpointConfiguration) new DevelopmentEndpointConfiguration() : new ProductionEndpointConfiguration(); MessageFormatter.WebsiteRootUrl = endpoints.WebsiteRootUrl; dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash)); dependencies.CacheAs(spectatorClient = new OnlineSpectatorClient(endpoints)); dependencies.CacheAs(multiplayerClient = new OnlineMultiplayerClient(endpoints)); var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory, Storage)); dependencies.Cache(fileStore = new FileStore(contextFactory, Storage)); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Scheduler, Host, () => difficultyCache, LocalConfig)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, performOnlineLookups: true)); // the following realm components are not actively used yet, but initialised and kept up to date for initial testing. realmRulesetStore = new RealmRulesetStore(realmFactory, Storage); dependencies.Cache(realmRulesetStore); // this should likely be moved to ArchiveModelManager when another case appears where it is necessary // to have inter-dependent model managers. this could be obtained with an IHasForeign<T> interface to // allow lookups to be done on the child (ScoreManager in this case) to perform the cascading delete. List <ScoreInfo> getBeatmapScores(BeatmapSetInfo set) { var beatmapIds = BeatmapManager.QueryBeatmaps(b => b.BeatmapSetInfoID == set.ID).Select(b => b.ID).ToList(); return(ScoreManager.QueryScores(s => beatmapIds.Contains(s.BeatmapInfo.ID)).ToList()); } BeatmapManager.ItemRemoved += item => ScoreManager.Delete(getBeatmapScores(item), true); BeatmapManager.ItemUpdated += item => ScoreManager.Undelete(getBeatmapScores(item), true); dependencies.Cache(difficultyCache = new BeatmapDifficultyCache()); AddInternal(difficultyCache); dependencies.Cache(userCache = new UserLookupCache()); AddInternal(userCache); var scorePerformanceManager = new ScorePerformanceCache(); dependencies.Cache(scorePerformanceManager); AddInternal(scorePerformanceManager); migrateDataToRealm(); dependencies.Cache(rulesetConfigCache = new RulesetConfigCache(realmFactory, RulesetStore)); var powerStatus = CreateBatteryInfo(); if (powerStatus != null) { dependencies.CacheAs(powerStatus); } dependencies.Cache(SessionStatics = new SessionStatics()); dependencies.Cache(new OsuColour()); RegisterImportHandler(BeatmapManager); RegisterImportHandler(ScoreManager); RegisterImportHandler(SkinManager); // drop track volume game-wide to leave some head-room for UI effects / samples. // this means that for the time being, gameplay sample playback is louder relative to the audio track, compared to stable. // we may want to revisit this if users notice or complain about the difference (consider this a bit of a trial). Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, globalTrackVolumeAdjust); Beatmap = new NonNullableBindable <WorkingBeatmap>(defaultBeatmap); dependencies.CacheAs <IBindable <WorkingBeatmap> >(Beatmap); dependencies.CacheAs(Beatmap); fileStore.Cleanup(); // add api components to hierarchy. if (API is APIAccess apiAccess) { AddInternal(apiAccess); } AddInternal(spectatorClient); AddInternal(multiplayerClient); AddInternal(rulesetConfigCache); GlobalActionContainer globalBindings; var mainContent = new Drawable[] { MenuCursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }, // to avoid positional input being blocked by children, ensure the GlobalActionContainer is above everything. globalBindings = new GlobalActionContainer(this) }; MenuCursorContainer.Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }; base.Content.Add(CreateScalingContainer().WithChildren(mainContent)); KeyBindingStore = new RealmKeyBindingStore(realmFactory, keyCombinationProvider); KeyBindingStore.Register(globalBindings, RulesetStore.AvailableRulesets); dependencies.Cache(globalBindings); PreviewTrackManager previewTrackManager; dependencies.Cache(previewTrackManager = new PreviewTrackManager(BeatmapManager.BeatmapTrackStore)); Add(previewTrackManager); AddInternal(MusicController = new MusicController()); dependencies.CacheAs(MusicController); Ruleset.BindValueChanged(onRulesetChanged); }