1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-19 16:03:14 +08:00

Merge branch 'master' into directory-select-screen

This commit is contained in:
Dan Balasescu 2021-05-17 17:22:03 +09:00 committed by GitHub
commit d75ef160e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
147 changed files with 3316 additions and 847 deletions

View File

@ -52,6 +52,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.422.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2021.422.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.510.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2021.513.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -28,10 +28,12 @@ namespace osu.Game.Rulesets.Catch.Mods
catchPlayfield.CatcherArea.MovableCatcher.CatchFruitOnPlate = false; catchPlayfield.CatcherArea.MovableCatcher.CatchFruitOnPlate = false;
} }
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
}
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
{ {
base.ApplyNormalVisibilityState(hitObject, state);
if (!(hitObject is DrawableCatchHitObject catchDrawable)) if (!(hitObject is DrawableCatchHitObject catchDrawable))
return; return;
@ -54,7 +56,7 @@ namespace osu.Game.Rulesets.Catch.Mods
var offset = hitObject.TimePreempt * fade_out_offset_multiplier; var offset = hitObject.TimePreempt * fade_out_offset_multiplier;
var duration = offset - hitObject.TimePreempt * fade_out_duration_multiplier; var duration = offset - hitObject.TimePreempt * fade_out_duration_multiplier;
using (drawable.BeginAbsoluteSequence(hitObject.StartTime - offset, true)) using (drawable.BeginAbsoluteSequence(hitObject.StartTime - offset))
drawable.FadeOut(duration); drawable.FadeOut(duration);
} }
} }

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
explosion = new LegacyRollingCounter(skin, LegacyFont.Combo) explosion = new LegacyRollingCounter(LegacyFont.Combo)
{ {
Alpha = 0.65f, Alpha = 0.65f,
Blending = BlendingParameters.Additive, Blending = BlendingParameters.Additive,
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
Origin = Anchor.Centre, Origin = Anchor.Centre,
Scale = new Vector2(1.5f), Scale = new Vector2(1.5f),
}, },
counter = new LegacyRollingCounter(skin, LegacyFont.Combo) counter = new LegacyRollingCounter(LegacyFont.Combo)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods namespace osu.Game.Rulesets.Mania.Mods
@ -39,5 +40,13 @@ namespace osu.Game.Rulesets.Mania.Mods
})); }));
} }
} }
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
}
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
}
} }
} }

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Edit.Checks; using osu.Game.Rulesets.Osu.Edit.Checks;
@ -224,12 +225,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
private void assertOk(IBeatmap beatmap) 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) 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, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle); 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) 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, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider); Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider);

View File

@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests
hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
Child = new SkinProvidingContainer(new DefaultSkin()) Child = new SkinProvidingContainer(new DefaultSkin(null))
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = drawableHitCircle = new DrawableHitCircle(hitCircle) Child = drawableHitCircle = new DrawableHitCircle(hitCircle)

View File

@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Edit.Checks.Components;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osuTK; using osuTK;
@ -31,9 +31,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks
new IssueTemplateOffscreenSlider(this) 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) switch (hitobject)
{ {

View File

@ -3,7 +3,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Edit.Checks.Components;
using osu.Game.Rulesets.Osu.Edit.Checks; using osu.Game.Rulesets.Osu.Edit.Checks;
@ -17,9 +16,9 @@ namespace osu.Game.Rulesets.Osu.Edit
new CheckOffscreenObjects() 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

@ -43,13 +43,11 @@ namespace osu.Game.Rulesets.Osu.Mods
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
{ {
base.ApplyIncreasedVisibilityState(hitObject, state);
applyState(hitObject, true); applyState(hitObject, true);
} }
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
{ {
base.ApplyNormalVisibilityState(hitObject, state);
applyState(hitObject, false); applyState(hitObject, false);
} }
@ -60,20 +58,20 @@ namespace osu.Game.Rulesets.Osu.Mods
OsuHitObject hitObject = drawableOsuObject.HitObject; OsuHitObject hitObject = drawableOsuObject.HitObject;
(double startTime, double duration) fadeOut = getFadeOutParameters(drawableOsuObject); (double fadeStartTime, double fadeDuration) = getFadeOutParameters(drawableOsuObject);
switch (drawableObject) switch (drawableObject)
{ {
case DrawableSliderTail _: case DrawableSliderTail _:
using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true)) using (drawableObject.BeginAbsoluteSequence(fadeStartTime))
drawableObject.FadeOut(fadeOut.duration); drawableObject.FadeOut(fadeDuration);
break; break;
case DrawableSliderRepeat sliderRepeat: case DrawableSliderRepeat sliderRepeat:
using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true)) using (drawableObject.BeginAbsoluteSequence(fadeStartTime))
// only apply to circle piece reverse arrow is not affected by hidden. // only apply to circle piece reverse arrow is not affected by hidden.
sliderRepeat.CirclePiece.FadeOut(fadeOut.duration); sliderRepeat.CirclePiece.FadeOut(fadeDuration);
break; break;
@ -88,23 +86,23 @@ namespace osu.Game.Rulesets.Osu.Mods
else else
{ {
// we don't want to see the approach circle // we don't want to see the approach circle
using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt, true)) using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt))
circle.ApproachCircle.Hide(); circle.ApproachCircle.Hide();
} }
using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true)) using (drawableObject.BeginAbsoluteSequence(fadeStartTime))
fadeTarget.FadeOut(fadeOut.duration); fadeTarget.FadeOut(fadeDuration);
break; break;
case DrawableSlider slider: case DrawableSlider slider:
using (slider.BeginAbsoluteSequence(fadeOut.startTime, true)) using (slider.BeginAbsoluteSequence(fadeStartTime))
slider.Body.FadeOut(fadeOut.duration, Easing.Out); slider.Body.FadeOut(fadeDuration, Easing.Out);
break; break;
case DrawableSliderTick sliderTick: case DrawableSliderTick sliderTick:
using (sliderTick.BeginAbsoluteSequence(fadeOut.startTime, true)) using (sliderTick.BeginAbsoluteSequence(fadeStartTime))
sliderTick.FadeOut(fadeOut.duration); sliderTick.FadeOut(fadeDuration);
break; break;
@ -112,14 +110,14 @@ namespace osu.Game.Rulesets.Osu.Mods
// hide elements we don't care about. // hide elements we don't care about.
// todo: hide background // todo: hide background
using (spinner.BeginAbsoluteSequence(fadeOut.startTime, true)) using (spinner.BeginAbsoluteSequence(fadeStartTime))
spinner.FadeOut(fadeOut.duration); spinner.FadeOut(fadeDuration);
break; break;
} }
} }
private (double startTime, double duration) getFadeOutParameters(DrawableOsuHitObject drawableObject) private (double fadeStartTime, double fadeDuration) getFadeOutParameters(DrawableOsuHitObject drawableObject)
{ {
switch (drawableObject) switch (drawableObject)
{ {
@ -137,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Mods
return getParameters(drawableObject.HitObject); return getParameters(drawableObject.HitObject);
} }
static (double startTime, double duration) getParameters(OsuHitObject hitObject) static (double fadeStartTime, double fadeDuration) getParameters(OsuHitObject hitObject)
{ {
var fadeOutStartTime = hitObject.StartTime - hitObject.TimePreempt + hitObject.TimeFadeIn; var fadeOutStartTime = hitObject.StartTime - hitObject.TimePreempt + hitObject.TimeFadeIn;
var fadeOutDuration = hitObject.TimePreempt * fade_out_duration_multiplier; var fadeOutDuration = hitObject.TimePreempt * fade_out_duration_multiplier;

View File

@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
Scale = new Vector2(SPRITE_SCALE), Scale = new Vector2(SPRITE_SCALE),
Y = SPINNER_TOP_OFFSET + 115, Y = SPINNER_TOP_OFFSET + 115,
}, },
bonusCounter = new LegacySpriteText(source, LegacyFont.Score) bonusCounter = new LegacySpriteText(LegacyFont.Score)
{ {
Alpha = 0f, Alpha = 0f,
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
Scale = new Vector2(SPRITE_SCALE), Scale = new Vector2(SPRITE_SCALE),
Position = new Vector2(-87, 445 + spm_hide_offset), Position = new Vector2(-87, 445 + spm_hide_offset),
}, },
spmCounter = new LegacySpriteText(source, LegacyFont.Score) spmCounter = new LegacySpriteText(LegacyFont.Score)
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,

View File

@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
if (!this.HasFont(LegacyFont.HitCircle)) if (!this.HasFont(LegacyFont.HitCircle))
return null; return null;
return new LegacySpriteText(Source, LegacyFont.HitCircle) return new LegacySpriteText(LegacyFont.HitCircle)
{ {
// stable applies a blanket 0.8x scale to hitcircle fonts // stable applies a blanket 0.8x scale to hitcircle fonts
Scale = new Vector2(0.8f), Scale = new Vector2(0.8f),

View File

@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.UI
var hitWindows = new OsuHitWindows(); var hitWindows = new OsuHitWindows();
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r))) foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
poolDictionary.Add(result, new DrawableJudgementPool(result, onJudgmentLoaded)); poolDictionary.Add(result, new DrawableJudgementPool(result, onJudgementLoaded));
AddRangeInternal(poolDictionary.Values); AddRangeInternal(poolDictionary.Values);
@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.UI
} }
} }
private void onJudgmentLoaded(DrawableOsuJudgement judgement) private void onJudgementLoaded(DrawableOsuJudgement judgement)
{ {
judgementAboveHitObjectLayer.Add(judgement.GetProxyAboveHitObjectsContent()); judgementAboveHitObjectLayer.Add(judgement.GetProxyAboveHitObjectsContent());
} }

