1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-13 20:07:25 +08:00

Merge branch 'master' into single-bind-reset-button

This commit is contained in:
Swords 2021-05-15 20:29:56 +10:00 committed by GitHub
commit b5e1f78c06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 470 additions and 151 deletions

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Edit.Checks;
@ -224,12 +225,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
private void assertOk(IBeatmap beatmap)
{
Assert.That(check.Run(beatmap, new TestWorkingBeatmap(beatmap)), Is.Empty);
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
Assert.That(check.Run(context), Is.Empty);
}
private void assertOffscreenCircle(IBeatmap beatmap)
{
var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList();
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle);
@ -237,7 +240,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
private void assertOffscreenSlider(IBeatmap beatmap)
{
var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList();
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider);

View File

@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks.Components;
using osu.Game.Rulesets.Osu.Objects;
using osuTK;
@ -31,9 +31,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks
new IssueTemplateOffscreenSlider(this)
};
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap)
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
{
foreach (var hitobject in playableBeatmap.HitObjects)
foreach (var hitobject in context.Beatmap.HitObjects)
{
switch (hitobject)
{

View File

@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks.Components;
using osu.Game.Rulesets.Osu.Edit.Checks;
@ -17,9 +16,9 @@ namespace osu.Game.Rulesets.Osu.Edit
new CheckOffscreenObjects()
};
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, WorkingBeatmap workingBeatmap)
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
{
return checks.SelectMany(check => check.Run(playableBeatmap, workingBeatmap));
return checks.SelectMany(check => check.Run(context));
}
}
}

View File

@ -6,6 +6,7 @@ using Moq;
using NUnit.Framework;
using osu.Framework.Audio.Track;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects;
@ -40,23 +41,23 @@ namespace osu.Game.Tests.Editing.Checks
mock.SetupGet(w => w.Beatmap).Returns(beatmap);
mock.SetupGet(w => w.Track).Returns((Track)null);
Assert.That(check.Run(beatmap, mock.Object), Is.Empty);
Assert.That(check.Run(new BeatmapVerifierContext(beatmap, mock.Object)), Is.Empty);
}
[Test]
public void TestAcceptable()
{
var mock = getMockWorkingBeatmap(192);
var context = getContext(192);
Assert.That(check.Run(beatmap, mock.Object), Is.Empty);
Assert.That(check.Run(context), Is.Empty);
}
[Test]
public void TestNullBitrate()
{
var mock = getMockWorkingBeatmap(null);
var context = getContext(null);
var issues = check.Run(beatmap, mock.Object).ToList();
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateNoBitrate);
@ -65,9 +66,9 @@ namespace osu.Game.Tests.Editing.Checks
[Test]
public void TestZeroBitrate()
{
var mock = getMockWorkingBeatmap(0);
var context = getContext(0);
var issues = check.Run(beatmap, mock.Object).ToList();
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateNoBitrate);
@ -76,9 +77,9 @@ namespace osu.Game.Tests.Editing.Checks
[Test]
public void TestTooHighBitrate()
{
var mock = getMockWorkingBeatmap(320);
var context = getContext(320);
var issues = check.Run(beatmap, mock.Object).ToList();
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateTooHighBitrate);
@ -87,14 +88,19 @@ namespace osu.Game.Tests.Editing.Checks
[Test]
public void TestTooLowBitrate()
{
var mock = getMockWorkingBeatmap(64);
var context = getContext(64);
var issues = check.Run(beatmap, mock.Object).ToList();
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateTooLowBitrate);
}
private BeatmapVerifierContext getContext(int? audioBitrate)
{
return new BeatmapVerifierContext(beatmap, getMockWorkingBeatmap(audioBitrate).Object);
}
/// <summary>
/// Returns the mock of the working beatmap with the given audio properties.
/// </summary>

View File

