mirror of
https://github.com/osukey/osukey.git
synced 2025-08-06 08:03:52 +09:00
Merge pull request #3063 from peppy/fix-model-import
Fix ArchiveModelManager's model import method not running import logic
This commit is contained in:
@ -89,33 +89,46 @@ namespace osu.Game.Beatmaps
|
|||||||
this.audioManager = audioManager;
|
this.audioManager = audioManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Populate(BeatmapSetInfo model, ArchiveReader archive)
|
protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive)
|
||||||
{
|
{
|
||||||
model.Beatmaps = createBeatmapDifficulties(archive);
|
if (archive != null)
|
||||||
|
beatmapSet.Beatmaps = createBeatmapDifficulties(archive);
|
||||||
|
|
||||||
foreach (BeatmapInfo b in model.Beatmaps)
|
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
|
||||||
{
|
{
|
||||||
// remove metadata from difficulties where it matches the set
|
// remove metadata from difficulties where it matches the set
|
||||||
if (model.Metadata.Equals(b.Metadata))
|
if (beatmapSet.Metadata.Equals(b.Metadata))
|
||||||
b.Metadata = null;
|
b.Metadata = null;
|
||||||
|
|
||||||
// by setting the model here, we can update the noline set id below.
|
b.BeatmapSet = beatmapSet;
|
||||||
b.BeatmapSet = model;
|
|
||||||
|
|
||||||
fetchAndPopulateOnlineIDs(b, model.Beatmaps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if a set already exists with the same online id, delete if it does.
|
// check if a set already exists with the same online id, delete if it does.
|
||||||
if (model.OnlineBeatmapSetID != null)
|
if (beatmapSet.OnlineBeatmapSetID != null)
|
||||||
{
|
{
|
||||||
var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID);
|
var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID);
|
||||||
if (existingOnlineId != null)
|
if (existingOnlineId != null)
|
||||||
{
|
{
|
||||||
Delete(existingOnlineId);
|
Delete(existingOnlineId);
|
||||||
beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID);
|
beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID);
|
||||||
Logger.Log($"Found existing beatmap set with same OnlineBeatmapSetID ({model.OnlineBeatmapSetID}). It has been purged.", LoggingTarget.Database);
|
Logger.Log($"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been purged.", LoggingTarget.Database);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateOnlineIds(beatmapSet.Beatmaps);
|
||||||
|
|
||||||
|
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
|
||||||
|
fetchAndPopulateOnlineIDs(b, beatmapSet.Beatmaps);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateOnlineIds(List<BeatmapInfo> beatmaps)
|
||||||
|
{
|
||||||
|
var beatmapIds = beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList();
|
||||||
|
|
||||||
|
// ensure all IDs are unique in this set and none match existing IDs in the local beatmap store.
|
||||||
|
if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1) || QueryBeatmaps(b => beatmapIds.Contains(b.OnlineBeatmapID)).Any())
|
||||||
|
// remove all online IDs if any problems were found.
|
||||||
|
beatmaps.ForEach(b => b.OnlineBeatmapID = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override BeatmapSetInfo CheckForExisting(BeatmapSetInfo model)
|
protected override BeatmapSetInfo CheckForExisting(BeatmapSetInfo model)
|
||||||
@ -296,7 +309,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="query">The query.</param>
|
/// <param name="query">The query.</param>
|
||||||
/// <returns>Results from the provided query.</returns>
|
/// <returns>Results from the provided query.</returns>
|
||||||
public IEnumerable<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
|
public IQueryable<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Denotes whether an osu-stable installation is present to perform automated imports from.
|
/// Denotes whether an osu-stable installation is present to perform automated imports from.
|
||||||
@ -359,8 +372,6 @@ namespace osu.Game.Beatmaps
|
|||||||
{
|
{
|
||||||
var beatmapInfos = new List<BeatmapInfo>();
|
var beatmapInfos = new List<BeatmapInfo>();
|
||||||
|
|
||||||
bool invalidateOnlineIDs = false;
|
|
||||||
|
|
||||||
foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu")))
|
foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu")))
|
||||||
{
|
{
|
||||||
using (var raw = reader.GetStream(name))
|
using (var raw = reader.GetStream(name))
|
||||||
@ -377,38 +388,15 @@ namespace osu.Game.Beatmaps
|
|||||||
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
|
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
|
||||||
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
|
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
|
||||||
|
|
||||||
if (beatmap.BeatmapInfo.OnlineBeatmapID.HasValue)
|
var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
|
||||||
{
|
|
||||||
var ourId = beatmap.BeatmapInfo.OnlineBeatmapID;
|
|
||||||
|
|
||||||
// check that no existing beatmap in database exists that is imported with the same online beatmap ID. if so, give it precedence.
|
|
||||||
if (QueryBeatmap(b => b.OnlineBeatmapID.Value == ourId) != null)
|
|
||||||
beatmap.BeatmapInfo.OnlineBeatmapID = null;
|
|
||||||
|
|
||||||
// check that no other beatmap in this imported set has a conflicting online beatmap ID. If so, presume *all* are incorrect.
|
|
||||||
if (beatmapInfos.Any(b => b.OnlineBeatmapID == ourId))
|
|
||||||
invalidateOnlineIDs = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
|
|
||||||
|
|
||||||
beatmap.BeatmapInfo.Ruleset = ruleset;
|
beatmap.BeatmapInfo.Ruleset = ruleset;
|
||||||
|
// TODO: this should be done in a better place once we actually need to dynamically update it.
|
||||||
if (ruleset != null)
|
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0;
|
||||||
{
|
|
||||||
// TODO: this should be done in a better place once we actually need to dynamically update it.
|
|
||||||
beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
beatmap.BeatmapInfo.StarDifficulty = 0;
|
|
||||||
|
|
||||||
beatmapInfos.Add(beatmap.BeatmapInfo);
|
beatmapInfos.Add(beatmap.BeatmapInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invalidateOnlineIDs)
|
|
||||||
beatmapInfos.ForEach(b => b.OnlineBeatmapID = null);
|
|
||||||
|
|
||||||
return beatmapInfos;
|
return beatmapInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,12 +409,12 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <returns>True if population was successful.</returns>
|
/// <returns>True if population was successful.</returns>
|
||||||
private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, IEnumerable<BeatmapInfo> otherBeatmaps, bool force = false)
|
private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, IEnumerable<BeatmapInfo> otherBeatmaps, bool force = false)
|
||||||
{
|
{
|
||||||
|
if (api?.State != APIState.Online)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null)
|
if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (api.State != APIState.Online)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database);
|
Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using osu.Framework.IO.File;
|
using osu.Framework.IO.File;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
@ -175,7 +176,24 @@ namespace osu.Game.Database
|
|||||||
/// <param name="archive">The archive to be imported.</param>
|
/// <param name="archive">The archive to be imported.</param>
|
||||||
public TModel Import(ArchiveReader archive)
|
public TModel Import(ArchiveReader archive)
|
||||||
{
|
{
|
||||||
TModel item = null;
|
try
|
||||||
|
{
|
||||||
|
return Import(CreateModel(archive), archive);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error(e, $"Model creation of {archive.Name} failed.", LoggingTarget.Database);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Import an item from a <see cref="TModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The model to be imported.</param>
|
||||||
|
/// <param name="archive">An optional archive to use for model population.</param>
|
||||||
|
public TModel Import(TModel item, ArchiveReader archive = null)
|
||||||
|
{
|
||||||
delayEvents();
|
delayEvents();
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -186,18 +204,16 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
if (!write.IsTransactionLeader) throw new InvalidOperationException($"Ensure there is no parent transaction so errors can correctly be handled by {this}");
|
if (!write.IsTransactionLeader) throw new InvalidOperationException($"Ensure there is no parent transaction so errors can correctly be handled by {this}");
|
||||||
|
|
||||||
// create a new model (don't yet add to database)
|
|
||||||
item = CreateModel(archive);
|
|
||||||
|
|
||||||
var existing = CheckForExisting(item);
|
var existing = CheckForExisting(item);
|
||||||
|
|
||||||
if (existing != null)
|
if (existing != null)
|
||||||
{
|
{
|
||||||
Logger.Log($"Found existing {typeof(TModel)} for {archive.Name} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
|
Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
item.Files = createFileInfos(archive, Files);
|
if (archive != null)
|
||||||
|
item.Files = createFileInfos(archive, Files);
|
||||||
|
|
||||||
Populate(item, archive);
|
Populate(item, archive);
|
||||||
|
|
||||||
@ -211,11 +227,11 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Log($"Import of {archive.Name} successfully completed!", LoggingTarget.Database);
|
Logger.Log($"Import of {item} successfully completed!", LoggingTarget.Database);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Error(e, $"Import of {archive.Name} failed and has been rolled back.", LoggingTarget.Database);
|
Logger.Error(e, $"Import of {item} failed and has been rolled back.", LoggingTarget.Database);
|
||||||
item = null;
|
item = null;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@ -227,12 +243,6 @@ namespace osu.Game.Database
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Import an item from a <see cref="TModel"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">The model to be imported.</param>
|
|
||||||
public void Import(TModel item) => ModelStore.Add(item);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Perform an update of the specified item.
|
/// Perform an update of the specified item.
|
||||||
/// TODO: Support file changes.
|
/// TODO: Support file changes.
|
||||||
@ -385,8 +395,8 @@ namespace osu.Game.Database
|
|||||||
/// After this method, the model should be in a state ready to commit to a store.
|
/// After this method, the model should be in a state ready to commit to a store.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="model">The model to populate.</param>
|
/// <param name="model">The model to populate.</param>
|
||||||
/// <param name="archive">The archive to use as a reference for population.</param>
|
/// <param name="archive">The archive to use as a reference for population. May be null.</param>
|
||||||
protected virtual void Populate(TModel model, ArchiveReader archive)
|
protected virtual void Populate(TModel model, [CanBeNull] ArchiveReader archive)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user