View File

@ -5,6 +5,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Skinning.Legacy; using osu.Game.Rulesets.Taiko.Skinning.Legacy;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -27,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
})); }));
AddToggleStep("Toggle passing", passing => this.ChildrenOfType<LegacyTaikoScroller>().ForEach(s => s.LastResult.Value = AddToggleStep("Toggle passing", passing => this.ChildrenOfType<LegacyTaikoScroller>().ForEach(s => s.LastResult.Value =
new JudgementResult(null, new Judgement()) { Type = passing ? HitResult.Great : HitResult.Miss })); new JudgementResult(new HitObject(), new Judgement()) { Type = passing ? HitResult.Great : HitResult.Miss }));
AddToggleStep("toggle playback direction", reversed => this.reversed = reversed); AddToggleStep("toggle playback direction", reversed => this.reversed = reversed);
} }

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Mods namespace osu.Game.Rulesets.Taiko.Mods
{ {
@ -10,5 +11,13 @@ namespace osu.Game.Rulesets.Taiko.Mods
public override string Description => @"Beats fade out before you hit them!"; public override string Description => @"Beats fade out before you hit them!";
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
public override bool HasImplementation => false; public override bool HasImplementation => false;
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
}
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
}
} }
} }

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
@ -12,6 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
public class TaikoModRandom : ModRandom, IApplicableToBeatmap public class TaikoModRandom : ModRandom, IApplicableToBeatmap
{ {
public override string Description => @"Shuffle around the colours!"; public override string Description => @"Shuffle around the colours!";
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(TaikoModSwap)).ToArray();
public void ApplyToBeatmap(IBeatmap beatmap) public void ApplyToBeatmap(IBeatmap beatmap)
{ {

View File

@ -0,0 +1,33 @@
// 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.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModSwap : Mod, IApplicableToBeatmap
{
public override string Name => "Swap";
public override string Acronym => "SW";
public override string Description => @"Dons become kats, kats become dons";
public override ModType Type => ModType.Conversion;
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModRandom)).ToArray();
public void ApplyToBeatmap(IBeatmap beatmap)
{
var taikoBeatmap = (TaikoBeatmap)beatmap;
foreach (var obj in taikoBeatmap.HitObjects)
{
if (obj is Hit hit)
hit.Type = hit.Type == HitType.Centre ? HitType.Rim : HitType.Centre;
}
}
}
}

View File

@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
base.Update(); base.Update();
// store X before checking wide enough so if we perform layout there is no positional discrepancy. // store X before checking wide enough so if we perform layout there is no positional discrepancy.
float currentX = (InternalChildren?.FirstOrDefault()?.X ?? 0) - (float)Clock.ElapsedFrameTime * 0.1f; float currentX = (InternalChildren.FirstOrDefault()?.X ?? 0) - (float)Clock.ElapsedFrameTime * 0.1f;
// ensure we have enough sprites // ensure we have enough sprites
if (!InternalChildren.Any() if (!InternalChildren.Any()

View File

@ -136,6 +136,7 @@ namespace osu.Game.Rulesets.Taiko
new TaikoModRandom(), new TaikoModRandom(),
new TaikoModDifficultyAdjust(), new TaikoModDifficultyAdjust(),
new TaikoModClassic(), new TaikoModClassic(),
new TaikoModSwap(),
}; };
case ModType.Automation: case ModType.Automation:

View File

@ -12,6 +12,7 @@ using osu.Framework.Platform;
using osu.Game.IPC; using osu.Game.IPC;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
@ -264,7 +265,7 @@ namespace osu.Game.Tests.Beatmaps.IO
// change filename // change filename
var firstFile = new FileInfo(Directory.GetFiles(extractedFolder).First()); var firstFile = new FileInfo(Directory.GetFiles(extractedFolder).First());
firstFile.MoveTo(Path.Combine(firstFile.DirectoryName, $"{firstFile.Name}-changed{firstFile.Extension}")); firstFile.MoveTo(Path.Combine(firstFile.DirectoryName.AsNonNull(), $"{firstFile.Name}-changed{firstFile.Extension}"));
using (var zip = ZipArchive.Create()) using (var zip = ZipArchive.Create())
{ {

View File

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

View File

@ -9,6 +9,7 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using FileInfo = osu.Game.IO.FileInfo; 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. // While this is a problem, it is out of scope for this check and is caught by a different one.
beatmap.Metadata.BackgroundFile = null; 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] [Test]
public void TestAcceptable() 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] [Test]
public void TestTooHighResolution() 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, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooHighResolution); Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooHighResolution);
@ -80,9 +81,9 @@ namespace osu.Game.Tests.Editing.Checks
[Test] [Test]
public void TestLowResolution() 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, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateLowResolution); Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateLowResolution);
@ -91,9 +92,9 @@ namespace osu.Game.Tests.Editing.Checks
[Test] [Test]
public void TestTooLowResolution() 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, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooLowResolution); Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooLowResolution);
@ -102,14 +103,19 @@ namespace osu.Game.Tests.Editing.Checks
[Test] [Test]
public void TestTooUncompressed() 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, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooUncompressed); 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> /// <summary>
/// Returns the mock of the working beatmap with the given background and filesize. /// Returns the mock of the working beatmap with the given background and filesize.
/// </summary> /// </summary>

View File

@ -6,11 +6,13 @@ using System.Linq;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Editing.Checks namespace osu.Game.Tests.Editing.Checks
{ {
@ -105,7 +107,7 @@ namespace osu.Game.Tests.Editing.Checks
new HitCircle { StartTime = 300 } 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, Has.Count.EqualTo(3));
Assert.That(issues.Where(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentDifferent).ToList(), Has.Count.EqualTo(2)); 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) 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) 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, Has.Count.EqualTo(count));
Assert.That(issues.All(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentSame)); 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) 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, Has.Count.EqualTo(count));
Assert.That(issues.All(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentDifferent)); 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> var beatmap = new Beatmap<HitObject> { HitObjects = hitobjects };
{ return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
HitObjects = hitobjects
};
} }
} }
} }

View File

@ -6,6 +6,7 @@ using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
@ -45,7 +46,8 @@ namespace osu.Game.Tests.Editing.Checks
[Test] [Test]
public void TestBackgroundSetAndInFiles() 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] [Test]
@ -53,7 +55,8 @@ namespace osu.Game.Tests.Editing.Checks
{ {
beatmap.BeatmapInfo.BeatmapSet.Files.Clear(); 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, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateDoesNotExist); Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateDoesNotExist);
@ -64,7 +67,8 @@ namespace osu.Game.Tests.Editing.Checks
{ {
beatmap.Metadata.BackgroundFile = null; 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, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateNoneSet); Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateNoneSet);

View File

@ -7,10 +7,12 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Editing.Checks namespace osu.Game.Tests.Editing.Checks
{ {
@ -100,12 +102,12 @@ namespace osu.Game.Tests.Editing.Checks
}, count: 2); }, count: 2);
// Start and end are 2 ms and 1.25 ms off respectively, hence two different issues in one object. // 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 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, Has.Count.EqualTo(2));
Assert.That(issues.Any(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateSmallUnsnap)); Assert.That(issues.Any(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateSmallUnsnap));
@ -122,34 +124,36 @@ namespace osu.Game.Tests.Editing.Checks
return mockSlider; 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, Has.Count.EqualTo(count));
Assert.That(issues.All(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateSmallUnsnap)); 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, Has.Count.EqualTo(count));
Assert.That(issues.All(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateLargeUnsnap)); 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, ControlPointInfo = cpi,
HitObjects = hitobjects HitObjects = hitObjects
}; };
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
} }
} }
} }

View File

@ -146,7 +146,7 @@ namespace osu.Game.Tests.Mods
if (isValid) if (isValid)
Assert.IsNull(invalid); Assert.IsNull(invalid);
else else
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid)); Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
} }
public abstract class CustomMod1 : Mod public abstract class CustomMod1 : Mod

View File

@ -4,6 +4,7 @@
using System.Linq; using System.Linq;
using Humanizer; using Humanizer;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Tests.Visual.Multiplayer; using osu.Game.Tests.Visual.Multiplayer;
@ -34,7 +35,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
changeState(6, MultiplayerUserState.WaitingForLoad); changeState(6, MultiplayerUserState.WaitingForLoad);
checkPlayingUserCount(6); checkPlayingUserCount(6);
AddStep("another user left", () => Client.RemoveUser(Client.Room?.Users.Last().User)); AddStep("another user left", () => Client.RemoveUser((Client.Room?.Users.Last().User).AsNonNull()));
checkPlayingUserCount(5); checkPlayingUserCount(5);
AddStep("leave room", () => Client.LeaveRoom()); AddStep("leave room", () => Client.LeaveRoom());

View File