@ -9,6 +9,7 @@ using Moq;
using NUnit.Framework;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects;
using FileInfo = osu.Game.IO.FileInfo;
@ -53,25 +54,25 @@ namespace osu.Game.Tests.Editing.Checks
{
// While this is a problem, it is out of scope for this check and is caught by a different one.
beatmap.Metadata.BackgroundFile = null;
var mock = getMockWorkingBeatmap(null, System.Array.Empty<byte>());
var context = getContext(null, System.Array.Empty<byte>());
Assert.That(check.Run(beatmap, mock.Object), Is.Empty);
Assert.That(check.Run(context), Is.Empty);
}
[Test]
public void TestAcceptable()
{
var mock = getMockWorkingBeatmap(new Texture(1920, 1080));
var context = getContext(new Texture(1920, 1080));
Assert.That(check.Run(beatmap, mock.Object), Is.Empty);
Assert.That(check.Run(context), Is.Empty);
}
[Test]
public void TestTooHighResolution()
{
var mock = getMockWorkingBeatmap(new Texture(3840, 2160));
var context = getContext(new Texture(3840, 2160));
var issues = check.Run(beatmap, mock.Object).ToList();
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooHighResolution);
@ -80,9 +81,9 @@ namespace osu.Game.Tests.Editing.Checks
[Test]
public void TestLowResolution()
{
var mock = getMockWorkingBeatmap(new Texture(640, 480));
var context = getContext(new Texture(640, 480));
var issues = check.Run(beatmap, mock.Object).ToList();
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateLowResolution);
@ -91,9 +92,9 @@ namespace osu.Game.Tests.Editing.Checks
[Test]
public void TestTooLowResolution()
{
var mock = getMockWorkingBeatmap(new Texture(100, 100));
var context = getContext(new Texture(100, 100));
var issues = check.Run(beatmap, mock.Object).ToList();
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooLowResolution);
@ -102,14 +103,19 @@ namespace osu.Game.Tests.Editing.Checks
[Test]
public void TestTooUncompressed()
{
var mock = getMockWorkingBeatmap(new Texture(1920, 1080), new byte[1024 * 1024 * 3]);
var context = getContext(new Texture(1920, 1080), new byte[1024 * 1024 * 3]);
var issues = check.Run(beatmap, mock.Object).ToList();
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooUncompressed);
}
private BeatmapVerifierContext getContext(Texture background, [CanBeNull] byte[] fileBytes = null)
{
return new BeatmapVerifierContext(beatmap, getMockWorkingBeatmap(background, fileBytes).Object);
}
/// <summary>
/// Returns the mock of the working beatmap with the given background and filesize.
/// </summary>

View File

@ -6,11 +6,13 @@ using System.Linq;
using Moq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Editing.Checks
{
@ -105,7 +107,7 @@ namespace osu.Game.Tests.Editing.Checks
new HitCircle { StartTime = 300 }
};
var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList();
var issues = check.Run(getContext(hitobjects)).ToList();
Assert.That(issues, Has.Count.EqualTo(3));
Assert.That(issues.Where(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentDifferent).ToList(), Has.Count.EqualTo(2));
@ -164,12 +166,12 @@ namespace osu.Game.Tests.Editing.Checks
private void assertOk(List<HitObject> hitobjects)
{
Assert.That(check.Run(getPlayableBeatmap(hitobjects), null), Is.Empty);
Assert.That(check.Run(getContext(hitobjects)), Is.Empty);
}
private void assertConcurrentSame(List<HitObject> hitobjects, int count = 1)
{
var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList();
var issues = check.Run(getContext(hitobjects)).ToList();
Assert.That(issues, Has.Count.EqualTo(count));
Assert.That(issues.All(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentSame));
@ -177,18 +179,16 @@ namespace osu.Game.Tests.Editing.Checks
private void assertConcurrentDifferent(List<HitObject> hitobjects, int count = 1)
{
var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList();
var issues = check.Run(getContext(hitobjects)).ToList();
Assert.That(issues, Has.Count.EqualTo(count));
Assert.That(issues.All(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentDifferent));
}
private IBeatmap getPlayableBeatmap(List<HitObject> hitobjects)
private BeatmapVerifierContext getContext(List<HitObject> hitobjects)
{
return new Beatmap<HitObject>
{
HitObjects = hitobjects
};
var beatmap = new Beatmap<HitObject> { HitObjects = hitobjects };
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
}
}
}

