mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 03:22:57 +08:00
Move ArchiveReaders to a more global namespace
Also moves delete and action logic to a shared implementation
This commit is contained in:
parent
6ff63c2f0c
commit
d340509b1d
@ -5,9 +5,9 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.IO;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.IO.Archives;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.IO
|
||||
{
|
||||
@ -19,7 +19,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
{
|
||||
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
|
||||
{
|
||||
var reader = new OszArchiveReader(osz);
|
||||
var reader = new ZipArchiveReader(osz);
|
||||
string[] expected =
|
||||
{
|
||||
"Soleily - Renatus (Deif) [Platter].osu",
|
||||
@ -46,7 +46,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
{
|
||||
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
|
||||
{
|
||||
var reader = new OszArchiveReader(osz);
|
||||
var reader = new ZipArchiveReader(osz);
|
||||
|
||||
BeatmapMetadata meta;
|
||||
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
|
||||
@ -71,7 +71,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
{
|
||||
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
|
||||
{
|
||||
var reader = new OszArchiveReader(osz);
|
||||
var reader = new ZipArchiveReader(osz);
|
||||
using (var stream = new StreamReader(
|
||||
reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
|
||||
{
|
||||
|
@ -12,9 +12,9 @@ using osu.Framework.Extensions;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Beatmaps.IO;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
@ -25,23 +25,13 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps.
|
||||
/// </summary>
|
||||
public partial class BeatmapManager : ArchiveModelImportManager<BeatmapSetInfo, BeatmapSetFileInfo>
|
||||
public partial class BeatmapManager : ArchiveModelManager<BeatmapSetInfo, BeatmapSetFileInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// Fired when a new <see cref="BeatmapSetInfo"/> becomes available in the database.
|
||||
/// </summary>
|
||||
public event Action<BeatmapSetInfo> BeatmapSetAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a single difficulty has been hidden.
|
||||
/// </summary>
|
||||
public event Action<BeatmapInfo> BeatmapHidden;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a <see cref="BeatmapSetInfo"/> is removed from the database.
|
||||
/// </summary>
|
||||
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a single difficulty has been restored.
|
||||
/// </summary>
|
||||
@ -76,8 +66,6 @@ namespace osu.Game.Beatmaps
|
||||
: base(storage, contextFactory, new BeatmapStore(contextFactory), importHost)
|
||||
{
|
||||
beatmaps = (BeatmapStore)ModelStore;
|
||||
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
|
||||
beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
|
||||
beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
|
||||
beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
|
||||
|
||||
@ -121,12 +109,6 @@ namespace osu.Game.Beatmaps
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Import a beatmap from a <see cref="BeatmapSetInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSet">The beatmap to be imported.</param>
|
||||
public void Import(BeatmapSetInfo beatmapSet) => beatmaps.Add(beatmapSet);
|
||||
|
||||
/// <summary>
|
||||
/// Downloads a beatmap.
|
||||
/// This will post notifications tracking progress.
|
||||
@ -171,7 +153,7 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
// This gets scheduled back to the update thread, but we want the import to run in the background.
|
||||
using (var stream = new MemoryStream(data))
|
||||
using (var archive = new OszArchiveReader(stream, beatmapSetInfo.ToString()))
|
||||
using (var archive = new ZipArchiveReader(stream, beatmapSetInfo.ToString()))
|
||||
Import(archive);
|
||||
|
||||
downloadNotification.State = ProgressNotificationState.Completed;
|
||||
@ -217,63 +199,6 @@ namespace osu.Game.Beatmaps
|
||||
/// <param name="beatmapSet">The beatmap set to update.</param>
|
||||
public void Update(BeatmapSetInfo beatmap) => beatmaps.Update(beatmap);
|
||||
|
||||
/// <summary>
|
||||
/// Restore all beatmaps that were previously deleted.
|
||||
/// This will post notifications tracking progress.
|
||||
/// </summary>
|
||||
public void UndeleteAll()
|
||||
{
|
||||
var deleteMaps = QueryBeatmapSets(bs => bs.DeletePending).ToList();
|
||||
|
||||
if (!deleteMaps.Any()) return;
|
||||
|
||||
var notification = new ProgressNotification
|
||||
{
|
||||
CompletionText = "Restored all deleted beatmaps!",
|
||||
Progress = 0,
|
||||
State = ProgressNotificationState.Active,
|
||||
};
|
||||
|
||||
PostNotification?.Invoke(notification);
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (var bs in deleteMaps)
|
||||
{
|
||||
if (notification.State == ProgressNotificationState.Cancelled)
|
||||
// user requested abort
|
||||
return;
|
||||
|
||||
notification.Text = $"Restoring ({i} of {deleteMaps.Count})";
|
||||
notification.Progress = (float)++i / deleteMaps.Count;
|
||||
Undelete(bs);
|
||||
}
|
||||
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore a beatmap that was previously deleted. Is a no-op if the beatmap is not in a deleted state, or has its protected flag set.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSet">The beatmap to restore</param>
|
||||
public void Undelete(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
if (beatmapSet.Protected)
|
||||
return;
|
||||
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
{
|
||||
usage.Context.ChangeTracker.AutoDetectChangesEnabled = false;
|
||||
|
||||
if (!beatmaps.Undelete(beatmapSet)) return;
|
||||
|
||||
if (!beatmapSet.Protected)
|
||||
Files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
|
||||
|
||||
usage.Context.ChangeTracker.AutoDetectChangesEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete a beatmap difficulty.
|
||||
/// </summary>
|
||||
|
@ -6,18 +6,14 @@ using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing
|
||||
/// </summary>
|
||||
public class BeatmapStore : DatabaseBackedStore, IMutableStore<BeatmapSetInfo>
|
||||
public class BeatmapStore : MutableDatabaseBackedStore<BeatmapSetInfo>
|
||||
{
|
||||
public event Action<BeatmapSetInfo> BeatmapSetAdded;
|
||||
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
|
||||
|
||||
public event Action<BeatmapInfo> BeatmapHidden;
|
||||
public event Action<BeatmapInfo> BeatmapRestored;
|
||||
|
||||
@ -26,88 +22,6 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a <see cref="BeatmapSetInfo"/> to the database.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSet">The beatmap to add.</param>
|
||||
public void Add(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
{
|
||||
var context = usage.Context;
|
||||
|
||||
foreach (var beatmap in beatmapSet.Beatmaps.Where(b => b.Metadata != null))
|
||||
{
|
||||
// If we detect a new metadata object it'll be attached to the current context so it can be reused
|
||||
// to prevent duplicate entries when persisting. To accomplish this we look in the cache (.Local)
|
||||
// of the corresponding table (.Set<BeatmapMetadata>()) for matching entries to our criteria.
|
||||
var contextMetadata = context.Set<BeatmapMetadata>().Local.SingleOrDefault(e => e.Equals(beatmap.Metadata));
|
||||
if (contextMetadata != null)
|
||||
beatmap.Metadata = contextMetadata;
|
||||
else
|
||||
context.BeatmapMetadata.Attach(beatmap.Metadata);
|
||||
}
|
||||
|
||||
context.BeatmapSetInfo.Attach(beatmapSet);
|
||||
|
||||
BeatmapSetAdded?.Invoke(beatmapSet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update a <see cref="BeatmapSetInfo"/> in the database. TODO: This only supports very basic updates currently.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSet">The beatmap to update.</param>
|
||||
public void Update(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
BeatmapSetRemoved?.Invoke(beatmapSet);
|
||||
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
usage.Context.BeatmapSetInfo.Update(beatmapSet);
|
||||
|
||||
BeatmapSetAdded?.Invoke(beatmapSet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete a <see cref="BeatmapSetInfo"/> from the database.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSet">The beatmap to delete.</param>
|
||||
/// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns>
|
||||
public bool Delete(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
using (ContextFactory.GetForWrite())
|
||||
{
|
||||
Refresh(ref beatmapSet, BeatmapSets);
|
||||
|
||||
if (beatmapSet.Protected || beatmapSet.DeletePending) return false;
|
||||
|
||||
beatmapSet.DeletePending = true;
|
||||
}
|
||||
|
||||
BeatmapSetRemoved?.Invoke(beatmapSet);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore a previously deleted <see cref="BeatmapSetInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSet">The beatmap to restore.</param>
|
||||
/// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns>
|
||||
public bool Undelete(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
using (ContextFactory.GetForWrite())
|
||||
{
|
||||
Refresh(ref beatmapSet, BeatmapSets);
|
||||
|
||||
if (!beatmapSet.DeletePending) return false;
|
||||
|
||||
beatmapSet.DeletePending = false;
|
||||
}
|
||||
|
||||
BeatmapSetAdded?.Invoke(beatmapSet);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide a <see cref="BeatmapInfo"/> in the database.
|
||||
/// </summary>
|
||||
|
@ -6,16 +6,21 @@ using Ionic.Zip;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.IO;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.IPC;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using FileInfo = osu.Game.IO.FileInfo;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
public abstract class ArchiveModelImportManager<TModel, TFileModel> : ICanImportArchives
|
||||
/// <summary>
|
||||
/// Encapsulates a model store class to give it import functionality.
|
||||
/// Adds cross-functionality with <see cref="FileStore"/> to give access to the central file store for the provided model.
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">The model type.</typeparam>
|
||||
/// <typeparam name="TFileModel">The associated file join type.</typeparam>
|
||||
public abstract class ArchiveModelManager<TModel, TFileModel> : ICanImportArchives
|
||||
where TModel : class, IHasFiles<TFileModel>, IHasPrimaryKey, ISoftDelete
|
||||
where TFileModel : INamedFileInfo, new()
|
||||
{
|
||||
@ -24,21 +29,35 @@ namespace osu.Game.Database
|
||||
/// </summary>
|
||||
public Action<Notification> PostNotification { protected get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a new <see cref="TModel"/> becomes available in the database.
|
||||
/// </summary>
|
||||
public event Action<TModel> ItemAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a <see cref="TModel"/> is removed from the database.
|
||||
/// </summary>
|
||||
public event Action<TModel> ItemRemoved;
|
||||
|
||||
public virtual string[] HandledExtensions => new[] { ".zip" };
|
||||
|
||||
protected readonly FileStore Files;
|
||||
|
||||
protected readonly IDatabaseContextFactory ContextFactory;
|
||||
|
||||
protected readonly IMutableStore<TModel> ModelStore;
|
||||
protected readonly MutableDatabaseBackedStore<TModel> ModelStore;
|
||||
|
||||
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
|
||||
private ArchiveImportIPCChannel ipc;
|
||||
|
||||
protected ArchiveModelImportManager(Storage storage, IDatabaseContextFactory contextFactory, IMutableStore<TModel> modelStore, IIpcHost importHost = null)
|
||||
protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStore<TModel> modelStore, IIpcHost importHost = null)
|
||||
{
|
||||
ContextFactory = contextFactory;
|
||||
|
||||
ModelStore = modelStore;
|
||||
ModelStore.ItemAdded += s => ItemAdded?.Invoke(s);
|
||||
ModelStore.ItemRemoved += s => ItemRemoved?.Invoke(s);
|
||||
|
||||
Files = new FileStore(contextFactory, storage);
|
||||
|
||||
if (importHost != null)
|
||||
@ -46,10 +65,10 @@ namespace osu.Game.Database
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Import one or more <see cref="BeatmapSetInfo"/> from filesystem <paramref name="paths"/>.
|
||||
/// Import one or more <see cref="TModel"/> items from filesystem <paramref name="paths"/>.
|
||||
/// This will post notifications tracking progress.
|
||||
/// </summary>
|
||||
/// <param name="paths">One or more beatmap locations on disk.</param>
|
||||
/// <param name="paths">One or more archive locations on disk.</param>
|
||||
public void Import(params string[] paths)
|
||||
{
|
||||
var notification = new ProgressNotification
|
||||
@ -80,7 +99,7 @@ namespace osu.Game.Database
|
||||
notification.Progress = (float)++i / paths.Length;
|
||||
|
||||
// We may or may not want to delete the file depending on where it is stored.
|
||||
// e.g. reconstructing/repairing database with beatmaps from default storage.
|
||||
// e.g. reconstructing/repairing database with items from default storage.
|
||||
// Also, not always a single file, i.e. for LegacyFilesystemReader
|
||||
// TODO: Add a check to prevent files from storage to be deleted.
|
||||
try
|
||||
@ -96,7 +115,7 @@ namespace osu.Game.Database
|
||||
catch (Exception e)
|
||||
{
|
||||
e = e.InnerException ?? e;
|
||||
Logger.Error(e, $@"Could not import beatmap set ({Path.GetFileName(path)})");
|
||||
Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})");
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,37 +123,43 @@ namespace osu.Game.Database
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Import a model from an <see cref="ArchiveReader"/>.
|
||||
/// Import an item from an <see cref="ArchiveReader"/>.
|
||||
/// </summary>
|
||||
/// <param name="archive">The beatmap to be imported.</param>
|
||||
/// <param name="archive">The archive to be imported.</param>
|
||||
public TModel Import(ArchiveReader archive)
|
||||
{
|
||||
using (ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes.
|
||||
{
|
||||
// create a new set info (don't yet add to database)
|
||||
var model = CreateModel(archive);
|
||||
// create a new model (don't yet add to database)
|
||||
var item = CreateModel(archive);
|
||||
|
||||
var existing = CheckForExisting(model);
|
||||
var existing = CheckForExisting(item);
|
||||
|
||||
if (existing != null) return existing;
|
||||
|
||||
model.Files = createFileInfos(archive, Files);
|
||||
item.Files = createFileInfos(archive, Files);
|
||||
|
||||
Populate(model, archive);
|
||||
Populate(item, archive);
|
||||
|
||||
// import to store
|
||||
ModelStore.Add(model);
|
||||
ModelStore.Add(item);
|
||||
|
||||
return model;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete a model from the manager.
|
||||
/// Is a no-op for already deleted models.
|
||||
/// Import an item from a <see cref="TModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to delete.</param>
|
||||
public void Delete(TModel model)
|
||||
/// <param name="item">The model to be imported.</param>
|
||||
public void Import(TModel item) => ModelStore.Add(item);
|
||||
|
||||
/// <summary>
|
||||
/// Delete an item from the manager.
|
||||
/// Is a no-op for already deleted items.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to delete.</param>
|
||||
public void Delete(TModel item)
|
||||
{
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
{
|
||||
@ -143,9 +168,9 @@ namespace osu.Game.Database
|
||||
context.ChangeTracker.AutoDetectChangesEnabled = false;
|
||||
|
||||
// re-fetch the model on the import context.
|
||||
var foundModel = ContextFactory.Get().Set<TModel>().Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == model.ID);
|
||||
var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == item.ID);
|
||||
|
||||
if (foundModel.DeletePending || !CheckCanDelete(foundModel)) return;
|
||||
if (foundModel.DeletePending) return;
|
||||
|
||||
if (ModelStore.Delete(foundModel))
|
||||
Files.Dereference(foundModel.Files.Select(f => f.FileInfo).ToArray());
|
||||
@ -154,6 +179,59 @@ namespace osu.Game.Database
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore all items that were previously deleted.
|
||||
/// This will post notifications tracking progress.
|
||||
/// </summary>
|
||||
public void UndeleteAll()
|
||||
{
|
||||
var deletedItems = queryModel().Where(m => m.DeletePending).ToList();
|
||||
|
||||
if (!deletedItems.Any()) return;
|
||||
|
||||
var notification = new ProgressNotification
|
||||
{
|
||||
CompletionText = "Restored all deleted items!",
|
||||
Progress = 0,
|
||||
State = ProgressNotificationState.Active,
|
||||
};
|
||||
|
||||
PostNotification?.Invoke(notification);
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (var item in deletedItems)
|
||||
{
|
||||
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.State = ProgressNotificationState.Completed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore an item that was previously deleted. Is a no-op if the item is not in a deleted state, or has its protected flag set.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to restore</param>
|
||||
public void Undelete(TModel item)
|
||||
{
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
{
|
||||
usage.Context.ChangeTracker.AutoDetectChangesEnabled = false;
|
||||
|
||||
if (!ModelStore.Undelete(item)) return;
|
||||
|
||||
Files.Reference(item.Files.Select(f => f.FileInfo).ToArray());
|
||||
|
||||
usage.Context.ChangeTracker.AutoDetectChangesEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create all required <see cref="FileInfo"/>s for the provided archive, adding them to the global file store.
|
||||
/// </summary>
|
||||
@ -193,7 +271,7 @@ namespace osu.Game.Database
|
||||
|
||||
protected virtual TModel CheckForExisting(TModel model) => null;
|
||||
|
||||
protected virtual bool CheckCanDelete(TModel model) => true;
|
||||
private DbSet<TModel> queryModel() => ContextFactory.Get().Set<TModel>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ArchiveReader"/> from a valid storage path.
|
||||
@ -203,7 +281,7 @@ namespace osu.Game.Database
|
||||
private ArchiveReader getReaderFrom(string path)
|
||||
{
|
||||
if (ZipFile.IsZipFile(path))
|
||||
return new OszArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path));
|
||||
return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path));
|
||||
return new LegacyFilesystemReader(path);
|
||||
}
|
||||
}
|
76
osu.Game/Database/MutableDatabaseBackedStore.cs
Normal file
76
osu.Game/Database/MutableDatabaseBackedStore.cs
Normal file
@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// A typed store which supports basic addition, deletion and updating for soft-deletable models.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The databased model.</typeparam>
|
||||
public abstract class MutableDatabaseBackedStore<T> : DatabaseBackedStore
|
||||
where T : class, IHasPrimaryKey, ISoftDelete
|
||||
{
|
||||
public event Action<T> ItemAdded;
|
||||
public event Action<T> ItemRemoved;
|
||||
|
||||
protected MutableDatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null)
|
||||
: base(contextFactory, storage)
|
||||
{
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
{
|
||||
var context = usage.Context;
|
||||
context.Attach(item);
|
||||
}
|
||||
|
||||
ItemAdded?.Invoke(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update a <see cref="T"/> in the database.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to update.</param>
|
||||
public void Update(T item)
|
||||
{
|
||||
ItemRemoved?.Invoke(item);
|
||||
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
usage.Context.Update(item);
|
||||
|
||||
ItemAdded?.Invoke(item);
|
||||
}
|
||||
|
||||
public bool Delete(T item)
|
||||
{
|
||||
using (ContextFactory.GetForWrite())
|
||||
{
|
||||
Refresh(ref item);
|
||||
|
||||
if (item.DeletePending) return false;
|
||||
|
||||
item.DeletePending = true;
|
||||
}
|
||||
|
||||
ItemRemoved?.Invoke(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Undelete(T item)
|
||||
{
|
||||
using (ContextFactory.GetForWrite())
|
||||
{
|
||||
Refresh(ref item);
|
||||
|
||||
if (!item.DeletePending) return false;
|
||||
|
||||
item.DeletePending = false;
|
||||
}
|
||||
|
||||
ItemAdded?.Invoke(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using osu.Framework.IO.Stores;
|
||||
|
||||
namespace osu.Game.Beatmaps.IO
|
||||
namespace osu.Game.IO.Archives
|
||||
{
|
||||
public abstract class ArchiveReader : IDisposable, IResourceStore<byte[]>
|
||||
{
|
@ -1,12 +1,12 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.IO.File;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using osu.Framework.IO.File;
|
||||
|
||||
namespace osu.Game.Beatmaps.IO
|
||||
namespace osu.Game.IO.Archives
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads an extracted legacy beatmap from disk.
|
@ -6,14 +6,14 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using Ionic.Zip;
|
||||
|
||||
namespace osu.Game.Beatmaps.IO
|
||||
namespace osu.Game.IO.Archives
|
||||
{
|
||||
public sealed class OszArchiveReader : ArchiveReader
|
||||
public sealed class ZipArchiveReader : ArchiveReader
|
||||
{
|
||||
private readonly Stream archiveStream;
|
||||
private readonly ZipFile archive;
|
||||
|
||||
public OszArchiveReader(Stream archiveStream, string name = null)
|
||||
public ZipArchiveReader(Stream archiveStream, string name = null)
|
||||
: base(name)
|
||||
{
|
||||
this.archiveStream = archiveStream;
|
@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.IO
|
||||
{
|
||||
public interface IMutableStore<in T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Add an object to the store.
|
||||
/// </summary>
|
||||
/// <param name="object">The object to add.</param>
|
||||
void Add(T item);
|
||||
|
||||
bool Delete(T item);
|
||||
}
|
||||
}
|
@ -223,13 +223,13 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
tabsBg.Colour = colours.Gray3;
|
||||
this.beatmaps = beatmaps;
|
||||
|
||||
beatmaps.BeatmapSetAdded += handleBeatmapAdd;
|
||||
beatmaps.ItemAdded += handleBeatmapAdd;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
if (beatmaps != null) beatmaps.BeatmapSetAdded -= handleBeatmapAdd;
|
||||
if (beatmaps != null) beatmaps.ItemAdded -= handleBeatmapAdd;
|
||||
}
|
||||
|
||||
private void handleBeatmapAdd(BeatmapSetInfo beatmap)
|
||||
|
@ -185,7 +185,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
resultCountsContainer.Colour = colours.Yellow;
|
||||
|
||||
beatmaps.BeatmapSetAdded += setAdded;
|
||||
beatmaps.ItemAdded += setAdded;
|
||||
}
|
||||
|
||||
private void setAdded(BeatmapSetInfo set)
|
||||
|
@ -74,8 +74,8 @@ namespace osu.Game.Overlays.Music
|
||||
},
|
||||
};
|
||||
|
||||
beatmaps.BeatmapSetAdded += list.AddBeatmapSet;
|
||||
beatmaps.BeatmapSetRemoved += list.RemoveBeatmapSet;
|
||||
beatmaps.ItemAdded += list.AddBeatmapSet;
|
||||
beatmaps.ItemRemoved += list.RemoveBeatmapSet;
|
||||
|
||||
list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets();
|
||||
|
||||
|
@ -10,8 +10,8 @@ using osu.Framework.Screens;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.IO;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
@ -62,7 +62,7 @@ namespace osu.Game.Screens.Menu
|
||||
if (setInfo == null)
|
||||
{
|
||||
// we need to import the default menu background beatmap
|
||||
setInfo = beatmaps.Import(new OszArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz"), "circles.osz"));
|
||||
setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz"), "circles.osz"));
|
||||
|
||||
setInfo.Protected = true;
|
||||
beatmaps.Update(setInfo);
|
||||
|
@ -197,8 +197,8 @@ namespace osu.Game.Screens.Select
|
||||
if (osu != null)
|
||||
Ruleset.BindTo(osu.Ruleset);
|
||||
|
||||
this.beatmaps.BeatmapSetAdded += onBeatmapSetAdded;
|
||||
this.beatmaps.BeatmapSetRemoved += onBeatmapSetRemoved;
|
||||
this.beatmaps.ItemAdded += onBeatmapSetAdded;
|
||||
this.beatmaps.ItemRemoved += onBeatmapSetRemoved;
|
||||
this.beatmaps.BeatmapHidden += onBeatmapHidden;
|
||||
this.beatmaps.BeatmapRestored += onBeatmapRestored;
|
||||
|
||||
@ -401,8 +401,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (beatmaps != null)
|
||||
{
|
||||
beatmaps.BeatmapSetAdded -= onBeatmapSetAdded;
|
||||
beatmaps.BeatmapSetRemoved -= onBeatmapSetRemoved;
|
||||
beatmaps.ItemAdded -= onBeatmapSetAdded;
|
||||
beatmaps.ItemRemoved -= onBeatmapSetRemoved;
|
||||
beatmaps.BeatmapHidden -= onBeatmapHidden;
|
||||
beatmaps.BeatmapRestored -= onBeatmapRestored;
|
||||
}
|
||||
|
@ -274,7 +274,7 @@
|
||||
<Compile Include="Configuration\SettingsStore.cs" />
|
||||
<Compile Include="Configuration\DatabasedConfigManager.cs" />
|
||||
<Compile Include="Configuration\SpeedChangeVisualisationMethod.cs" />
|
||||
<Compile Include="Database\ArchiveModelImportManager.cs" />
|
||||
<Compile Include="Database\ArchiveModelManager.cs" />
|
||||
<Compile Include="Database\DatabaseContextFactory.cs" />
|
||||
<Compile Include="Database\DatabaseWriteUsage.cs" />
|
||||
<Compile Include="Database\ICanImportArchives.cs" />
|
||||
@ -282,10 +282,13 @@
|
||||
<Compile Include="Database\IHasPrimaryKey.cs" />
|
||||
<Compile Include="Database\INamedFileInfo.cs" />
|
||||
<Compile Include="Database\ISoftDelete.cs" />
|
||||
<Compile Include="Database\MutableDatabaseBackedStore.cs" />
|
||||
<Compile Include="Database\SingletonContextFactory.cs" />
|
||||
<Compile Include="Graphics\Containers\LinkFlowContainer.cs" />
|
||||
<Compile Include="Graphics\Textures\LargeTextureStore.cs" />
|
||||
<Compile Include="IO\IMutableStore.cs" />
|
||||
<Compile Include="IO\Archives\ArchiveReader.cs" />
|
||||
<Compile Include="IO\Archives\LegacyFilesystemReader.cs" />
|
||||
<Compile Include="IO\Archives\ZipArchiveReader.cs" />
|
||||
<Compile Include="IO\IHasFiles.cs" />
|
||||
<Compile Include="Online\API\APIDownloadRequest.cs" />
|
||||
<Compile Include="Online\API\Requests\GetUserRequest.cs" />
|
||||
@ -378,8 +381,6 @@
|
||||
<Compile Include="Beatmaps\DummyWorkingBeatmap.cs" />
|
||||
<Compile Include="Beatmaps\Formats\Decoder.cs" />
|
||||
<Compile Include="Beatmaps\Formats\LegacyBeatmapDecoder.cs" />
|
||||
<Compile Include="Beatmaps\IO\ArchiveReader.cs" />
|
||||
<Compile Include="Beatmaps\IO\LegacyFilesystemReader.cs" />
|
||||
<Compile Include="Online\API\Requests\GetUserScoresRequest.cs" />
|
||||
<Compile Include="Screens\Edit\Screens\Compose\RadioButtons\DrawableRadioButton.cs" />
|
||||
<Compile Include="Screens\Edit\Screens\Compose\RadioButtons\RadioButton.cs" />
|
||||
@ -394,7 +395,6 @@
|
||||
<Compile Include="Screens\Play\BreaksOverlay\InfoLine.cs" />
|
||||
<Compile Include="Screens\Play\BreaksOverlay\LetterboxOverlay.cs" />
|
||||
<Compile Include="Screens\Play\BreaksOverlay\RemainingTimeCounter.cs" />
|
||||
<Compile Include="Beatmaps\IO\OszArchiveReader.cs" />
|
||||
<Compile Include="Beatmaps\Legacy\LegacyBeatmap.cs" />
|
||||
<Compile Include="Beatmaps\RankStatus.cs" />
|
||||
<Compile Include="Beatmaps\Timing\BreakPeriod.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user