@ -167,5 +167,21 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox, new Vector2(20))); AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox, new Vector2(20)));
AddUntilStep("rotation handle hidden", () => rotationHandle.Alpha == 0); AddUntilStep("rotation handle hidden", () => rotationHandle.Alpha == 0);
} }
/// <summary>
/// Tests that hovering over two handles instantaneously from one to another does not crash or cause issues to the visibility state.
/// </summary>
[Test]
public void TestHoverOverTwoHandlesInstantaneously()
{
AddStep("hover over top-left scale handle", () =>
InputManager.MoveMouseTo(this.ChildrenOfType<SelectionBoxScaleHandle>().Single(s => s.Anchor == Anchor.TopLeft)));
AddStep("hover over top-right scale handle", () =>
InputManager.MoveMouseTo(this.ChildrenOfType<SelectionBoxScaleHandle>().Single(s => s.Anchor == Anchor.TopRight)));
AddUntilStep("top-left rotation handle hidden", () =>
this.ChildrenOfType<SelectionBoxRotationHandle>().Single(r => r.Anchor == Anchor.TopLeft).Alpha == 0);
AddUntilStep("top-right rotation handle shown", () =>
this.ChildrenOfType<SelectionBoxRotationHandle>().Single(r => r.Anchor == Anchor.TopRight).Alpha == 1);
}
} }
} }

View File

@ -1,22 +1,18 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD; using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestSceneComboCounter : SkinnableTestScene public class TestSceneComboCounter : SkinnableTestScene
{ {
private IEnumerable<SkinnableComboCounter> comboCounters => CreatedDrawables.OfType<SkinnableComboCounter>();
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
[Cached] [Cached]
@ -25,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[SetUpSteps] [SetUpSteps]
public void SetUpSteps() public void SetUpSteps()
{ {
AddStep("Create combo counters", () => SetContents(() => new SkinnableComboCounter())); AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter))));
} }
[Test] [Test]

View File

@ -2,10 +2,12 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Skinning;
using osu.Game.Skinning.Editor; using osu.Game.Skinning.Editor;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
@ -14,12 +16,17 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
private SkinEditor skinEditor; private SkinEditor skinEditor;
[Resolved]
private SkinManager skinManager { get; set; }
protected override bool Autoplay => true;
[SetUpSteps] [SetUpSteps]
public override void SetUpSteps() public override void SetUpSteps()
{ {
base.SetUpSteps(); base.SetUpSteps();
AddStep("add editor overlay", () => AddStep("reload skin editor", () =>
{ {
skinEditor?.Expire(); skinEditor?.Expire();
Player.ScaleTo(SkinEditorOverlay.VISIBLE_TARGET_SCALE); Player.ScaleTo(SkinEditorOverlay.VISIBLE_TARGET_SCALE);

View File

@ -1,13 +1,11 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
@ -32,12 +30,13 @@ namespace osu.Game.Tests.Visual.Gameplay
SetContents(() => SetContents(() =>
{ {
var ruleset = new OsuRuleset(); var ruleset = new OsuRuleset();
var mods = new[] { ruleset.GetAutoplayMod() };
var working = CreateWorkingBeatmap(ruleset.RulesetInfo); var working = CreateWorkingBeatmap(ruleset.RulesetInfo);
var beatmap = working.GetPlayableBeatmap(ruleset.RulesetInfo); var beatmap = working.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap); var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap, mods);
var hudOverlay = new HUDOverlay(drawableRuleset, Array.Empty<Mod>()) var hudOverlay = new HUDOverlay(drawableRuleset, mods)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,

View File

@ -7,7 +7,7 @@ using osu.Framework.Testing;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD; using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void SetUpSteps() public void SetUpSteps()
{ {
AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1); AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1);
AddStep("Create accuracy counters", () => SetContents(() => new SkinnableAccuracyCounter())); AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter))));
} }
[Test] [Test]

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Testing; using osu.Framework.Testing;
@ -12,14 +10,12 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD; using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestSceneSkinnableHealthDisplay : SkinnableTestScene public class TestSceneSkinnableHealthDisplay : SkinnableTestScene
{ {
private IEnumerable<SkinnableHealthDisplay> healthDisplays => CreatedDrawables.OfType<SkinnableHealthDisplay>();
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
[Cached(typeof(HealthProcessor))] [Cached(typeof(HealthProcessor))]
@ -28,10 +24,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[SetUpSteps] [SetUpSteps]
public void SetUpSteps() public void SetUpSteps()
{ {
AddStep("Create health displays", () => AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay))));
{
SetContents(() => new SkinnableHealthDisplay());
});
AddStep(@"Reset all", delegate AddStep(@"Reset all", delegate
{ {
healthProcessor.Health.Value = 1; healthProcessor.Health.Value = 1;

View File

@ -1,23 +1,18 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD; using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestSceneSkinnableScoreCounter : SkinnableTestScene public class TestSceneSkinnableScoreCounter : SkinnableTestScene
{ {
private IEnumerable<SkinnableScoreCounter> scoreCounters => CreatedDrawables.OfType<SkinnableScoreCounter>();
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
[Cached] [Cached]
@ -26,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[SetUpSteps] [SetUpSteps]
public void SetUpSteps() public void SetUpSteps()
{ {
AddStep("Create score counters", () => SetContents(() => new SkinnableScoreCounter())); AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter))));
} }
[Test] [Test]
@ -40,7 +35,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestVeryLargeScore() public void TestVeryLargeScore()
{ {
AddStep("set large score", () => scoreCounters.ForEach(counter => scoreProcessor.TotalScore.Value = 1_000_000_000)); AddStep("set large score", () => scoreProcessor.TotalScore.Value = 1_000_000_000);
} }
} }
} }

View File

@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("set user ready", () => client.ChangeState(MultiplayerUserState.Ready)); AddStep("set user ready", () => client.ChangeState(MultiplayerUserState.Ready));
AddStep("delete beatmap", () => beatmaps.Delete(importedSet)); AddStep("delete beatmap", () => beatmaps.Delete(importedSet));
AddAssert("user state is idle", () => client.LocalUser?.State == MultiplayerUserState.Idle); AddUntilStep("user state is idle", () => client.LocalUser?.State == MultiplayerUserState.Idle);
} }
[Test] [Test]

View File

@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
@ -45,14 +46,14 @@ namespace osu.Game.Tests.Visual.Online
switch (args.Action) switch (args.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
args.NewItems.Cast<Mod>().ForEach(mod => selectedMods.Add(new OsuSpriteText args.NewItems.AsNonNull().Cast<Mod>().ForEach(mod => selectedMods.Add(new OsuSpriteText
{ {
Text = mod.Acronym, Text = mod.Acronym,
})); }));
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
args.OldItems.Cast<Mod>().ForEach(mod => args.OldItems.AsNonNull().Cast<Mod>().ForEach(mod =>
{ {
foreach (var selected in selectedMods) foreach (var selected in selectedMods)
{ {

View File

@ -11,11 +11,13 @@ using osu.Framework.Platform;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay;
using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
using osu.Game.Users; using osu.Game.Users;
using osuTK.Input; using osuTK.Input;
@ -24,8 +26,6 @@ namespace osu.Game.Tests.Visual.Playlists
{ {
public class TestScenePlaylistsRoomSubScreen : RoomTestScene public class TestScenePlaylistsRoomSubScreen : RoomTestScene
{ {
protected override bool UseOnlineAPI => true;
[Cached(typeof(IRoomManager))] [Cached(typeof(IRoomManager))]
private readonly TestRoomManager roomManager = new TestRoomManager(); private readonly TestRoomManager roomManager = new TestRoomManager();
@ -41,6 +41,18 @@ namespace osu.Game.Tests.Visual.Playlists
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait();
((DummyAPIAccess)API).HandleRequest = req =>
{
switch (req)
{
case CreateRoomScoreRequest createRoomScoreRequest:
createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 });
return true;
}
return false;
};
} }
[SetUpSteps] [SetUpSteps]
@ -59,12 +71,16 @@ namespace osu.Game.Tests.Visual.Playlists
Room.Name.Value = "my awesome room"; Room.Name.Value = "my awesome room";
Room.Host.Value = new User { Id = 2, Username = "peppy" }; Room.Host.Value = new User { Id = 2, Username = "peppy" };
Room.RecentParticipants.Add(Room.Host.Value); Room.RecentParticipants.Add(Room.Host.Value);
Room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
Room.Playlist.Add(new PlaylistItem Room.Playlist.Add(new PlaylistItem
{ {
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo } Ruleset = { Value = new OsuRuleset().RulesetInfo }
}); });
}); });
AddStep("start match", () => match.ChildrenOfType<PlaylistsReadyButton>().First().Click());
AddUntilStep("player loader loaded", () => Stack.CurrentScreen is PlayerLoader);
} }
[Test] [Test]

View File