View File

@ -6,6 +6,7 @@ using System.Linq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.IO;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects;
using osu.Game.Tests.Beatmaps;
@ -45,7 +46,8 @@ namespace osu.Game.Tests.Editing.Checks
[Test]
public void TestBackgroundSetAndInFiles()
{
Assert.That(check.Run(beatmap, new TestWorkingBeatmap(beatmap)), Is.Empty);
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
Assert.That(check.Run(context), Is.Empty);
}
[Test]
@ -53,7 +55,8 @@ namespace osu.Game.Tests.Editing.Checks
{
beatmap.BeatmapInfo.BeatmapSet.Files.Clear();
var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList();
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateDoesNotExist);
@ -64,7 +67,8 @@ namespace osu.Game.Tests.Editing.Checks
{
beatmap.Metadata.BackgroundFile = null;
var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList();
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
var issues = check.Run(context).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateNoneSet);

View File

@ -7,10 +7,12 @@ using Moq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Editing.Checks
{
@ -100,12 +102,12 @@ namespace osu.Game.Tests.Editing.Checks
}, count: 2);
// Start and end are 2 ms and 1.25 ms off respectively, hence two different issues in one object.
var hitobjects = new List<HitObject>
var hitObjects = new List<HitObject>
{
getSliderMock(startTime: 98, endTime: 398.75d).Object
};
var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList();
var issues = check.Run(getContext(hitObjects)).ToList();
Assert.That(issues, Has.Count.EqualTo(2));
Assert.That(issues.Any(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateSmallUnsnap));
@ -122,34 +124,36 @@ namespace osu.Game.Tests.Editing.Checks
return mockSlider;
}
private void assertOk(List<HitObject> hitobjects)
private void assertOk(List<HitObject> hitObjects)
{
Assert.That(check.Run(getPlayableBeatmap(hitobjects), null), Is.Empty);
Assert.That(check.Run(getContext(hitObjects)), Is.Empty);
}
private void assert1Ms(List<HitObject> hitobjects, int count = 1)
private void assert1Ms(List<HitObject> hitObjects, int count = 1)
{
var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList();
var issues = check.Run(getContext(hitObjects)).ToList();
Assert.That(issues, Has.Count.EqualTo(count));
Assert.That(issues.All(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateSmallUnsnap));
}
private void assert2Ms(List<HitObject> hitobjects, int count = 1)
private void assert2Ms(List<HitObject> hitObjects, int count = 1)
{
var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList();
var issues = check.Run(getContext(hitObjects)).ToList();
Assert.That(issues, Has.Count.EqualTo(count));
Assert.That(issues.All(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateLargeUnsnap));
}
private IBeatmap getPlayableBeatmap(List<HitObject> hitobjects)
private BeatmapVerifierContext getContext(List<HitObject> hitObjects)
{
return new Beatmap<HitObject>
var beatmap = new Beatmap<HitObject>
{
ControlPointInfo = cpi,
HitObjects = hitobjects
HitObjects = hitObjects
};
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
}
}
}

View File

