mirror of
https://github.com/ppy/osu
synced 2024-12-27 17:32:56 +00:00
Merge remote-tracking branch 'upstream/master' into fix-mod-retention
This commit is contained in:
commit
b8bb97607b
4
CodeAnalysis/BannedSymbols.txt
Normal file
4
CodeAnalysis/BannedSymbols.txt
Normal file
@ -0,0 +1,4 @@
|
||||
M:System.Object.Equals(System.Object,System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
||||
M:System.Object.Equals(System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
||||
M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(Fallbacks to ValueType). Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
||||
T:System.IComparable;Don't use non-generic IComparable. Use generic version instead.
|
@ -14,6 +14,10 @@
|
||||
<ItemGroup Label="Resources">
|
||||
<EmbeddedResource Include="Resources\**\*.*" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Code Analysis">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="2.9.7" PrivateAssets="All" />
|
||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<!-- DeepEqual is not netstandard-compatible. This is fine since we run tests with .NET Framework anyway.
|
||||
This is required due to https://github.com/NuGet/Home/issues/5740 -->
|
||||
|
@ -4,7 +4,10 @@
|
||||
|
||||
# osu!
|
||||
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu)
|
||||
[![GitHub release](https://img.shields.io/github/release/ppy/osu.svg)]()
|
||||
[![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu)
|
||||
[![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy)
|
||||
|
||||
Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Commonly known by the codename "osu!lazer". Pew pew.
|
||||
|
||||
|
@ -411,6 +411,48 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestImportWithDuplicateHashes()
|
||||
{
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportNestedStructure)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var osu = loadOsu(host);
|
||||
|
||||
var temp = TestResources.GetTestBeatmapForImport();
|
||||
|
||||
string extractedFolder = $"{temp}_extracted";
|
||||
Directory.CreateDirectory(extractedFolder);
|
||||
|
||||
try
|
||||
{
|
||||
using (var zip = ZipArchive.Open(temp))
|
||||
zip.WriteToDirectory(extractedFolder);
|
||||
|
||||
using (var zip = ZipArchive.Create())
|
||||
{
|
||||
zip.AddAllFromDirectory(extractedFolder);
|
||||
zip.AddEntry("duplicate.osu", Directory.GetFiles(extractedFolder, "*.osu").First());
|
||||
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
|
||||
}
|
||||
|
||||
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
||||
|
||||
ensureLoaded(osu);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(extractedFolder, true);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
host.Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestImportNestedStructure()
|
||||
{
|
||||
|
@ -10,6 +10,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
@ -51,11 +52,6 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
this.rulesets = rulesets;
|
||||
|
||||
Add(carousel = new TestBeatmapCarousel
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -338,10 +334,19 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
[Test]
|
||||
public void TestHiding()
|
||||
{
|
||||
BeatmapSetInfo hidingSet = createTestBeatmapSet(1);
|
||||
hidingSet.Beatmaps[1].Hidden = true;
|
||||
BeatmapSetInfo hidingSet = null;
|
||||
List<BeatmapSetInfo> hiddenList = new List<BeatmapSetInfo>();
|
||||
|
||||
loadBeatmaps(new List<BeatmapSetInfo> { hidingSet });
|
||||
AddStep("create hidden set", () =>
|
||||
{
|
||||
hidingSet = createTestBeatmapSet(1);
|
||||
hidingSet.Beatmaps[1].Hidden = true;
|
||||
|
||||
hiddenList.Clear();
|
||||
hiddenList.Add(hidingSet);
|
||||
});
|
||||
|
||||
loadBeatmaps(hiddenList);
|
||||
|
||||
setSelected(1, 1);
|
||||
|
||||
@ -375,9 +380,14 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
[Test]
|
||||
public void TestSelectingFilteredRuleset()
|
||||
{
|
||||
var testMixed = createTestBeatmapSet(set_count + 1);
|
||||
BeatmapSetInfo testMixed = null;
|
||||
|
||||
createCarousel();
|
||||
|
||||
AddStep("add mixed ruleset beatmapset", () =>
|
||||
{
|
||||
testMixed = createTestBeatmapSet(set_count + 1);
|
||||
|
||||
for (int i = 0; i <= 2; i++)
|
||||
{
|
||||
testMixed.Beatmaps[i].Ruleset = rulesets.AvailableRulesets.ElementAt(i);
|
||||
@ -429,6 +439,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null)
|
||||
{
|
||||
createCarousel();
|
||||
|
||||
if (beatmapSets == null)
|
||||
{
|
||||
beatmapSets = new List<BeatmapSetInfo>();
|
||||
@ -448,6 +460,20 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddUntilStep("Wait for load", () => changed);
|
||||
}
|
||||
|
||||
private void createCarousel(Container target = null)
|
||||
{
|
||||
AddStep("Create carousel", () =>
|
||||
{
|
||||
selectedSets.Clear();
|
||||
eagerSelectedIDs.Clear();
|
||||
|
||||
(target ?? this).Child = carousel = new TestBeatmapCarousel
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void ensureRandomFetchSuccess() =>
|
||||
AddAssert("ensure prev random fetch worked", () => selectedSets.Peek() == carousel.SelectedBeatmapSet);
|
||||
|
||||
|
@ -129,9 +129,12 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
var beatmapIds = beatmapSet.Beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList();
|
||||
|
||||
LogForModel(beatmapSet, "Validating online IDs...");
|
||||
|
||||
// ensure all IDs are unique
|
||||
if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1))
|
||||
{
|
||||
LogForModel(beatmapSet, "Found non-unique IDs, resetting...");
|
||||
resetIds();
|
||||
return;
|
||||
}
|
||||
@ -144,8 +147,12 @@ namespace osu.Game.Beatmaps
|
||||
// reset the import ids (to force a re-fetch) *unless* they match the candidate CheckForExisting set.
|
||||
// we can ignore the case where the new ids are contained by the CheckForExisting set as it will either be used (import skipped) or deleted.
|
||||
var existing = CheckForExisting(beatmapSet);
|
||||
|
||||
if (existing == null || existingBeatmaps.Any(b => !existing.Beatmaps.Contains(b)))
|
||||
{
|
||||
LogForModel(beatmapSet, "Found existing import with IDs already, resetting...");
|
||||
resetIds();
|
||||
}
|
||||
}
|
||||
|
||||
void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineBeatmapID = null);
|
||||
@ -296,8 +303,13 @@ namespace osu.Game.Beatmaps
|
||||
var decoder = Decoder.GetDecoder<Beatmap>(sr);
|
||||
IBeatmap beatmap = decoder.Decode(sr);
|
||||
|
||||
string hash = ms.ComputeSHA2Hash();
|
||||
|
||||
if (beatmapInfos.Any(b => b.Hash == hash))
|
||||
continue;
|
||||
|
||||
beatmap.BeatmapInfo.Path = file.Filename;
|
||||
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
|
||||
beatmap.BeatmapInfo.Hash = hash;
|
||||
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
|
||||
|
||||
var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
|
||||
@ -380,25 +392,30 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
var req = new GetBeatmapRequest(beatmap);
|
||||
|
||||
req.Success += res =>
|
||||
{
|
||||
LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
|
||||
|
||||
beatmap.Status = res.Status;
|
||||
beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
|
||||
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
|
||||
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
|
||||
};
|
||||
|
||||
req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); };
|
||||
req.Failure += fail;
|
||||
|
||||
try
|
||||
{
|
||||
// intentionally blocking to limit web request concurrency
|
||||
req.Perform(api);
|
||||
|
||||
var res = req.Result;
|
||||
|
||||
beatmap.Status = res.Status;
|
||||
beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
|
||||
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
|
||||
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
|
||||
|
||||
LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
fail(e);
|
||||
}
|
||||
|
||||
void fail(Exception e)
|
||||
{
|
||||
beatmap.OnlineBeatmapID = null;
|
||||
LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})");
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
get => scope;
|
||||
set
|
||||
{
|
||||
if (value.Equals(scope))
|
||||
if (EqualityComparer<TScope>.Default.Equals(value, scope))
|
||||
return;
|
||||
|
||||
scope = value;
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Overlays
|
||||
get => beatmapSets;
|
||||
set
|
||||
{
|
||||
if (beatmapSets?.Equals(value) ?? false) return;
|
||||
if (ReferenceEquals(beatmapSets, value)) return;
|
||||
|
||||
beatmapSets = value?.ToList();
|
||||
|
||||
|
@ -12,6 +12,7 @@ namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
Margin = new MarginPadding { Top = 5 },
|
||||
RelativeSizeAxes = Axes.X,
|
||||
CommitOnFocusLost = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Overlays
|
||||
get => users;
|
||||
set
|
||||
{
|
||||
if (users?.Equals(value) ?? false)
|
||||
if (ReferenceEquals(users, value))
|
||||
return;
|
||||
|
||||
users = value?.ToList();
|
||||
|
@ -102,7 +102,7 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private void flash(Drawable d, double beatLength, bool kiai, TrackAmplitudes amplitudes)
|
||||
{
|
||||
d.FadeTo(Math.Max(0, ((d.Equals(leftBox) ? amplitudes.LeftChannel : amplitudes.RightChannel) - amplitude_dead_zone) / (kiai ? kiai_multiplier : alpha_multiplier)), box_fade_in_time)
|
||||
d.FadeTo(Math.Max(0, ((ReferenceEquals(d, leftBox) ? amplitudes.LeftChannel : amplitudes.RightChannel) - amplitude_dead_zone) / (kiai ? kiai_multiplier : alpha_multiplier)), box_fade_in_time)
|
||||
.Then()
|
||||
.FadeOut(beatLength, Easing.In);
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
// 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 System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class KeyCounterAction<T> : KeyCounter
|
||||
@ -16,7 +18,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public bool OnPressed(T action, bool forwards)
|
||||
{
|
||||
if (!action.Equals(Action))
|
||||
if (!EqualityComparer<T>.Default.Equals(action, Action))
|
||||
return false;
|
||||
|
||||
IsLit = true;
|
||||
@ -27,7 +29,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public bool OnReleased(T action, bool forwards)
|
||||
{
|
||||
if (!action.Equals(Action))
|
||||
if (!EqualityComparer<T>.Default.Equals(action, Action))
|
||||
return false;
|
||||
|
||||
IsLit = false;
|
||||
|
@ -65,7 +65,7 @@ namespace osu.Game.Screens.Select
|
||||
/// </summary>
|
||||
protected readonly Container FooterPanels;
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap();
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
|
||||
|
||||
protected readonly BeatmapCarousel Carousel;
|
||||
private readonly BeatmapInfoWedge beatmapInfoWedge;
|
||||
@ -393,7 +393,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
// We may be arriving here due to another component changing the bindable Beatmap.
|
||||
// In these cases, the other component has already loaded the beatmap, so we don't need to do so again.
|
||||
if (!Equals(beatmap, Beatmap.Value.BeatmapInfo))
|
||||
if (!EqualityComparer<BeatmapInfo>.Default.Equals(beatmap, Beatmap.Value.BeatmapInfo))
|
||||
{
|
||||
Logger.Log($"beatmap changed from \"{Beatmap.Value.BeatmapInfo}\" to \"{beatmap}\"");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user