@ -1,32 +1,65 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // 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;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Screens.Ranking.Expanded; using osu.Game.Screens.Ranking.Expanded;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking namespace osu.Game.Tests.Visual.Ranking
{ {
public class TestSceneStarRatingDisplay : OsuTestScene public class TestSceneStarRatingDisplay : OsuTestScene
{ {
public TestSceneStarRatingDisplay() [Test]
public void TestDisplay()
{ {
Child = new FillFlowContainer AddStep("load displays", () => Child = new FillFlowContainer
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Children = new Drawable[] ChildrenEnumerable = new[]
{ {
new StarRatingDisplay(new StarDifficulty(1.23, 0)), 1.23,
new StarRatingDisplay(new StarDifficulty(2.34, 0)), 2.34,
new StarRatingDisplay(new StarDifficulty(3.45, 0)), 3.45,
new StarRatingDisplay(new StarDifficulty(4.56, 0)), 4.56,
new StarRatingDisplay(new StarDifficulty(5.67, 0)), 5.67,
new StarRatingDisplay(new StarDifficulty(6.78, 0)), 6.78,
new StarRatingDisplay(new StarDifficulty(10.11, 0)), 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

@ -264,7 +264,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private void moveLogoFacade() private void moveLogoFacade()
{ {
if (!(logoFacade?.Transforms).Any() && !(transferContainer?.Transforms).Any()) if (!logoFacade.Transforms.Any() && !transferContainer.Transforms.Any())
{ {
Random random = new Random(); Random random = new Random();
trackingContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300); trackingContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300);

View File

@ -7,6 +7,7 @@ using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.Win32; using Microsoft.Win32;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Threading; using osu.Framework.Threading;
@ -77,8 +78,8 @@ namespace osu.Game.Tournament.IPC
using (var stream = IPCStorage.GetStream(file_ipc_filename)) using (var stream = IPCStorage.GetStream(file_ipc_filename))
using (var sr = new StreamReader(stream)) using (var sr = new StreamReader(stream))
{ {
var beatmapId = int.Parse(sr.ReadLine()); var beatmapId = int.Parse(sr.ReadLine().AsNonNull());
var mods = int.Parse(sr.ReadLine()); var mods = int.Parse(sr.ReadLine().AsNonNull());
if (lastBeatmapId != beatmapId) if (lastBeatmapId != beatmapId)
{ {
@ -124,7 +125,7 @@ namespace osu.Game.Tournament.IPC
using (var stream = IPCStorage.GetStream(file_ipc_state_filename)) using (var stream = IPCStorage.GetStream(file_ipc_state_filename))
using (var sr = new StreamReader(stream)) using (var sr = new StreamReader(stream))
{ {
State.Value = (TourneyState)Enum.Parse(typeof(TourneyState), sr.ReadLine()); State.Value = (TourneyState)Enum.Parse(typeof(TourneyState), sr.ReadLine().AsNonNull());
} }
} }
catch (Exception) catch (Exception)

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="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> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops computing the star difficulty.</param>
/// <returns>The <see cref="StarDifficulty"/>.</returns> /// <returns>The <see cref="StarDifficulty"/>.</returns>
public Task<StarDifficulty> GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, [CanBeNull] IEnumerable<Mod> mods = null, public virtual Task<StarDifficulty> GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null,
CancellationToken cancellationToken = default) [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. // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset.
rulesetInfo ??= beatmapInfo.Ruleset; rulesetInfo ??= beatmapInfo.Ruleset;

View File

@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Localisation;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.IO.Serialization; using osu.Game.IO.Serialization;
@ -127,6 +128,8 @@ namespace osu.Game.Beatmaps
// Metadata // Metadata
public string Version { get; set; } public string Version { get; set; }
private string versionString => string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]";
[JsonProperty("difficulty_rating")] [JsonProperty("difficulty_rating")]
public double StarDifficulty { get; set; } public double StarDifficulty { get; set; }
@ -143,11 +146,12 @@ namespace osu.Game.Beatmaps
Version Version
}.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty<string>()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); }.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty<string>()).Where(s => !string.IsNullOrEmpty(s)).ToArray();
public override string ToString() public override string ToString() => $"{Metadata ?? BeatmapSet?.Metadata} {versionString}".Trim();
{
string version = string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]";
return $"{Metadata ?? BeatmapSet?.Metadata} {version}".Trim(); public RomanisableString ToRomanisableString()
{
var metadata = (Metadata ?? BeatmapSet?.Metadata)?.ToRomanisableString() ?? new RomanisableString(null, null);
return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim());
} }
public bool Equals(BeatmapInfo other) public bool Equals(BeatmapInfo other)

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -83,6 +82,12 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
beatmap.OnlineBeatmapID = res.OnlineBeatmapID; beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
if (beatmap.Metadata != null)
beatmap.Metadata.AuthorID = res.AuthorID;
if (beatmap.BeatmapSet.Metadata != null)
beatmap.BeatmapSet.Metadata.AuthorID = res.AuthorID;
LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}."); LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
} }
} }
@ -157,7 +162,7 @@ namespace osu.Game.Beatmaps
using (var cmd = db.CreateCommand()) using (var cmd = db.CreateCommand())
{ {
cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path"; cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path";
cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash)); cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash));
cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value)); cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value));
@ -174,6 +179,12 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0); beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0);
beatmap.OnlineBeatmapID = reader.GetInt32(1); beatmap.OnlineBeatmapID = reader.GetInt32(1);
if (beatmap.Metadata != null)
beatmap.Metadata.AuthorID = reader.GetInt32(3);
if (beatmap.BeatmapSet.Metadata != null)
beatmap.BeatmapSet.Metadata.AuthorID = reader.GetInt32(3);
LogForModel(set, $"Cached local retrieval for {beatmap}."); LogForModel(set, $"Cached local retrieval for {beatmap}.");
return true; return true;
} }
@ -194,17 +205,6 @@ namespace osu.Game.Beatmaps
cacheDownloadRequest?.Dispose(); cacheDownloadRequest?.Dispose();
updateScheduler?.Dispose(); updateScheduler?.Dispose();
} }
[Serializable]
[SuppressMessage("ReSharper", "InconsistentNaming")]
private class CachedOnlineBeatmapLookup
{
public int approved { get; set; }
public int? beatmapset_id { get; set; }
public int? beatmap_id { get; set; }
}
} }
} }
} }

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Localisation;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Users; using osu.Game.Users;
@ -34,6 +35,21 @@ namespace osu.Game.Beatmaps
[JsonIgnore] [JsonIgnore]
public List<BeatmapSetInfo> BeatmapSets { get; set; } public List<BeatmapSetInfo> BeatmapSets { get; set; }
/// <summary>
/// Helper property to deserialize a username to <see cref="User"/>.
/// </summary>
[JsonProperty(@"user_id")]
[Column("AuthorID")]
public int AuthorID
{
get => Author?.Id ?? 1;
set
{
Author ??= new User();
Author.Id = value;
}
}
/// <summary> /// <summary>
/// Helper property to deserialize a username to <see cref="User"/>. /// Helper property to deserialize a username to <see cref="User"/>.
/// </summary> /// </summary>
@ -42,7 +58,11 @@ namespace osu.Game.Beatmaps
public string AuthorString public string AuthorString
{ {
get => Author?.Username; get => Author?.Username;
set => Author = new User { Username = value }; set
{
Author ??= new User();
Author.Username = value;
}
} }
/// <summary> /// <summary>
@ -71,6 +91,12 @@ namespace osu.Game.Beatmaps
return $"{Artist} - {Title} {author}".Trim(); return $"{Artist} - {Title} {author}".Trim();
} }
public RomanisableString ToRomanisableString()
{
string author = Author == null ? string.Empty : $"({Author})";
return new RomanisableString($"{ArtistUnicode} - {TitleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim());
}
[JsonIgnore] [JsonIgnore]
public string[] SearchableTerms => new[] public string[] SearchableTerms => new[]
{ {

View File

@ -324,7 +324,7 @@ namespace osu.Game.Beatmaps
public bool SkinLoaded => skin.IsResultAvailable; public bool SkinLoaded => skin.IsResultAvailable;
public ISkin Skin => skin.Value; public ISkin Skin => skin.Value;
protected virtual ISkin GetSkin() => new DefaultSkin(); protected virtual ISkin GetSkin() => new DefaultSkin(null);
private readonly RecyclableLazy<ISkin> skin; private readonly RecyclableLazy<ISkin> skin;
public abstract Stream GetStream(string storagePath); public abstract Stream GetStream(string storagePath);

View File

@ -472,7 +472,7 @@ namespace osu.Game.Database
} }
/// <summary> /// <summary>
/// Delete new file. /// Delete an existing file.
/// </summary> /// </summary>
/// <param name="model">The item to operate on.</param> /// <param name="model">The item to operate on.</param>
/// <param name="file">The existing file to be deleted.</param> /// <param name="file">The existing file to be deleted.</param>

View File

@ -3,8 +3,10 @@
using System; using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Screens.Play.HUD;
using osuTK; using osuTK;
namespace osu.Game.Extensions namespace osu.Game.Extensions
@ -43,5 +45,23 @@ namespace osu.Game.Extensions
/// <returns>The delta vector in Parent's coordinates.</returns> /// <returns>The delta vector in Parent's coordinates.</returns>
public static Vector2 ScreenSpaceDeltaToParentSpace(this Drawable drawable, Vector2 delta) => public static Vector2 ScreenSpaceDeltaToParentSpace(this Drawable drawable, Vector2 delta) =>
drawable.Parent.ToLocalSpace(drawable.Parent.ToScreenSpace(Vector2.Zero) + delta); drawable.Parent.ToLocalSpace(drawable.Parent.ToScreenSpace(Vector2.Zero) + delta);
public static SkinnableInfo CreateSkinnableInfo(this Drawable component) => new SkinnableInfo(component);
public static void ApplySkinnableInfo(this Drawable component, SkinnableInfo info)
{
// todo: can probably make this better via deserialisation directly using a common interface.
component.Position = info.Position;
component.Rotation = info.Rotation;
component.Scale = info.Scale;
component.Anchor = info.Anchor;
component.Origin = info.Origin;
if (component is Container container)
{
foreach (var child in info.Children)
container.Add(child.CreateInstance());
}
}
} }
} }

View File