@ -1,32 +1,65 @@
// 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.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Screens.Ranking.Expanded;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneStarRatingDisplay : OsuTestScene
{
public TestSceneStarRatingDisplay()
[Test]
public void TestDisplay()
{
Child = new FillFlowContainer
AddStep("load displays", () => Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
ChildrenEnumerable = new[]
{
new StarRatingDisplay(new StarDifficulty(1.23, 0)),
new StarRatingDisplay(new StarDifficulty(2.34, 0)),
new StarRatingDisplay(new StarDifficulty(3.45, 0)),
new StarRatingDisplay(new StarDifficulty(4.56, 0)),
new StarRatingDisplay(new StarDifficulty(5.67, 0)),
new StarRatingDisplay(new StarDifficulty(6.78, 0)),
new StarRatingDisplay(new StarDifficulty(10.11, 0)),
1.23,
2.34,
3.45,
4.56,
5.67,
6.78,
10.11,
}.Select(starRating => new StarRatingDisplay(new StarDifficulty(starRating, 0))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
})
});
}
};
[Test]
public void TestChangingStarRatingDisplay()
{
StarRatingDisplay starRating = null;
AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(3f),
});
AddRepeatStep("set random value", () =>
{
starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1);
}, 10);
AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d =>
{
if (starRating != null)
starRating.Current.Value = new StarDifficulty(d, 1);
});
}
}
}

View File

@ -0,0 +1,151 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osuTK;
namespace osu.Game.Tests.Visual.SongSelect
{
public class TestSceneBeatmapMetadataDisplay : OsuTestScene
{
private BeatmapMetadataDisplay display;
[Resolved]
private BeatmapManager manager { get; set; }
[Cached(typeof(BeatmapDifficultyCache))]
private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache();
[Test]
public void TestLocal([Values("Beatmap", "Some long title and stuff")]
string title,
[Values("Trial", "Some1's very hardest difficulty")]
string version)
{
showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap
{
BeatmapInfo =
{
Metadata = new BeatmapMetadata
{
Title = title,
},
Version = version,
StarDifficulty = RNG.NextDouble(0, 10),
}
}));
}
[Test]
public void TestDelayedStarRating()
{
AddStep("block calculation", () => testDifficultyCache.BlockCalculation = true);
showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap
{
BeatmapInfo =
{
Metadata = new BeatmapMetadata
{
Title = "Heavy beatmap",
},
Version = "10k objects",
StarDifficulty = 99.99f,
}
}));
AddStep("allow calculation", () => testDifficultyCache.BlockCalculation = false);
}
[Test]
public void TestRandomFromDatabase()
{
showMetadataForBeatmap(() =>
{
var allBeatmapSets = manager.GetAllUsableBeatmapSets(IncludedDetails.Minimal);
if (allBeatmapSets.Count == 0)
return manager.DefaultBeatmap;
var randomBeatmapSet = allBeatmapSets[RNG.Next(0, allBeatmapSets.Count - 1)];
var randomBeatmap = randomBeatmapSet.Beatmaps[RNG.Next(0, randomBeatmapSet.Beatmaps.Count - 1)];
return manager.GetWorkingBeatmap(randomBeatmap);
});
}
private void showMetadataForBeatmap(Func<WorkingBeatmap> getBeatmap)
{
AddStep("setup display", () =>
{
var randomMods = Ruleset.Value.CreateInstance().GetAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList();
OsuLogo logo = new OsuLogo { Scale = new Vector2(0.15f) };
Remove(testDifficultyCache);
Children = new Drawable[]
{
testDifficultyCache,
display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable<IReadOnlyList<Mod>>(randomMods), logo)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Alpha = 0f,
}
};
display.FadeIn(400, Easing.OutQuint);
});
AddWaitStep("wait a bit", 5);
AddStep("finish loading", () => display.Loading = false);
}
private class TestBeatmapDifficultyCache : BeatmapDifficultyCache
{
private TaskCompletionSource<bool> calculationBlocker;
private bool blockCalculation;
public bool BlockCalculation
{
get => blockCalculation;
set
{
if (value == blockCalculation)
return;
blockCalculation = value;
if (value)
calculationBlocker = new TaskCompletionSource<bool>();
else
calculationBlocker?.SetResult(false);
}
}
public override async Task<StarDifficulty> GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable<Mod> mods = null, CancellationToken cancellationToken = default)
{
if (blockCalculation)
await calculationBlocker.Task;
return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken);
}
}
}
}

