diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 4ff16b604a..3bd5c440c1 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -89,34 +89,46 @@ public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, R this.audioManager = audioManager; } - protected override void Populate(BeatmapSetInfo model, ArchiveReader archive) + protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive) { if (archive != null) - model.Beatmaps = createBeatmapDifficulties(archive); + 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 - if (model.Metadata.Equals(b.Metadata)) + if (beatmapSet.Metadata.Equals(b.Metadata)) b.Metadata = null; - // by setting the model here, we can update the noline set id below. - b.BeatmapSet = model; - - fetchAndPopulateOnlineIDs(b, model.Beatmaps); + b.BeatmapSet = beatmapSet; } // 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) { Delete(existingOnlineId); 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 beatmaps) + { + var beatmapIds = beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID); + + // 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) @@ -297,7 +309,7 @@ public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap /// /// The query. /// Results from the provided query. - public IEnumerable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); + public IQueryable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); /// /// Denotes whether an osu-stable installation is present to perform automated imports from. @@ -360,8 +372,6 @@ private List createBeatmapDifficulties(ArchiveReader reader) { var beatmapInfos = new List(); - bool invalidateOnlineIDs = false; - foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu"))) { using (var raw = reader.GetStream(name)) @@ -378,38 +388,15 @@ private List createBeatmapDifficulties(ArchiveReader reader) beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); - if (beatmap.BeatmapInfo.OnlineBeatmapID.HasValue) - { - 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); - + var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); beatmap.BeatmapInfo.Ruleset = ruleset; - - if (ruleset != null) - { - // 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; + // 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 ?? 0; beatmapInfos.Add(beatmap.BeatmapInfo); } } - if (invalidateOnlineIDs) - beatmapInfos.ForEach(b => b.OnlineBeatmapID = null); - return beatmapInfos; } @@ -422,12 +409,12 @@ private List createBeatmapDifficulties(ArchiveReader reader) /// True if population was successful. private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, IEnumerable otherBeatmaps, bool force = false) { + if (api?.State != APIState.Online) + return false; + if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null) return true; - if (api.State != APIState.Online) - return false; - Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database); try