@ -6,6 +6,7 @@
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Extensions.ObjectExtensions;
namespace osu.Game.Extensions namespace osu.Game.Extensions
{ {
@ -50,7 +51,7 @@ namespace osu.Game.Extensions
} }
else if (continuationTask.IsFaulted) else if (continuationTask.IsFaulted)
{ {
tcs.TrySetException(continuationTask.Exception); tcs.TrySetException(continuationTask.Exception.AsNonNull());
} }
else else
{ {

View File

@ -0,0 +1,31 @@
// 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.Linq;
namespace osu.Game.Extensions
{
internal static class TypeExtensions
{
/// <summary>
/// Returns <paramref name="type"/>'s <see cref="Type.AssemblyQualifiedName"/>
/// with the assembly version, culture and public key token values removed.
/// </summary>
/// <remarks>
/// This method is usually used in extensibility scenarios (i.e. for custom rulesets or skins)
/// when a version-agnostic identifier associated with a C# class - potentially originating from
/// an external assembly - is needed.
/// Leaving only the type and assembly names in such a scenario allows to preserve compatibility
/// across assembly versions.
/// </remarks>
internal static string GetInvariantInstantiationInfo(this Type type)
{
string assemblyQualifiedName = type.AssemblyQualifiedName;
if (assemblyQualifiedName == null)
throw new ArgumentException($"{type}'s assembly-qualified name is null. Ensure that it is a concrete type and not a generic type parameter.", nameof(type));
return string.Join(',', assemblyQualifiedName.Split(',').Take(2));
}
}
}

View File

@ -7,7 +7,10 @@ using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Graphics.Containers namespace osu.Game.Graphics.Containers
@ -59,6 +62,14 @@ namespace osu.Game.Graphics.Containers
public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action<SpriteText> creationParameters = null) public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action<SpriteText> creationParameters = null)
=> createLink(AddText(text, creationParameters), new LinkDetails(action, argument), tooltipText); => createLink(AddText(text, creationParameters), new LinkDetails(action, argument), tooltipText);
public void AddLink(LocalisableString text, LinkAction action, string argument, string tooltipText = null, Action<SpriteText> creationParameters = null)
{
var spriteText = new OsuSpriteText { Text = text };
AddText(spriteText, creationParameters);
createLink(spriteText.Yield(), new LinkDetails(action, argument), tooltipText);
}
public void AddLink(IEnumerable<SpriteText> text, LinkAction action = LinkAction.External, string linkArgument = null, string tooltipText = null) public void AddLink(IEnumerable<SpriteText> text, LinkAction action = LinkAction.External, string linkArgument = null, string tooltipText = null)
{ {
foreach (var t in text) foreach (var t in text)

View File

@ -0,0 +1,18 @@
// 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.Framework.Allocation;
namespace osu.Game.Graphics.UserInterface
{
public class DangerousTriangleButton : TriangleButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.PinkDark;
Triangles.ColourDark = colours.PinkDarker;
Triangles.ColourLight = colours.Pink;
}
}
}

View File

@ -38,6 +38,7 @@ namespace osu.Game.IO.Legacy
/// <summary> Reads a string from the buffer. Overrides the base implementation so it can cope with nulls. </summary> /// <summary> Reads a string from the buffer. Overrides the base implementation so it can cope with nulls. </summary>
public override string ReadString() public override string ReadString()
{ {
// ReSharper disable once AssignNullToNotNullAttribute
if (ReadByte() == 0) return null; if (ReadByte() == 0) return null;
return base.ReadString(); return base.ReadString();

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using osu.Framework.Extensions.ObjectExtensions;
namespace osu.Game.IO.Serialization.Converters namespace osu.Game.IO.Serialization.Converters
{ {
@ -60,7 +61,7 @@ namespace osu.Game.IO.Serialization.Converters
throw new JsonException("Expected $type token."); throw new JsonException("Expected $type token.");
var typeName = lookupTable[(int)tok["$type"]]; var typeName = lookupTable[(int)tok["$type"]];
var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); var instance = (T)Activator.CreateInstance(Type.GetType(typeName).AsNonNull());
serializer.Populate(itemReader, instance); serializer.Populate(itemReader, instance);
list.Add(instance); list.Add(instance);

View File

@ -48,7 +48,7 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleBeatmapListing), new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleBeatmapListing),
new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications), new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications),
new KeyBinding(new[] { InputKey.Shift, InputKey.F2 }, GlobalAction.ToggleSkinEditor), new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.S }, GlobalAction.ToggleSkinEditor),
new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.Escape, GlobalAction.Back),
new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back), new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back),

View File

@ -0,0 +1,508 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20210511060743_AddSkinInstantiationInfo")]
partial class AddSkinInstantiationInfo
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<double>("SliderMultiplier");
b.Property<double>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("AudioLeadIn");
b.Property<double>("BPM");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<bool>("EpilepsyWarning");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<double>("Length");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset>("DateAdded");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Key")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<int?>("SkinInfoID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("SkinInfoID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int?>("ScoreInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("ScoreInfoID");
b.ToTable("ScoreFileInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("Accuracy")
.HasColumnType("DECIMAL(1,4)");
b.Property<int>("BeatmapInfoID");
b.Property<int>("Combo");
b.Property<DateTimeOffset>("Date");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int>("MaxCombo");
b.Property<string>("ModsJson")
.HasColumnName("Mods");
b.Property<long?>("OnlineScoreID");
b.Property<double?>("PP");
b.Property<int>("Rank");
b.Property<int>("RulesetID");
b.Property<string>("StatisticsJson")
.HasColumnName("Statistics");
b.Property<long>("TotalScore");
b.Property<int?>("UserID")
.HasColumnName("UserID");
b.Property<string>("UserString")
.HasColumnName("User");
b.HasKey("ID");
b.HasIndex("BeatmapInfoID");
b.HasIndex("OnlineScoreID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("ScoreInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Settings")
.HasForeignKey("SkinInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Scoring.ScoreInfo")
.WithMany("Files")
.HasForeignKey("ScoreInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany("Scores")
.HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class AddSkinInstantiationInfo : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "InstantiationInfo",
table: "SkinInfo",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "InstantiationInfo",
table: "SkinInfo");
}
}
}

View File

@ -0,0 +1,511 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20210514062639_AddAuthorIdToBeatmapMetadata")]
partial class AddAuthorIdToBeatmapMetadata
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<double>("SliderMultiplier");
b.Property<double>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("AudioLeadIn");
b.Property<double>("BPM");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<bool>("EpilepsyWarning");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<double>("Length");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<int>("AuthorID")
.HasColumnName("AuthorID");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset>("DateAdded");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Key")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<int?>("SkinInfoID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("SkinInfoID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int?>("ScoreInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("ScoreInfoID");
b.ToTable("ScoreFileInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("Accuracy")
.HasColumnType("DECIMAL(1,4)");
b.Property<int>("BeatmapInfoID");
b.Property<int>("Combo");
b.Property<DateTimeOffset>("Date");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int>("MaxCombo");
b.Property<string>("ModsJson")
.HasColumnName("Mods");
b.Property<long?>("OnlineScoreID");
b.Property<double?>("PP");
b.Property<int>("Rank");
b.Property<int>("RulesetID");
b.Property<string>("StatisticsJson")
.HasColumnName("Statistics");
b.Property<long>("TotalScore");
b.Property<int?>("UserID")
.HasColumnName("UserID");
b.Property<string>("UserString")
.HasColumnName("User");
b.HasKey("ID");
b.HasIndex("BeatmapInfoID");
b.HasIndex("OnlineScoreID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("ScoreInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Settings")
.HasForeignKey("SkinInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Scoring.ScoreInfo")
.WithMany("Files")
.HasForeignKey("ScoreInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany("Scores")
.HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class AddAuthorIdToBeatmapMetadata : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "AuthorID",
table: "BeatmapMetadata",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "AuthorID",
table: "BeatmapMetadata");
}
}
}

View File

@ -126,6 +126,9 @@ namespace osu.Game.Migrations
b.Property<string>("AudioFile"); b.Property<string>("AudioFile");
b.Property<int>("AuthorID")
.HasColumnName("AuthorID");
b.Property<string>("AuthorString") b.Property<string>("AuthorString")
.HasColumnName("Author"); .HasColumnName("Author");
@ -141,8 +144,6 @@ namespace osu.Game.Migrations
b.Property<string>("TitleUnicode"); b.Property<string>("TitleUnicode");
b.Property<string>("VideoFile");
b.HasKey("ID"); b.HasKey("ID");
b.ToTable("BeatmapMetadata"); b.ToTable("BeatmapMetadata");
@ -352,7 +353,7 @@ namespace osu.Game.Migrations
b.Property<long>("TotalScore"); b.Property<long>("TotalScore");
b.Property<long?>("UserID") b.Property<int?>("UserID")
.HasColumnName("UserID"); .HasColumnName("UserID");
b.Property<string>("UserString") b.Property<string>("UserString")
@ -402,6 +403,8 @@ namespace osu.Game.Migrations
b.Property<string>("Hash"); b.Property<string>("Hash");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name"); b.Property<string>("Name");
b.HasKey("ID"); b.HasKey("ID");

View File

