mirror of
https://github.com/ppy/osu.git
synced 2024-11-06 06:57:39 +08:00
Make deletion and purging logic even more global
This commit is contained in:
parent
d340509b1d
commit
d3dd31dadb
@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
if (deleteMaps)
|
||||
{
|
||||
manager.DeleteAll();
|
||||
manager.Delete(manager.GetAllUsableBeatmapSets());
|
||||
game.Beatmap.SetDefault();
|
||||
}
|
||||
|
||||
|
@ -71,8 +71,6 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
this.rulesets = rulesets;
|
||||
this.api = api;
|
||||
|
||||
beatmaps.Cleanup();
|
||||
}
|
||||
|
||||
protected override void Populate(BeatmapSetInfo model, ArchiveReader archive)
|
||||
@ -102,7 +100,7 @@ namespace osu.Game.Beatmaps
|
||||
if (existingOnlineId != null)
|
||||
{
|
||||
Delete(existingOnlineId);
|
||||
beatmaps.Cleanup(s => s.ID == existingOnlineId.ID);
|
||||
beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,12 +191,6 @@ namespace osu.Game.Beatmaps
|
||||
/// <returns>The <see cref="DownloadBeatmapSetRequest"/> object if it exists, or null.</returns>
|
||||
public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
|
||||
|
||||
/// <summary>
|
||||
/// Update a BeatmapSetInfo with all changes. TODO: This only supports very basic updates currently.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSet">The beatmap set to update.</param>
|
||||
public void Update(BeatmapSetInfo beatmap) => beatmaps.Update(beatmap);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a beatmap difficulty.
|
||||
/// </summary>
|
||||
@ -239,13 +231,6 @@ namespace osu.Game.Beatmaps
|
||||
/// <returns>The first result for the provided query, or null if no results were found.</returns>
|
||||
public BeatmapSetInfo QueryBeatmapSet(Expression<Func<BeatmapSetInfo, bool>> query) => beatmaps.BeatmapSets.AsNoTracking().FirstOrDefault(query);
|
||||
|
||||
/// <summary>
|
||||
/// Refresh an existing instance of a <see cref="BeatmapSetInfo"/> from the store.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSet">A stale instance.</param>
|
||||
/// <returns>A fresh instance.</returns>
|
||||
public BeatmapSetInfo Refresh(BeatmapSetInfo beatmapSet) => QueryBeatmapSet(s => s.ID == beatmapSet.ID);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all usable <see cref="BeatmapSetInfo"/>s.
|
||||
/// </summary>
|
||||
@ -294,41 +279,6 @@ namespace osu.Game.Beatmaps
|
||||
await Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs")), TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete all beatmaps.
|
||||
/// This will post notifications tracking progress.
|
||||
/// </summary>
|
||||
public void DeleteAll()
|
||||
{
|
||||
var maps = GetAllUsableBeatmapSets();
|
||||
|
||||
if (maps.Count == 0) return;
|
||||
|
||||
var notification = new ProgressNotification
|
||||
{
|
||||
Progress = 0,
|
||||
CompletionText = "Deleted all beatmaps!",
|
||||
State = ProgressNotificationState.Active,
|
||||
};
|
||||
|
||||
PostNotification?.Invoke(notification);
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (var b in maps)
|
||||
{
|
||||
if (notification.State == ProgressNotificationState.Cancelled)
|
||||
// user requested abort
|
||||
return;
|
||||
|
||||
notification.Text = $"Deleting ({i} of {maps.Count})";
|
||||
notification.Progress = (float)++i / maps.Count;
|
||||
Delete(b);
|
||||
}
|
||||
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a SHA-2 hash from the provided archive based on contained beatmap (.osu) file content.
|
||||
/// </summary>
|
||||
|
@ -2,8 +2,8 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Game.Database;
|
||||
|
||||
@ -63,32 +63,24 @@ namespace osu.Game.Beatmaps
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Cleanup() => Cleanup(_ => true);
|
||||
|
||||
public void Cleanup(Expression<Func<BeatmapSetInfo, bool>> query)
|
||||
protected override IQueryable<BeatmapSetInfo> AddIncludesForDeletion(IQueryable<BeatmapSetInfo> query)
|
||||
{
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
{
|
||||
var context = usage.Context;
|
||||
return base.AddIncludesForDeletion(query)
|
||||
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
|
||||
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
|
||||
.Include(s => s.Metadata);
|
||||
}
|
||||
|
||||
var purgeable = context.BeatmapSetInfo.Where(s => s.DeletePending && !s.Protected)
|
||||
.Where(query)
|
||||
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
|
||||
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
|
||||
.Include(s => s.Metadata).ToList();
|
||||
protected override void Purge(List<BeatmapSetInfo> items, OsuDbContext context)
|
||||
{
|
||||
// metadata is M-N so we can't rely on cascades
|
||||
context.BeatmapMetadata.RemoveRange(items.Select(s => s.Metadata));
|
||||
context.BeatmapMetadata.RemoveRange(items.SelectMany(s => s.Beatmaps.Select(b => b.Metadata).Where(m => m != null)));
|
||||
|
||||
if (!purgeable.Any()) return;
|
||||
// todo: we can probably make cascades work here with a FK in BeatmapDifficulty. just make to make it work correctly.
|
||||
context.BeatmapDifficulty.RemoveRange(items.SelectMany(s => s.Beatmaps.Select(b => b.BaseDifficulty)));
|
||||
|
||||
// metadata is M-N so we can't rely on cascades
|
||||
context.BeatmapMetadata.RemoveRange(purgeable.Select(s => s.Metadata));
|
||||
context.BeatmapMetadata.RemoveRange(purgeable.SelectMany(s => s.Beatmaps.Select(b => b.Metadata).Where(m => m != null)));
|
||||
|
||||
// todo: we can probably make cascades work here with a FK in BeatmapDifficulty. just make to make it work correctly.
|
||||
context.BeatmapDifficulty.RemoveRange(purgeable.SelectMany(s => s.Beatmaps.Select(b => b.BaseDifficulty)));
|
||||
|
||||
// cascades down to beatmaps.
|
||||
context.BeatmapSetInfo.RemoveRange(purgeable);
|
||||
}
|
||||
base.Purge(items, context);
|
||||
}
|
||||
|
||||
public IQueryable<BeatmapSetInfo> BeatmapSets => ContextFactory.Get().BeatmapSetInfo
|
||||
|
@ -62,6 +62,8 @@ namespace osu.Game.Database
|
||||
|
||||
if (importHost != null)
|
||||
ipc = new ArchiveImportIPCChannel(importHost, this);
|
||||
|
||||
ModelStore.PurgeDeletable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -154,6 +156,13 @@ namespace osu.Game.Database
|
||||
/// <param name="item">The model to be imported.</param>
|
||||
public void Import(TModel item) => ModelStore.Add(item);
|
||||
|
||||
/// <summary>
|
||||
/// Perform an update of the specified item.
|
||||
/// TODO: Support file changes.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to update.</param>
|
||||
public void Update(TModel item) => ModelStore.Update(item);
|
||||
|
||||
/// <summary>
|
||||
/// Delete an item from the manager.
|
||||
/// Is a no-op for already deleted items.
|
||||
@ -180,14 +189,48 @@ namespace osu.Game.Database
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore all items that were previously deleted.
|
||||
/// Delete multiple items.
|
||||
/// This will post notifications tracking progress.
|
||||
/// </summary>
|
||||
public void UndeleteAll()
|
||||
public void Delete(List<TModel> items)
|
||||
{
|
||||
var deletedItems = queryModel().Where(m => m.DeletePending).ToList();
|
||||
if (items.Count == 0) return;
|
||||
|
||||
if (!deletedItems.Any()) return;
|
||||
var notification = new ProgressNotification
|
||||
{
|
||||
Progress = 0,
|
||||
CompletionText = "Deleted all beatmaps!",
|
||||
State = ProgressNotificationState.Active,
|
||||
};
|
||||
|
||||
PostNotification?.Invoke(notification);
|
||||
|
||||
int i = 0;
|
||||
|
||||
using (ContextFactory.GetForWrite())
|
||||
{
|
||||
foreach (var b in items)
|
||||
{
|
||||
if (notification.State == ProgressNotificationState.Cancelled)
|
||||
// user requested abort
|
||||
return;
|
||||
|
||||
notification.Text = $"Deleting ({i} of {items.Count})";
|
||||
notification.Progress = (float)++i / items.Count;
|
||||
Delete(b);
|
||||
}
|
||||
}
|
||||
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore multiple items that were previously deleted.
|
||||
/// This will post notifications tracking progress.
|
||||
/// </summary>
|
||||
public void Undelete(List<TModel> items)
|
||||
{
|
||||
if (!items.Any()) return;
|
||||
|
||||
var notification = new ProgressNotification
|
||||
{
|
||||
@ -200,15 +243,18 @@ namespace osu.Game.Database
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (var item in deletedItems)
|
||||
using (ContextFactory.GetForWrite())
|
||||
{
|
||||
if (notification.State == ProgressNotificationState.Cancelled)
|
||||
// user requested abort
|
||||
return;
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (notification.State == ProgressNotificationState.Cancelled)
|
||||
// user requested abort
|
||||
return;
|
||||
|
||||
notification.Text = $"Restoring ({i} of {deletedItems.Count})";
|
||||
notification.Progress = (float)++i / deletedItems.Count;
|
||||
Undelete(item);
|
||||
notification.Text = $"Restoring ({i} of {items.Count})";
|
||||
notification.Progress = (float)++i / items.Count;
|
||||
Undelete(item);
|
||||
}
|
||||
}
|
||||
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Database
|
||||
/// <summary>
|
||||
/// Perform any common clean-up tasks. Should be run when idle, or whenever necessary.
|
||||
/// </summary>
|
||||
public virtual void Cleanup()
|
||||
public virtual void PurgeDeletable()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace osu.Game.Database
|
||||
@ -72,5 +75,37 @@ namespace osu.Game.Database
|
||||
ItemAdded?.Invoke(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual IQueryable<T> AddIncludesForDeletion(IQueryable<T> query) => query;
|
||||
|
||||
protected virtual void Purge(List<T> items, OsuDbContext context)
|
||||
{
|
||||
// cascades down to beatmaps.
|
||||
context.RemoveRange(items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Purge items in a pending delete state.
|
||||
/// </summary>
|
||||
/// <param name="query">An optional query limiting the scope of the purge.</param>
|
||||
public void PurgeDeletable(Expression<Func<T, bool>> query = null)
|
||||
{
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
{
|
||||
var context = usage.Context;
|
||||
|
||||
var lookup = context.Set<T>().Where(s => s.DeletePending);
|
||||
|
||||
if (query != null) lookup = lookup.Where(query);
|
||||
|
||||
AddIncludesForDeletion(lookup);
|
||||
|
||||
var purgeable = lookup.ToList();
|
||||
|
||||
if (!purgeable.Any()) return;
|
||||
|
||||
Purge(purgeable, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ namespace osu.Game.IO
|
||||
}
|
||||
}
|
||||
|
||||
public override void Cleanup()
|
||||
public override void PurgeDeletable()
|
||||
{
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
{
|
||||
|
@ -172,7 +172,7 @@ namespace osu.Game
|
||||
|
||||
API.Register(this);
|
||||
|
||||
FileStore.Cleanup();
|
||||
FileStore.PurgeDeletable();
|
||||
}
|
||||
|
||||
private void runMigrations()
|
||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() =>
|
||||
{
|
||||
deleteButton.Enabled.Value = false;
|
||||
Task.Run(() => beatmaps.DeleteAll()).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true));
|
||||
Task.Run(() => beatmaps.Delete(beatmaps.GetAllUsableBeatmapSets())).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true));
|
||||
}));
|
||||
}
|
||||
},
|
||||
@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
Action = () =>
|
||||
{
|
||||
undeleteButton.Enabled.Value = false;
|
||||
Task.Run(() => beatmaps.UndeleteAll()).ContinueWith(t => Schedule(() => undeleteButton.Enabled.Value = true));
|
||||
Task.Run(() => beatmaps.Undelete(beatmaps.QueryBeatmapSet(b => b.DeletePending))).ContinueWith(t => Schedule(() => undeleteButton.Enabled.Value = true));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user