1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 18:42:56 +08:00

Merge remote-tracking branch 'upstream/master' into fix-carousel-double-filter

This commit is contained in:
Dean Herbert 2019-11-15 18:20:36 +09:00
commit 296fff9243
20 changed files with 192 additions and 55 deletions

View 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.

View File

@ -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 -->

View File

@ -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.

View File

@ -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()
{

View File

@ -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);

View File

@ -236,6 +236,22 @@ namespace osu.Game.Tests.Visual.SongSelect
void onRulesetChange(ValueChangedEvent<RulesetInfo> e) => rulesetChangeIndex = actionIndex++;
}
[Test]
public void TestModsRetainedBetweenSongSelect()
{
AddAssert("empty mods", () => !Mods.Value.Any());
createSongSelect();
addRulesetImportStep(0);
changeMods(new OsuModHardRock());
createSongSelect();
AddAssert("mods retained", () => Mods.Value.Any());
}
[Test]
public void TestStartAfterUnMatchingFilterDoesNotStart()
{

View File

@ -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})");
}
}

View File

@ -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;

View File

@ -925,6 +925,8 @@ namespace osu.Game
{
OverlayActivationMode.Value = newOsuScreen.InitialOverlayActivationMode;
musicController.AllowRateAdjustments = newOsuScreen.AllowRateAdjustments;
if (newOsuScreen.HideOverlaysOnEnter)
CloseAllOverlays();
else

View File

@ -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();

View File

@ -233,6 +233,24 @@ namespace osu.Game.Overlays
queuedDirection = null;
}
private bool allowRateAdjustments;
/// <summary>
/// Whether mod rate adjustments are allowed to be applied.
/// </summary>
public bool AllowRateAdjustments
{
get => allowRateAdjustments;
set
{
if (allowRateAdjustments == value)
return;
allowRateAdjustments = value;
ResetTrackAdjustments();
}
}
public void ResetTrackAdjustments()
{
var track = current?.Track;
@ -241,8 +259,11 @@ namespace osu.Game.Overlays
track.ResetSpeedAdjustments();
foreach (var mod in mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(track);
if (allowRateAdjustments)
{
foreach (var mod in mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(track);
}
}
protected override void Dispose(bool isDisposing)

View File

@ -12,6 +12,7 @@ namespace osu.Game.Overlays.Settings
{
Margin = new MarginPadding { Top = 5 },
RelativeSizeAxes = Axes.X,
CommitOnFocusLost = true,
};
}
}

View File

@ -38,7 +38,7 @@ namespace osu.Game.Overlays
get => users;
set
{
if (users?.Equals(value) ?? false)
if (ReferenceEquals(users, value))
return;
users = value?.ToList();

View File

@ -51,5 +51,10 @@ namespace osu.Game.Screens
Bindable<WorkingBeatmap> Beatmap { get; }
Bindable<RulesetInfo> Ruleset { get; }
/// <summary>
/// Whether mod rate adjustments are allowed to be applied.
/// </summary>
bool AllowRateAdjustments { get; }
}
}

View File

@ -37,6 +37,8 @@ namespace osu.Game.Screens.Menu
public override bool AllowExternalScreenChange => true;
public override bool AllowRateAdjustments => false;
private Screen songSelect;
private MenuSideFlashes sideFlashes;

View File

@ -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);
}

View File

@ -91,6 +91,8 @@ namespace osu.Game.Screens
public Bindable<RulesetInfo> Ruleset { get; private set; }
public virtual bool AllowRateAdjustments => true;
public Bindable<IReadOnlyList<Mod>> Mods { get; private set; }
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)

View File

@ -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;

View File

@ -53,43 +53,41 @@ namespace osu.Game.Screens.Select
/// <summary>
/// Can be null if <see cref="ShowFooter"/> is false.
/// </summary>
protected BeatmapOptionsOverlay BeatmapOptions;
protected BeatmapOptionsOverlay BeatmapOptions { get; private set; }
/// <summary>
/// Can be null if <see cref="ShowFooter"/> is false.
/// </summary>
protected Footer Footer;
protected Footer Footer { get; private set; }
/// <summary>
/// Contains any panel which is triggered by a footer button.
/// Helps keep them located beneath the footer itself.
/// </summary>
protected Container FooterPanels;
protected Container FooterPanels { get; private set; }
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap();
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
protected BeatmapCarousel Carousel { get; private set; }
protected BeatmapCarousel Carousel;
private BeatmapInfoWedge beatmapInfoWedge;
private DialogOverlay dialogOverlay;
private BeatmapManager beatmaps;
protected ModSelectOverlay ModSelect;
protected ModSelectOverlay ModSelect { get; private set; }
protected SampleChannel SampleConfirm { get; private set; }
protected SampleChannel SampleConfirm;
private SampleChannel sampleChangeDifficulty;
private SampleChannel sampleChangeBeatmap;
protected BeatmapDetailArea BeatmapDetails;
protected BeatmapDetailArea BeatmapDetails { get; private set; }
private readonly Bindable<RulesetInfo> decoupledRuleset = new Bindable<RulesetInfo>();
[Resolved(canBeNull: true)]
private MusicController music { get; set; }
[Cached]
[Cached(Type = typeof(IBindable<IReadOnlyList<Mod>>))]
private readonly Bindable<IReadOnlyList<Mod>> mods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>()); // Bound to the game's mods, but is not reset on exiting
protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) => Carousel.Filter(criteria);
[BackgroundDependencyLoader(true)]
@ -224,7 +222,7 @@ namespace osu.Game.Screens.Select
if (Footer != null)
{
Footer.AddButton(new FooterButtonMods { Current = mods }, ModSelect);
Footer.AddButton(new FooterButtonMods { Current = Mods }, ModSelect);
Footer.AddButton(new FooterButtonRandom { Action = triggerRandom });
Footer.AddButton(new FooterButtonOptions(), BeatmapOptions);
@ -264,13 +262,6 @@ namespace osu.Game.Screens.Select
}
}
protected override void LoadComplete()
{
base.LoadComplete();
mods.BindTo(Mods);
}
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
@ -392,7 +383,7 @@ namespace osu.Game.Screens.Select
{
Logger.Log($"ruleset changed from \"{decoupledRuleset.Value}\" to \"{ruleset}\"");
mods.Value = Array.Empty<Mod>();
Mods.Value = Array.Empty<Mod>();
decoupledRuleset.Value = ruleset;
// force a filter before attempting to change the beatmap.
@ -407,7 +398,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}\"");
@ -540,9 +531,6 @@ namespace osu.Game.Screens.Select
if (Beatmap.Value.Track != null)
Beatmap.Value.Track.Looping = false;
mods.UnbindAll();
Mods.Value = Array.Empty<Mod>();
return false;
}

View File

@ -16,6 +16,8 @@ namespace osu.Game.Screens
public override bool CursorVisible => false;
public override bool AllowRateAdjustments => false;
public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
}
}