@ -270,7 +270,7 @@ namespace osu.Game.Online.API
{ {
try try
{ {
return JObject.Parse(req.GetResponseString()).SelectToken("form_error", true).AsNonNull().ToObject<RegistrationRequest.RegistrationRequestErrors>(); return JObject.Parse(req.GetResponseString().AsNonNull()).SelectToken("form_error", true).AsNonNull().ToObject<RegistrationRequest.RegistrationRequestErrors>();
} }
catch catch
{ {

View File

@ -24,7 +24,13 @@ namespace osu.Game.Online.Spectator
[Key(2)] [Key(2)]
public IEnumerable<APIMod> Mods { get; set; } = Enumerable.Empty<APIMod>(); public IEnumerable<APIMod> Mods { get; set; } = Enumerable.Empty<APIMod>();
public bool Equals(SpectatorState other) => BeatmapID == other?.BeatmapID && Mods.SequenceEqual(other?.Mods) && RulesetID == other?.RulesetID; public bool Equals(SpectatorState other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return BeatmapID == other.BeatmapID && Mods.SequenceEqual(other.Mods) && RulesetID == other.RulesetID;
}
public override string ToString() => $"Beatmap:{BeatmapID} Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}"; public override string ToString() => $"Beatmap:{BeatmapID} Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}";
} }

View File

@ -339,22 +339,13 @@ namespace osu.Game.Overlays.KeyBinding
} }
} }
public class ClearButton : TriangleButton public class ClearButton : DangerousTriangleButton
{ {
public ClearButton() public ClearButton()
{ {
Text = "Clear"; Text = "Clear";
Size = new Vector2(80, 20); Size = new Vector2(80, 20);
} }
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.Pink;
Triangles.ColourDark = colours.PinkDark;
Triangles.ColourLight = colours.PinkLight;
}
} }
public class KeyButton : Container public class KeyButton : Container

View File

@ -11,7 +11,6 @@ using osu.Game.Input;
using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osuTK; using osuTK;
using osu.Game.Graphics;
namespace osu.Game.Overlays.KeyBinding namespace osu.Game.Overlays.KeyBinding
{ {
@ -55,10 +54,10 @@ namespace osu.Game.Overlays.KeyBinding
} }
} }
public class ResetButton : TriangleButton public class ResetButton : DangerousTriangleButton
{ {
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load()
{ {
Text = "Reset all bindings in section"; Text = "Reset all bindings in section";
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
@ -66,10 +65,6 @@ namespace osu.Game.Overlays.KeyBinding
Height = 20; Height = 20;
Content.CornerRadius = 5; Content.CornerRadius = 5;
BackgroundColour = colours.PinkDark;
Triangles.ColourDark = colours.PinkDarker;
Triangles.ColourLight = colours.Pink;
} }
} }
} }

View File

@ -3,7 +3,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Edit.Checks.Components;
@ -29,9 +28,9 @@ namespace osu.Game.Rulesets.Edit
new CheckConcurrentObjects() 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 CheckCategory Category => CheckCategory.Audio;
protected override string TypeOfFile => "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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Edit.Checks.Components;
namespace osu.Game.Rulesets.Edit.Checks namespace osu.Game.Rulesets.Edit.Checks
@ -26,13 +25,13 @@ namespace osu.Game.Rulesets.Edit.Checks
new IssueTemplateNoBitrate(this) 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) if (audioFile == null)
yield break; yield break;
var track = workingBeatmap.Track; var track = context.WorkingBeatmap.Track;
if (track?.Bitrate == null || track.Bitrate.Value == 0) if (track?.Bitrate == null || track.Bitrate.Value == 0)
yield return new IssueTemplateNoBitrate(this).Create(); 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 CheckCategory Category => CheckCategory.Resources;
protected override string TypeOfFile => "background"; 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Edit.Checks.Components;
namespace osu.Game.Rulesets.Edit.Checks namespace osu.Game.Rulesets.Edit.Checks
@ -30,13 +29,13 @@ namespace osu.Game.Rulesets.Edit.Checks
new IssueTemplateTooUncompressed(this) 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) if (backgroundFile == null)
yield break; yield break;
var texture = workingBeatmap.Background; var texture = context.WorkingBeatmap.Background;
if (texture == null) if (texture == null)
yield break; yield break;
@ -48,8 +47,8 @@ namespace osu.Game.Rulesets.Edit.Checks
else if (texture.Width < low_width || texture.Height < low_height) else if (texture.Width < low_width || texture.Height < low_height)
yield return new IssueTemplateLowResolution(this).Create(texture.Width, texture.Height); yield return new IssueTemplateLowResolution(this).Create(texture.Width, texture.Height);
string storagePath = playableBeatmap.BeatmapInfo.BeatmapSet.GetPathForFile(backgroundFile); string storagePath = context.Beatmap.BeatmapInfo.BeatmapSet.GetPathForFile(backgroundFile);
double filesizeMb = workingBeatmap.GetStream(storagePath).Length / (1024d * 1024d); double filesizeMb = context.WorkingBeatmap.GetStream(storagePath).Length / (1024d * 1024d);
if (filesizeMb > max_filesize_mb) if (filesizeMb > max_filesize_mb)
yield return new IssueTemplateTooUncompressed(this).Create(filesizeMb); 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Edit.Checks.Components;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
@ -22,15 +21,17 @@ namespace osu.Game.Rulesets.Edit.Checks
new IssueTemplateConcurrentDifferent(this) 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 hitObjects = context.Beatmap.HitObjects;
{
var hitobject = playableBeatmap.HitObjects[i];
for (int j = i + 1; j < playableBeatmap.HitObjects.Count; ++j) for (int i = 0; i < hitObjects.Count - 1; ++i)
{
var hitobject = hitObjects[i];
for (int j = i + 1; j < hitObjects.Count; ++j)
{ {
var nextHitobject = playableBeatmap.HitObjects[j]; var nextHitobject = hitObjects[j];
// Accounts for rulesets with hitobjects separated by columns, such as Mania. // Accounts for rulesets with hitobjects separated by columns, such as Mania.
// In these cases we only care about concurrent objects within the same column. // 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 CheckCategory Category { get; }
protected abstract string TypeOfFile { 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}"); public CheckMetadata Metadata => new CheckMetadata(Category, $"Missing {TypeOfFile}");
@ -21,9 +21,9 @@ namespace osu.Game.Rulesets.Edit.Checks
new IssueTemplateDoesNotExist(this) 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) if (filename == null)
{ {
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Edit.Checks
} }
// If the file is set, also make sure it still exists. // 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) if (storagePath != null)
yield break; yield break;

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Edit.Checks.Components;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
@ -22,11 +21,11 @@ namespace osu.Game.Rulesets.Edit.Checks
new IssueTemplateSmallUnsnap(this) 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); double startUnsnap = hitobject.StartTime - controlPointInfo.GetClosestSnappedTime(hitobject.StartTime);
string startPostfix = hitobject is IHasDuration ? "start" : ""; string startPostfix = hitobject is IHasDuration ? "start" : "";

View File

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

View File

@ -18,7 +18,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components
/// <summary> An error occurred and a complete check could not be made. </summary> /// <summary> An error occurred and a complete check could not be made. </summary>
Error, Error,
// TODO: Negligible issues should be hidden by default.
/// <summary> A possible mistake so minor/unlikely that it can often be safely ignored. </summary> /// <summary> A possible mistake so minor/unlikely that it can often be safely ignored. </summary>
Negligible, Negligible,
} }

View File

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

View File

@ -1,9 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
@ -18,14 +16,6 @@ namespace osu.Game.Rulesets.Mods
public override ModType Type => ModType.DifficultyIncrease; public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => true; public override bool Ranked => true;
/// <summary>
/// Check whether the provided hitobject should be considered the "first" hideable object.
/// Can be used to skip spinners, for instance.
/// </summary>
/// <param name="hitObject">The hitobject to check.</param>
[Obsolete("Use IsFirstAdjustableObject() instead.")] // Can be removed 20210506
protected virtual bool IsFirstHideableObject(DrawableHitObject hitObject) => true;
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{ {
// Default value of ScoreProcessor's Rank in Hidden Mod should be SS+ // Default value of ScoreProcessor's Rank in Hidden Mod should be SS+
@ -46,39 +36,5 @@ namespace osu.Game.Rulesets.Mods
return rank; return rank;
} }
} }
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
#pragma warning disable 618
ApplyFirstObjectIncreaseVisibilityState(hitObject, state);
#pragma warning restore 618
}
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
#pragma warning disable 618
ApplyHiddenState(hitObject, state);
#pragma warning restore 618
}
/// <summary>
/// Apply a special visibility state to the first object in a beatmap, if the user chooses to turn on the "increase first object visibility" setting.
/// </summary>
/// <param name="hitObject">The hit object to apply the state change to.</param>
/// <param name="state">The state of the hit object.</param>
[Obsolete("Use ApplyIncreasedVisibilityState() instead.")] // Can be removed 20210506
protected virtual void ApplyFirstObjectIncreaseVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
}
/// <summary>
/// Apply a hidden state to the provided object.
/// </summary>
/// <param name="hitObject">The hit object to apply the state change to.</param>
/// <param name="state">The state of the hit object.</param>
[Obsolete("Use ApplyNormalVisibilityState() instead.")] // Can be removed 20210506
protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state)
{
}
} }
} }

View File

@ -26,6 +26,7 @@ using JetBrains.Annotations;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Extensions;
using osu.Game.Rulesets.Filter; using osu.Game.Rulesets.Filter;
using osu.Game.Screens.Ranking.Statistics; using osu.Game.Screens.Ranking.Statistics;
@ -135,7 +136,7 @@ namespace osu.Game.Rulesets
Name = Description, Name = Description,
ShortName = ShortName, ShortName = ShortName,
ID = (this as ILegacyRuleset)?.LegacyID, ID = (this as ILegacyRuleset)?.LegacyID,
InstantiationInfo = GetType().AssemblyQualifiedName, InstantiationInfo = GetType().GetInvariantInstantiationInfo(),
Available = true, Available = true,
}; };
} }

View File

