mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 15:16:38 +09:00
Merge branch 'master' into store-modified-time
This commit is contained in:
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Models;
|
||||
|
||||
namespace osu.Game.Database
|
||||
@ -11,8 +12,16 @@ namespace osu.Game.Database
|
||||
/// </summary>
|
||||
public interface IHasRealmFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// Available files in this model, with locally filenames.
|
||||
/// When performing lookups, consider using <see cref="BeatmapSetInfoExtensions.GetFile"/> or <see cref="BeatmapSetInfoExtensions.GetPathForFile"/> to do case-insensitive lookups.
|
||||
/// </summary>
|
||||
IList<RealmNamedFileUsage> Files { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A combined hash representing the model, based on the files it contains.
|
||||
/// Implementation specific.
|
||||
/// </summary>
|
||||
string Hash { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
@ -27,27 +25,30 @@ namespace osu.Game.Database
|
||||
public class LegacyImportManager : Component
|
||||
{
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; }
|
||||
private SkinManager skins { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
private BeatmapManager beatmaps { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private ScoreManager scores { get; set; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OsuGame game { get; set; }
|
||||
private ScoreManager scores { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
private OsuGame? game { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realmAccess { get; set; }
|
||||
private IDialogOverlay dialogOverlay { get; set; } = null!;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private DesktopGameHost desktopGameHost { get; set; }
|
||||
[Resolved]
|
||||
private RealmAccess realmAccess { get; set; } = null!;
|
||||
|
||||
private StableStorage cachedStorage;
|
||||
[Resolved(canBeNull: true)] // canBeNull required while we remain on mono for mobile platforms.
|
||||
private DesktopGameHost? desktopGameHost { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private INotificationOverlay? notifications { get; set; }
|
||||
|
||||
private StableStorage? cachedStorage;
|
||||
|
||||
public bool SupportsImportFromStable => RuntimeInfo.IsDesktop;
|
||||
|
||||
@ -98,6 +99,9 @@ namespace osu.Game.Database
|
||||
stableStorage = GetCurrentStableStorage();
|
||||
}
|
||||
|
||||
if (stableStorage == null)
|
||||
return;
|
||||
|
||||
var importTasks = new List<Task>();
|
||||
|
||||
Task beatmapImportTask = Task.CompletedTask;
|
||||
@ -108,7 +112,14 @@ namespace osu.Game.Database
|
||||
importTasks.Add(new LegacySkinImporter(skins).ImportFromStableAsync(stableStorage));
|
||||
|
||||
if (content.HasFlagFast(StableContent.Collections))
|
||||
importTasks.Add(beatmapImportTask.ContinueWith(_ => new LegacyCollectionImporter(realmAccess).ImportFromStorage(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion));
|
||||
{
|
||||
importTasks.Add(beatmapImportTask.ContinueWith(_ => new LegacyCollectionImporter(realmAccess)
|
||||
{
|
||||
// Other legacy importers import via model managers which handle the posting of notifications.
|
||||
// Collections are an exception.
|
||||
PostNotification = n => notifications?.Post(n)
|
||||
}.ImportFromStorage(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion));
|
||||
}
|
||||
|
||||
if (content.HasFlagFast(StableContent.Scores))
|
||||
importTasks.Add(beatmapImportTask.ContinueWith(_ => new LegacyScoreImporter(scores).ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion));
|
||||
@ -116,7 +127,7 @@ namespace osu.Game.Database
|
||||
await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public StableStorage GetCurrentStableStorage()
|
||||
public StableStorage? GetCurrentStableStorage()
|
||||
{
|
||||
if (cachedStorage != null)
|
||||
return cachedStorage;
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Humanizer;
|
||||
using osu.Framework.Logging;
|
||||
@ -107,7 +108,15 @@ namespace osu.Game.Database
|
||||
notification.State = ProgressNotificationState.Cancelled;
|
||||
|
||||
if (!(error is OperationCanceledException))
|
||||
Logger.Error(error, $"{importer.HumanisedModelName.Titleize()} download failed!");
|
||||
{
|
||||
if (error is WebException webException && webException.Message == @"TooManyRequests")
|
||||
{
|
||||
notification.Close();
|
||||
PostNotification?.Invoke(new TooManyDownloadsNotification());
|
||||
}
|
||||
else
|
||||
Logger.Error(error, $"{importer.HumanisedModelName.Titleize()} download failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Models;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
@ -79,7 +80,7 @@ namespace osu.Game.Database
|
||||
/// </summary>
|
||||
public void AddFile(TModel item, Stream contents, string filename, Realm realm)
|
||||
{
|
||||
var existing = item.Files.FirstOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase));
|
||||
var existing = item.GetFile(filename);
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
|
@ -25,6 +25,7 @@ using osu.Game.Configuration;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Models;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using Realms;
|
||||
@ -173,6 +174,11 @@ namespace osu.Game.Database
|
||||
if (!Filename.EndsWith(realm_extension, StringComparison.Ordinal))
|
||||
Filename += realm_extension;
|
||||
|
||||
#if DEBUG
|
||||
if (!DebugUtils.IsNUnitRunning)
|
||||
applyFilenameSchemaSuffix(ref Filename);
|
||||
#endif
|
||||
|
||||
string newerVersionFilename = $"{Filename.Replace(realm_extension, string.Empty)}_newer_version{realm_extension}";
|
||||
|
||||
// Attempt to recover a newer database version if available.
|
||||
@ -212,6 +218,51 @@ namespace osu.Game.Database
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Some developers may be annoyed if a newer version migration (ie. caused by testing a pull request)
|
||||
/// cause their test database to be unusable with previous versions.
|
||||
/// To get around this, store development databases against their realm version.
|
||||
/// Note that this means changes made on newer realm versions will disappear.
|
||||
/// </summary>
|
||||
private void applyFilenameSchemaSuffix(ref string filename)
|
||||
{
|
||||
string originalFilename = filename;
|
||||
|
||||
filename = getVersionedFilename(schema_version);
|
||||
|
||||
// First check if the current realm version already exists...
|
||||
if (storage.Exists(filename))
|
||||
return;
|
||||
|
||||
// Check for a previous version we can use as a base database to migrate from...
|
||||
for (int i = schema_version - 1; i >= 0; i--)
|
||||
{
|
||||
string previousFilename = getVersionedFilename(i);
|
||||
|
||||
if (storage.Exists(previousFilename))
|
||||
{
|
||||
copyPreviousVersion(previousFilename, filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, check for a non-versioned file exists (aka before this method was added)...
|
||||
if (storage.Exists(originalFilename))
|
||||
copyPreviousVersion(originalFilename, filename);
|
||||
|
||||
void copyPreviousVersion(string previousFilename, string newFilename)
|
||||
{
|
||||
using (var previous = storage.GetStream(previousFilename))
|
||||
using (var current = storage.CreateFileSafely(newFilename))
|
||||
{
|
||||
Logger.Log(@$"Copying previous realm database {previousFilename} to {newFilename} for migration to schema version {schema_version}");
|
||||
previous.CopyTo(current);
|
||||
}
|
||||
}
|
||||
|
||||
string getVersionedFilename(int version) => originalFilename.Replace(realm_extension, $"_{version}{realm_extension}");
|
||||
}
|
||||
|
||||
private void attemptRecoverFromFile(string recoveryFilename)
|
||||
{
|
||||
Logger.Log($@"Performing recovery from {recoveryFilename}", LoggingTarget.Database);
|
||||
@ -293,6 +344,11 @@ namespace osu.Game.Database
|
||||
foreach (var s in pendingDeleteSkins)
|
||||
realm.Remove(s);
|
||||
|
||||
var pendingDeletePresets = realm.All<ModPreset>().Where(s => s.DeletePending);
|
||||
|
||||
foreach (var s in pendingDeletePresets)
|
||||
realm.Remove(s);
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
|
||||
|
26
osu.Game/Database/TooManyDownloadsNotification.cs
Normal file
26
osu.Game/Database/TooManyDownloadsNotification.cs
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
public class TooManyDownloadsNotification : SimpleNotification
|
||||
{
|
||||
public TooManyDownloadsNotification()
|
||||
{
|
||||
Text = BeatmapsetsStrings.DownloadLimitExceeded;
|
||||
Icon = FontAwesome.Solid.ExclamationCircle;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
IconBackground.Colour = colours.RedDark;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user