View File

@ -103,8 +103,8 @@ namespace osu.Game.Beatmaps
/// <param name="mods">The <see cref="Mod"/>s to get the difficulty with.</param>
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops computing the star difficulty.</param>
/// <returns>The <see cref="StarDifficulty"/>.</returns>
public Task<StarDifficulty> GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, [CanBeNull] IEnumerable<Mod> mods = null,
CancellationToken cancellationToken = default)
public virtual Task<StarDifficulty> GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null,
[CanBeNull] IEnumerable<Mod> mods = null, CancellationToken cancellationToken = default)
{
// In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset.
rulesetInfo ??= beatmapInfo.Ruleset;

View File

@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Edit.Checks.Components;
@ -29,9 +28,9 @@ namespace osu.Game.Rulesets.Edit
new CheckConcurrentObjects()
};
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, WorkingBeatmap workingBeatmap)
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
{
return checks.SelectMany(check => check.Run(playableBeatmap, workingBeatmap));
return checks.SelectMany(check => check.Run(context));
}
}
}

View File

@ -0,0 +1,38 @@
// 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 osu.Game.Beatmaps;
#nullable enable
namespace osu.Game.Rulesets.Edit
{
/// <summary>
/// Represents the context provided by the beatmap verifier to the checks it runs.
/// Contains information about what is being checked and how it should be checked.
/// </summary>
public class BeatmapVerifierContext
{
/// <summary>
/// The playable beatmap instance of the current beatmap.
/// </summary>
public readonly IBeatmap Beatmap;
/// <summary>
/// The working beatmap instance of the current beatmap.
/// </summary>
public readonly IWorkingBeatmap WorkingBeatmap;
/// <summary>
/// The difficulty level which the current beatmap is considered to be.
/// </summary>
public DifficultyRating InterpretedDifficulty;
public BeatmapVerifierContext(IBeatmap beatmap, IWorkingBeatmap workingBeatmap, DifficultyRating difficultyRating = DifficultyRating.ExpertPlus)
{
Beatmap = beatmap;
WorkingBeatmap = workingBeatmap;
InterpretedDifficulty = difficultyRating;
}
}
}

View File