@ -3,8 +3,8 @@
using System; using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Testing; using osu.Framework.Testing;
namespace osu.Game.Rulesets namespace osu.Game.Rulesets
@ -18,20 +18,7 @@ namespace osu.Game.Rulesets
public string ShortName { get; set; } public string ShortName { get; set; }
private string instantiationInfo; public string InstantiationInfo { get; set; }
public string InstantiationInfo
{
get => instantiationInfo;
set => instantiationInfo = abbreviateInstantiationInfo(value);
}
private string abbreviateInstantiationInfo(string value)
{
// exclude version onwards, matching only on namespace and type.
// this is mainly to allow for new versions of already loaded rulesets to "upgrade" from old.
return string.Join(',', value.Split(',').Take(2));
}
[JsonIgnore] [JsonIgnore]
public bool Available { get; set; } public bool Available { get; set; }
@ -41,7 +28,7 @@ namespace osu.Game.Rulesets
{ {
if (!Available) return null; if (!Available) return null;
var ruleset = (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo)); var ruleset = (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo).AsNonNull());
// overwrite the pre-populated RulesetInfo with a potentially database attached copy. // overwrite the pre-populated RulesetInfo with a potentially database attached copy.
ruleset.RulesetInfo = this; ruleset.RulesetInfo = this;

View File

@ -7,6 +7,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using osu.Framework; using osu.Framework;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Database; using osu.Game.Database;
@ -111,7 +112,7 @@ namespace osu.Game.Rulesets
{ {
try try
{ {
var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo))).RulesetInfo; var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo).AsNonNull())).RulesetInfo;
r.Name = instanceInfo.Name; r.Name = instanceInfo.Name;
r.ShortName = instanceInfo.ShortName; r.ShortName = instanceInfo.ShortName;

View File

@ -3,9 +3,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
@ -24,6 +26,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// Includes selection and manipulation support via a <see cref="Components.SelectionHandler{T}"/>. /// Includes selection and manipulation support via a <see cref="Components.SelectionHandler{T}"/>.
/// </summary> /// </summary>
public abstract class BlueprintContainer<T> : CompositeDrawable, IKeyBindingHandler<PlatformAction> public abstract class BlueprintContainer<T> : CompositeDrawable, IKeyBindingHandler<PlatformAction>
where T : class
{ {
protected DragBox DragBox { get; private set; } protected DragBox DragBox { get; private set; }
@ -39,6 +42,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private IEditorChangeHandler changeHandler { get; set; } private IEditorChangeHandler changeHandler { get; set; }
protected readonly BindableList<T> SelectedItems = new BindableList<T>();
protected BlueprintContainer() protected BlueprintContainer()
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
@ -47,6 +52,24 @@ namespace osu.Game.Screens.Edit.Compose.Components
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
SelectedItems.CollectionChanged += (selectedObjects, args) =>
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var o in args.NewItems)
SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Select();
break;
case NotifyCollectionChangedAction.Remove:
foreach (var o in args.OldItems)
SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Deselect();
break;
}
};
SelectionHandler = CreateSelectionHandler(); SelectionHandler = CreateSelectionHandler();
SelectionHandler.DeselectAll = deselectAll; SelectionHandler.DeselectAll = deselectAll;

View File

@ -2,10 +2,8 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
@ -24,8 +22,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected readonly HitObjectComposer Composer; protected readonly HitObjectComposer Composer;
private readonly BindableList<HitObject> selectedHitObjects = new BindableList<HitObject>();
protected EditorBlueprintContainer(HitObjectComposer composer) protected EditorBlueprintContainer(HitObjectComposer composer)
{ {
Composer = composer; Composer = composer;
@ -34,23 +30,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
selectedHitObjects.BindTo(Beatmap.SelectedHitObjects); SelectedItems.BindTo(Beatmap.SelectedHitObjects);
selectedHitObjects.CollectionChanged += (selectedObjects, args) =>
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var o in args.NewItems)
SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Select();
break;
case NotifyCollectionChangedAction.Remove:
foreach (var o in args.OldItems)
SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Deselect();
break;
}
};
} }
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -84,8 +84,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (activeHandle?.IsHeld == true) if (activeHandle?.IsHeld == true)
return; return;
activeHandle = rotationHandles.SingleOrDefault(h => h.IsHeld || h.IsHovered); activeHandle = rotationHandles.FirstOrDefault(h => h.IsHeld || h.IsHovered);
activeHandle ??= allDragHandles.SingleOrDefault(h => h.IsHovered); activeHandle ??= allDragHandles.FirstOrDefault(h => h.IsHovered);
if (activeHandle != null) if (activeHandle != null)
{ {

View File

@ -635,6 +635,9 @@ namespace osu.Game.Screens.Edit
case EditorScreenMode.Verify: case EditorScreenMode.Verify:
currentScreen = new VerifyScreen(); currentScreen = new VerifyScreen();
break; break;
default:
throw new InvalidOperationException("Editor menu bar switched to an unsupported mode");
} }
LoadComponentAsync(currentScreen, newScreen => LoadComponentAsync(currentScreen, newScreen =>

View File

@ -10,7 +10,7 @@ using osu.Game.Overlays;
namespace osu.Game.Screens.Edit namespace osu.Game.Screens.Edit
{ {
public class RoundedContentEditorScreen : EditorScreen public class EditorRoundedScreen : EditorScreen
{ {
public const int HORIZONTAL_PADDING = 100; public const int HORIZONTAL_PADDING = 100;
@ -24,7 +24,7 @@ namespace osu.Game.Screens.Edit
protected override Container<Drawable> Content => roundedContent; protected override Container<Drawable> Content => roundedContent;
public RoundedContentEditorScreen(EditorScreenMode mode) public EditorRoundedScreen(EditorScreenMode mode)
: base(mode) : base(mode)
{ {
ColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); ColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);

View File

@ -0,0 +1,44 @@
// 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;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
namespace osu.Game.Screens.Edit
{
public abstract class EditorRoundedScreenSettings : CompositeDrawable
{
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colours)
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new Box
{
Colour = colours.Background4,
RelativeSizeAxes = Axes.Both,
},
new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = CreateSections()
},
}
};
}
protected abstract IReadOnlyList<Drawable> CreateSections();
}
}

View File

@ -0,0 +1,61 @@
// 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.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Screens.Edit
{
public abstract class EditorRoundedScreenSettingsSection : CompositeDrawable
{
private const int header_height = 50;
protected abstract string HeaderText { get; }
protected FillFlowContainer Flow { get; private set; }
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colours)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Masking = true;
InternalChildren = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Height = header_height,
Padding = new MarginPadding { Horizontal = 20 },
Child = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Text = HeaderText,
Font = new FontUsage(size: 25, weight: "bold")
}
},
new Container
{
Y = header_height,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = Flow = new FillFlowContainer
{
Padding = new MarginPadding { Horizontal = 20 },
Spacing = new Vector2(10),
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
}
}
};
}
}
}

View File

@ -7,7 +7,7 @@ using osu.Game.Graphics.Containers;
namespace osu.Game.Screens.Edit.Setup namespace osu.Game.Screens.Edit.Setup
{ {
public class SetupScreen : RoundedContentEditorScreen public class SetupScreen : EditorRoundedScreen
{ {
[Cached] [Cached]
private SectionsContainer<SetupSection> sections = new SectionsContainer<SetupSection>(); private SectionsContainer<SetupSection> sections = new SectionsContainer<SetupSection>();

View File

@ -93,7 +93,7 @@ namespace osu.Game.Screens.Edit.Setup
public SetupScreenTabControl() public SetupScreenTabControl()
{ {
TabContainer.Margin = new MarginPadding { Horizontal = RoundedContentEditorScreen.HORIZONTAL_PADDING }; TabContainer.Margin = new MarginPadding { Horizontal = EditorRoundedScreen.HORIZONTAL_PADDING };
AddInternal(background = new Box AddInternal(background = new Box
{ {

View File

@ -33,7 +33,7 @@ namespace osu.Game.Screens.Edit.Setup
Padding = new MarginPadding Padding = new MarginPadding
{ {
Vertical = 10, Vertical = 10,
Horizontal = RoundedContentEditorScreen.HORIZONTAL_PADDING Horizontal = EditorRoundedScreen.HORIZONTAL_PADDING
}; };
InternalChild = new FillFlowContainer InternalChild = new FillFlowContainer

View File

@ -2,44 +2,13 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
namespace osu.Game.Screens.Edit.Timing namespace osu.Game.Screens.Edit.Timing
{ {
public class ControlPointSettings : CompositeDrawable public class ControlPointSettings : EditorRoundedScreenSettings
{ {
[BackgroundDependencyLoader] protected override IReadOnlyList<Drawable> CreateSections() => new Drawable[]
private void load(OverlayColourProvider colours)
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new Box
{
Colour = colours.Background4,
RelativeSizeAxes = Axes.Both,
},
new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = createSections()
},
}
};
}
private IReadOnlyList<Drawable> createSections() => new Drawable[]
{ {
new GroupSection(), new GroupSection(),
new TimingSection(), new TimingSection(),

View File

@ -15,7 +15,7 @@ using osuTK;
namespace osu.Game.Screens.Edit.Timing namespace osu.Game.Screens.Edit.Timing
{ {
public class TimingScreen : RoundedContentEditorScreen public class TimingScreen : EditorRoundedScreen
{ {
[Cached] [Cached]
private Bindable<ControlPointGroup> selectedGroup = new Bindable<ControlPointGroup>(); private Bindable<ControlPointGroup> selectedGroup = new Bindable<ControlPointGroup>();

View File

@ -0,0 +1,27 @@
// 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.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Overlays.Settings;
namespace osu.Game.Screens.Edit.Verify
{
internal class InterpretationSection : EditorRoundedScreenSettingsSection
{
protected override string HeaderText => "Interpretation";
[BackgroundDependencyLoader]
private void load(VerifyScreen verify)
{
Flow.Add(new SettingsEnumDropdown<DifficultyRating>
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
TooltipText = "Affects checks that depend on difficulty level",
Current = verify.InterpretedDifficulty.GetBoundCopy()
});
}
}
}

View File

@ -0,0 +1,115 @@
// 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;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks.Components;
using osuTK;
namespace osu.Game.Screens.Edit.Verify
{
[Cached]
public class IssueList : CompositeDrawable
{
private IssueTable table;
[Resolved]
private EditorClock clock { get; set; }
[Resolved]
private IBindable<WorkingBeatmap> workingBeatmap { get; set; }
[Resolved]
private EditorBeatmap beatmap { get; set; }
[Resolved]
private VerifyScreen verify { get; set; }
private IBeatmapVerifier rulesetVerifier;
private BeatmapVerifier generalVerifier;
private BeatmapVerifierContext context;
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colours)
{
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[]
{
new Box
{
Colour = colours.Background2,
RelativeSizeAxes = Axes.Both,
},
new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = table = new IssueTable(),
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding(20),
Children = new Drawable[]
{
new TriangleButton
{
Text = "Refresh",
Action = refresh,
Size = new Vector2(120, 40),
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
},
}
},
};
}
protected override void LoadComplete()
{
base.LoadComplete();
verify.InterpretedDifficulty.BindValueChanged(_ => refresh());
verify.HiddenIssueTypes.BindCollectionChanged((_, __) => refresh());
refresh();
}
private void refresh()
{
var issues = generalVerifier.Run(context);
if (rulesetVerifier != null)
issues = issues.Concat(rulesetVerifier.Run(context));
issues = filter(issues);
table.Issues = issues
.OrderBy(issue => issue.Template.Type)
.ThenBy(issue => issue.Check.Metadata.Category);
}
private IEnumerable<Issue> filter(IEnumerable<Issue> issues)
{
return issues.Where(issue => !verify.HiddenIssueTypes.Contains(issue.Template.Type));
}
}
}

View File

@ -2,45 +2,16 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
namespace osu.Game.Screens.Edit.Verify namespace osu.Game.Screens.Edit.Verify
{ {
public class IssueSettings : CompositeDrawable public class IssueSettings : EditorRoundedScreenSettings
{ {
[BackgroundDependencyLoader] protected override IReadOnlyList<Drawable> CreateSections() => new Drawable[]
private void load(OsuColour colours)
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new Box
{
Colour = colours.Gray3,
RelativeSizeAxes = Axes.Both,
},
new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = createSections()
},
}
};
}
private IReadOnlyList<Drawable> createSections() => new Drawable[]
{ {
new InterpretationSection(),
new VisibilitySection()
}; };
} }
} }

View File

@ -18,7 +18,9 @@ namespace osu.Game.Screens.Edit.Verify
public class IssueTable : EditorTable public class IssueTable : EditorTable
{ {
[Resolved] [Resolved]
private Bindable<Issue> selectedIssue { get; set; } private VerifyScreen verify { get; set; }
private Bindable<Issue> selectedIssue;
[Resolved] [Resolved]
private EditorClock clock { get; set; } private EditorClock clock { get; set; }
@ -71,6 +73,7 @@ namespace osu.Game.Screens.Edit.Verify
{ {
base.LoadComplete(); base.LoadComplete();
selectedIssue = verify.SelectedIssue.GetBoundCopy();
selectedIssue.BindValueChanged(issue => selectedIssue.BindValueChanged(issue =>
{ {
foreach (var b in BackgroundFlow) b.Selected = b.Item == issue.NewValue; foreach (var b in BackgroundFlow) b.Selected = b.Item == issue.NewValue;

View File

@ -1,26 +1,25 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Edit.Checks.Components;
using osuTK;
namespace osu.Game.Screens.Edit.Verify namespace osu.Game.Screens.Edit.Verify
{ {
public class VerifyScreen : RoundedContentEditorScreen [Cached]
public class VerifyScreen : EditorRoundedScreen
{ {
[Cached] public readonly Bindable<Issue> SelectedIssue = new Bindable<Issue>();
private Bindable<Issue> selectedIssue = new Bindable<Issue>();
public readonly Bindable<DifficultyRating> InterpretedDifficulty = new Bindable<DifficultyRating>();
public readonly BindableList<IssueType> HiddenIssueTypes = new BindableList<IssueType> { IssueType.Negligible };
public IssueList IssueList { get; private set; }
public VerifyScreen() public VerifyScreen()
: base(EditorScreenMode.Verify) : base(EditorScreenMode.Verify)
@ -30,6 +29,10 @@ namespace osu.Game.Screens.Edit.Verify
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
InterpretedDifficulty.Default = EditorBeatmap.BeatmapInfo.DifficultyRating;
InterpretedDifficulty.SetDefault();
IssueList = new IssueList();
Child = new Container Child = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -45,92 +48,12 @@ namespace osu.Game.Screens.Edit.Verify
{ {
new Drawable[] new Drawable[]
{ {
new IssueList(), IssueList,
new IssueSettings(), new IssueSettings(),
}, },
} }
} }
}; };
} }
public class IssueList : CompositeDrawable
{
private IssueTable table;
[Resolved]
private EditorClock clock { get; set; }
[Resolved]
private IBindable<WorkingBeatmap> workingBeatmap { get; set; }
[Resolved]
private EditorBeatmap beatmap { get; set; }
[Resolved]
private Bindable<Issue> selectedIssue { get; set; }
private IBeatmapVerifier rulesetVerifier;
private BeatmapVerifier generalVerifier;
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colours)
{
generalVerifier = new BeatmapVerifier();
rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier();
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new Box
{
Colour = colours.Background2,
RelativeSizeAxes = Axes.Both,
},
new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = table = new IssueTable(),
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding(20),
Children = new Drawable[]
{
new TriangleButton
{
Text = "Refresh",
Action = refresh,
Size = new Vector2(120, 40),
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
},
}
},
};
}
protected override void LoadComplete()
{
base.LoadComplete();
refresh();
}
private void refresh()
{
var issues = generalVerifier.Run(beatmap, workingBeatmap.Value);
if (rulesetVerifier != null)
issues = issues.Concat(rulesetVerifier.Run(beatmap, workingBeatmap.Value));
table.Issues = issues
.OrderBy(issue => issue.Template.Type)
.ThenBy(issue => issue.Check.Metadata.Category);
}
}
} }
} }

View File

@ -0,0 +1,54 @@
// 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.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Edit.Checks.Components;
namespace osu.Game.Screens.Edit.Verify
{
internal class VisibilitySection : EditorRoundedScreenSettingsSection
{
private readonly IssueType[] configurableIssueTypes =
{
IssueType.Warning,
IssueType.Error,
IssueType.Negligible
};
private BindableList<IssueType> hiddenIssueTypes;
protected override string HeaderText => "Visibility";
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colours, VerifyScreen verify)
{
hiddenIssueTypes = verify.HiddenIssueTypes.GetBoundCopy();
foreach (IssueType issueType in configurableIssueTypes)
{
var checkbox = new SettingsCheckbox
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
LabelText = issueType.ToString(),
Current = { Default = !hiddenIssueTypes.Contains(issueType) }
};
checkbox.Current.SetDefault();
checkbox.Current.BindValueChanged(state =>
{
if (!state.NewValue)
hiddenIssueTypes.Add(issueType);
else
hiddenIssueTypes.Remove(issueType);
});
Flow.Add(checkbox);
}
}
}
}

View File

@ -108,7 +108,7 @@ namespace osu.Game.Screens.OnlinePlay
difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) }; difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) };
beatmapText.Clear(); beatmapText.Clear();
beatmapText.AddLink(Item.Beatmap.ToString(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); beatmapText.AddLink(Item.Beatmap.Value.ToRomanisableString(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString());
authorText.Clear(); authorText.Clear();

View File

@ -150,7 +150,11 @@ namespace osu.Game.Screens.OnlinePlay.Match
protected void StartPlay() protected void StartPlay()
{ {
sampleStart?.Play(); sampleStart?.Play();
ParentScreen?.Push(CreateGameplayScreen());
// fallback is to allow this class to operate when there is no parent OnlineScreen (testing purposes).
var targetScreen = (Screen)ParentScreen ?? this;
targetScreen.Push(CreateGameplayScreen());
} }
/// <summary> /// <summary>

View File

@ -27,6 +27,7 @@ using osu.Game.Screens.OnlinePlay.Match.Components;
using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
using osu.Game.Screens.OnlinePlay.Multiplayer.Participants; using osu.Game.Screens.OnlinePlay.Multiplayer.Participants;
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
using osu.Game.Users; using osu.Game.Users;
using osuTK; using osuTK;
@ -452,7 +453,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
return new MultiSpectatorScreen(userIds); return new MultiSpectatorScreen(userIds);
default: default:
return new MultiplayerPlayer(SelectedItem.Value, userIds); return new PlayerLoader(() => new MultiplayerPlayer(SelectedItem.Value, userIds));
} }
} }

Some files were not shown because too many files have changed in this diff Show More