@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Edit.Checks
{
protected override CheckCategory Category => CheckCategory.Audio;
protected override string TypeOfFile => "audio";
protected override string GetFilename(IBeatmap playableBeatmap) => playableBeatmap.Metadata?.AudioFile;
protected override string GetFilename(IBeatmap beatmap) => beatmap.Metadata?.AudioFile;
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks.Components;
namespace osu.Game.Rulesets.Edit.Checks
@ -26,13 +25,13 @@ namespace osu.Game.Rulesets.Edit.Checks
new IssueTemplateNoBitrate(this)
};
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap)
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
{
var audioFile = playableBeatmap.Metadata?.AudioFile;
var audioFile = context.Beatmap.Metadata?.AudioFile;
if (audioFile == null)
yield break;
var track = workingBeatmap.Track;
var track = context.WorkingBeatmap.Track;
if (track?.Bitrate == null || track.Bitrate.Value == 0)
yield return new IssueTemplateNoBitrate(this).Create();

View File

@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Edit.Checks
{
protected override CheckCategory Category => CheckCategory.Resources;
protected override string TypeOfFile => "background";
protected override string GetFilename(IBeatmap playableBeatmap) => playableBeatmap.Metadata?.BackgroundFile;
protected override string GetFilename(IBeatmap beatmap) => beatmap.Metadata?.BackgroundFile;
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks.Components;
namespace osu.Game.Rulesets.Edit.Checks
@ -30,13 +29,13 @@ namespace osu.Game.Rulesets.Edit.Checks
new IssueTemplateTooUncompressed(this)
};
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap)
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
{
var backgroundFile = playableBeatmap.Metadata?.BackgroundFile;
var backgroundFile = context.Beatmap.Metadata?.BackgroundFile;
if (backgroundFile == null)
yield break;
var texture = workingBeatmap.Background;
var texture = context.WorkingBeatmap.Background;
if (texture == null)
yield break;
@ -48,8 +47,8 @@ namespace osu.Game.Rulesets.Edit.Checks
else if (texture.Width < low_width || texture.Height < low_height)
yield return new IssueTemplateLowResolution(this).Create(texture.Width, texture.Height);
string storagePath = playableBeatmap.BeatmapInfo.BeatmapSet.GetPathForFile(backgroundFile);
double filesizeMb = workingBeatmap.GetStream(storagePath).Length / (1024d * 1024d);
string storagePath = context.Beatmap.BeatmapInfo.BeatmapSet.GetPathForFile(backgroundFile);
double filesizeMb = context.WorkingBeatmap.GetStream(storagePath).Length / (1024d * 1024d);
if (filesizeMb > max_filesize_mb)
yield return new IssueTemplateTooUncompressed(this).Create(filesizeMb);

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks.Components;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@ -22,15 +21,17 @@ namespace osu.Game.Rulesets.Edit.Checks
new IssueTemplateConcurrentDifferent(this)
};
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap)
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
{
for (int i = 0; i < playableBeatmap.HitObjects.Count - 1; ++i)
{
var hitobject = playableBeatmap.HitObjects[i];
var hitObjects = context.Beatmap.HitObjects;
for (int j = i + 1; j < playableBeatmap.HitObjects.Count; ++j)
for (int i = 0; i < hitObjects.Count - 1; ++i)
{
var nextHitobject = playableBeatmap.HitObjects[j];
var hitobject = hitObjects[i];
for (int j = i + 1; j < hitObjects.Count; ++j)
{
var nextHitobject = hitObjects[j];
// Accounts for rulesets with hitobjects separated by columns, such as Mania.
// In these cases we only care about concurrent objects within the same column.

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Edit.Checks
{
protected abstract CheckCategory Category { get; }
protected abstract string TypeOfFile { get; }
protected abstract string GetFilename(IBeatmap playableBeatmap);
protected abstract string GetFilename(IBeatmap beatmap);
public CheckMetadata Metadata => new CheckMetadata(Category, $"Missing {TypeOfFile}");
@ -21,9 +21,9 @@ namespace osu.Game.Rulesets.Edit.Checks
new IssueTemplateDoesNotExist(this)
};
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap)
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
{
var filename = GetFilename(playableBeatmap);
var filename = GetFilename(context.Beatmap);
if (filename == null)
{
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Edit.Checks
}
// If the file is set, also make sure it still exists.
var storagePath = playableBeatmap.BeatmapInfo.BeatmapSet.GetPathForFile(filename);
var storagePath = context.Beatmap.BeatmapInfo.BeatmapSet.GetPathForFile(filename);
if (storagePath != null)
yield break;

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks.Components;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@ -22,11 +21,11 @@ namespace osu.Game.Rulesets.Edit.Checks
new IssueTemplateSmallUnsnap(this)
};
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap)
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
{
var controlPointInfo = playableBeatmap.ControlPointInfo;
var controlPointInfo = context.Beatmap.ControlPointInfo;
foreach (var hitobject in playableBeatmap.HitObjects)
foreach (var hitobject in context.Beatmap.HitObjects)
{
double startUnsnap = hitobject.StartTime - controlPointInfo.GetClosestSnappedTime(hitobject.StartTime);
string startPostfix = hitobject is IHasDuration ? "start" : "";

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Beatmaps;
namespace osu.Game.Rulesets.Edit.Checks.Components
{
@ -24,8 +23,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components
/// <summary>
/// Runs this check and returns any issues detected for the provided beatmap.
/// </summary>
/// <param name="playableBeatmap">The playable beatmap of the beatmap to run the check on.</param>
/// <param name="workingBeatmap">The working beatmap of the beatmap to run the check on.</param>
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap);
/// <param name="context">The beatmap verifier context associated with the beatmap.</param>
public IEnumerable<Issue> Run(BeatmapVerifierContext context);
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks.Components;
namespace osu.Game.Rulesets.Edit
@ -12,6 +11,6 @@ namespace osu.Game.Rulesets.Edit
/// </summary>
public interface IBeatmapVerifier
{
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, WorkingBeatmap workingBeatmap);
public IEnumerable<Issue> Run(BeatmapVerifierContext context);
}
}

View File

@ -37,6 +37,7 @@ namespace osu.Game.Screens.Edit.Verify
private IBeatmapVerifier rulesetVerifier;
private BeatmapVerifier generalVerifier;
private BeatmapVerifierContext context;
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colours)
@ -44,6 +45,9 @@ namespace osu.Game.Screens.Edit.Verify
generalVerifier = new BeatmapVerifier();
rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier();
context = new BeatmapVerifierContext(beatmap, workingBeatmap.Value, verify.InterpretedDifficulty.Value);
verify.InterpretedDifficulty.BindValueChanged(difficulty => context.InterpretedDifficulty = difficulty.NewValue);
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
@ -91,10 +95,10 @@ namespace osu.Game.Screens.Edit.Verify
private void refresh()
{
var issues = generalVerifier.Run(beatmap, workingBeatmap.Value);
var issues = generalVerifier.Run(context);
if (rulesetVerifier != null)
issues = issues.Concat(rulesetVerifier.Run(beatmap, workingBeatmap.Value));
issues = issues.Concat(rulesetVerifier.Run(context));
issues = filter(issues);

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -14,6 +15,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Ranking.Expanded;
using osuTK;
namespace osu.Game.Screens.Play
@ -25,7 +27,7 @@ namespace osu.Game.Screens.Play
{
private readonly WorkingBeatmap beatmap;
private readonly Bindable<IReadOnlyList<Mod>> mods;
private readonly Drawable facade;
private readonly Drawable logoFacade;
private LoadingSpinner loading;
public IBindable<IReadOnlyList<Mod>> Mods => mods;
@ -41,19 +43,24 @@ namespace osu.Game.Screens.Play
}
}
public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable<IReadOnlyList<Mod>> mods, Drawable facade)
public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable<IReadOnlyList<Mod>> mods, Drawable logoFacade)
{
this.beatmap = beatmap;
this.facade = facade;
this.logoFacade = logoFacade;
this.mods = new Bindable<IReadOnlyList<Mod>>();
this.mods.BindTo(mods);
}
private IBindable<StarDifficulty?> starDifficulty;
private FillFlowContainer versionFlow;
private StarRatingDisplay starRatingDisplay;
[BackgroundDependencyLoader]
private void load()
private void load(BeatmapDifficultyCache difficultyCache)
{
var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata();
var metadata = beatmap.BeatmapInfo.Metadata;
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
@ -66,7 +73,7 @@ namespace osu.Game.Screens.Play
Direction = FillDirection.Vertical,
Children = new[]
{
facade.With(d =>
logoFacade.With(d =>
{
d.Anchor = Anchor.TopCentre;
d.Origin = Anchor.TopCentre;
@ -107,16 +114,30 @@ namespace osu.Game.Screens.Play
loading = new LoadingLayer(true)
}
},
versionFlow = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Direction = FillDirection.Vertical,
Spacing = new Vector2(5f),
Margin = new MarginPadding { Bottom = 40 },
Children = new Drawable[]
{
new OsuSpriteText
{
Text = beatmap?.BeatmapInfo?.Version,
Font = OsuFont.GetFont(size: 26, italics: true),
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Margin = new MarginPadding
{
Bottom = 40
Origin = Anchor.TopCentre,
},
starRatingDisplay = new StarRatingDisplay(default)
{
Alpha = 0f,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
}
}
},
new GridContainer
{
@ -159,9 +180,38 @@ namespace osu.Game.Screens.Play
}
};
starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo);
Loading = true;
}
protected override void LoadComplete()
{
base.LoadComplete();
if (starDifficulty.Value != null)
{
starRatingDisplay.Current.Value = starDifficulty.Value.Value;
starRatingDisplay.Show();
}
else
{
starRatingDisplay.Hide();
starDifficulty.ValueChanged += d =>
{
Debug.Assert(d.NewValue != null);
starRatingDisplay.Current.Value = d.NewValue.Value;
versionFlow.AutoSizeDuration = 300;
versionFlow.AutoSizeEasing = Easing.OutQuint;
starRatingDisplay.FadeIn(300, Easing.InQuint);
};
}
}
private class MetadataLineLabel : OsuSpriteText
{
public MetadataLineLabel(string text)

View File

@ -3,12 +3,14 @@
using System.Globalization;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
@ -20,9 +22,21 @@ namespace osu.Game.Screens.Ranking.Expanded
/// <summary>
/// A pill that displays the star rating of a <see cref="BeatmapInfo"/>.
/// </summary>
public class StarRatingDisplay : CompositeDrawable
public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue<StarDifficulty>
{
private readonly StarDifficulty difficulty;
private Box background;
private OsuTextFlowContainer textFlow;
[Resolved]
private OsuColour colours { get; set; }
private readonly BindableWithCurrent<StarDifficulty> current = new BindableWithCurrent<StarDifficulty>();
public Bindable<StarDifficulty> Current
{
get => current.Current;
set => current.Current = value;
}
/// <summary>
/// Creates a new <see cref="StarRatingDisplay"/> using an already computed <see cref="StarDifficulty"/>.
@ -30,7 +44,7 @@ namespace osu.Game.Screens.Ranking.Expanded
/// <param name="starDifficulty">The already computed <see cref="StarDifficulty"/> to display the star difficulty of.</param>
public StarRatingDisplay(StarDifficulty starDifficulty)
{
difficulty = starDifficulty;
Current.Value = starDifficulty;
}
[BackgroundDependencyLoader]
@ -38,15 +52,6 @@ namespace osu.Game.Screens.Ranking.Expanded
{
AutoSizeAxes = Axes.Both;
var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.');
string wholePart = starRatingParts[0];
string fractionPart = starRatingParts[1];
string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
ColourInfo backgroundColour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus
? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959"))
: (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating);
InternalChildren = new Drawable[]
{
new CircularContainer
@ -55,10 +60,9 @@ namespace osu.Game.Screens.Ranking.Expanded
Masking = true,
Children = new Drawable[]
{
new Box
background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = backgroundColour
},
}
},
@ -78,32 +82,54 @@ namespace osu.Game.Screens.Ranking.Expanded
Icon = FontAwesome.Solid.Star,
Colour = Color4.Black
},
new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black))
textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black))
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
TextAnchor = Anchor.BottomLeft,
}.With(t =>
}
}
}
};
}
protected override void LoadComplete()
{
t.AddText($"{wholePart}", s =>
base.LoadComplete();
Current.BindValueChanged(_ => updateDisplay(), true);
}
private void updateDisplay()
{
var starRatingParts = Current.Value.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.');
string wholePart = starRatingParts[0];
string fractionPart = starRatingParts[1];
string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
var rating = Current.Value.DifficultyRating;
background.Colour = rating == DifficultyRating.ExpertPlus
? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959"))
: (ColourInfo)colours.ForDifficultyRating(rating);
textFlow.Clear();
textFlow.AddText($"{wholePart}", s =>
{
s.Colour = Color4.Black;
s.Font = s.Font.With(size: 14);
s.UseFullGlyphHeight = false;
});
t.AddText($"{separator}{fractionPart}", s =>
textFlow.AddText($"{separator}{fractionPart}", s =>
{
s.Colour = Color4.Black;
s.Font = s.Font.With(size: 7);
s.UseFullGlyphHeight = false;
});
})
}
}
};
}
}
}