diff --git a/osu.Android.props b/osu.Android.props
index 752eb160ed..f7b7b6fb23 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,7 +51,7 @@
-
+
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs
new file mode 100644
index 0000000000..559d612037
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs
@@ -0,0 +1,185 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Utils;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Screens.Edit;
+using osuTK;
+using osuTK.Input;
+
+namespace osu.Game.Rulesets.Osu.Tests.Editor
+{
+ public class TestSceneSliderStreamConversion : TestSceneOsuEditor
+ {
+ private BindableBeatDivisor beatDivisor => (BindableBeatDivisor)Editor.Dependencies.Get(typeof(BindableBeatDivisor));
+
+ [Test]
+ public void TestSimpleConversion()
+ {
+ Slider slider = null;
+
+ AddStep("select first slider", () =>
+ {
+ slider = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider);
+ EditorClock.Seek(slider.StartTime);
+ EditorBeatmap.SelectedHitObjects.Add(slider);
+ });
+
+ convertToStream();
+
+ AddAssert("stream created", () => streamCreatedFor(slider,
+ (time: 0, pathPosition: 0),
+ (time: 0.25, pathPosition: 0.25),
+ (time: 0.5, pathPosition: 0.5),
+ (time: 0.75, pathPosition: 0.75),
+ (time: 1, pathPosition: 1)));
+
+ AddStep("undo", () => Editor.Undo());
+ AddAssert("slider restored", () => sliderRestored(slider));
+
+ AddStep("select first slider", () =>
+ {
+ slider = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider);
+ EditorClock.Seek(slider.StartTime);
+ EditorBeatmap.SelectedHitObjects.Add(slider);
+ });
+ AddStep("change beat divisor", () => beatDivisor.Value = 8);
+
+ convertToStream();
+ AddAssert("stream created", () => streamCreatedFor(slider,
+ (time: 0, pathPosition: 0),
+ (time: 0.125, pathPosition: 0.125),
+ (time: 0.25, pathPosition: 0.25),
+ (time: 0.375, pathPosition: 0.375),
+ (time: 0.5, pathPosition: 0.5),
+ (time: 0.625, pathPosition: 0.625),
+ (time: 0.75, pathPosition: 0.75),
+ (time: 0.875, pathPosition: 0.875),
+ (time: 1, pathPosition: 1)));
+ }
+
+ [Test]
+ public void TestConversionWithNonMatchingDivisor()
+ {
+ Slider slider = null;
+
+ AddStep("select second slider", () =>
+ {
+ slider = (Slider)EditorBeatmap.HitObjects.Where(h => h is Slider).ElementAt(1);
+ EditorClock.Seek(slider.StartTime);
+ EditorBeatmap.SelectedHitObjects.Add(slider);
+ });
+ AddStep("change beat divisor", () => beatDivisor.Value = 3);
+
+ convertToStream();
+
+ AddAssert("stream created", () => streamCreatedFor(slider,
+ (time: 0, pathPosition: 0),
+ (time: 2 / 3d, pathPosition: 2 / 3d)));
+ }
+
+ [Test]
+ public void TestConversionWithRepeats()
+ {
+ Slider slider = null;
+
+ AddStep("select first slider with repeats", () =>
+ {
+ slider = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider s && s.RepeatCount > 0);
+ EditorClock.Seek(slider.StartTime);
+ EditorBeatmap.SelectedHitObjects.Add(slider);
+ });
+ AddStep("change beat divisor", () => beatDivisor.Value = 2);
+
+ convertToStream();
+
+ AddAssert("stream created", () => streamCreatedFor(slider,
+ (time: 0, pathPosition: 0),
+ (time: 0.25, pathPosition: 0.5),
+ (time: 0.5, pathPosition: 1),
+ (time: 0.75, pathPosition: 0.5),
+ (time: 1, pathPosition: 0)));
+ }
+
+ [Test]
+ public void TestConversionPreservesSliderProperties()
+ {
+ Slider slider = null;
+
+ AddStep("select second new-combo-starting slider", () =>
+ {
+ slider = (Slider)EditorBeatmap.HitObjects.Where(h => h is Slider s && s.NewCombo).ElementAt(1);
+ EditorClock.Seek(slider.StartTime);
+ EditorBeatmap.SelectedHitObjects.Add(slider);
+ });
+
+ convertToStream();
+
+ AddAssert("stream created", () => streamCreatedFor(slider,
+ (time: 0, pathPosition: 0),
+ (time: 0.25, pathPosition: 0.25),
+ (time: 0.5, pathPosition: 0.5),
+ (time: 0.75, pathPosition: 0.75),
+ (time: 1, pathPosition: 1)));
+
+ AddStep("undo", () => Editor.Undo());
+ AddAssert("slider restored", () => sliderRestored(slider));
+ }
+
+ private void convertToStream()
+ {
+ AddStep("convert to stream", () =>
+ {
+ InputManager.PressKey(Key.LControl);
+ InputManager.PressKey(Key.LShift);
+ InputManager.Key(Key.F);
+ InputManager.ReleaseKey(Key.LShift);
+ InputManager.ReleaseKey(Key.LControl);
+ });
+ }
+
+ private bool streamCreatedFor(Slider slider, params (double time, double pathPosition)[] expectedCircles)
+ {
+ if (EditorBeatmap.HitObjects.Contains(slider))
+ return false;
+
+ foreach ((double expectedTime, double expectedPathPosition) in expectedCircles)
+ {
+ double time = slider.StartTime + slider.Duration * expectedTime;
+ Vector2 position = slider.Position + slider.Path.PositionAt(expectedPathPosition);
+
+ if (!EditorBeatmap.HitObjects.OfType().Any(h => matches(h, time, position, slider.NewCombo && expectedTime == 0)))
+ return false;
+ }
+
+ return true;
+
+ bool matches(HitCircle circle, double time, Vector2 position, bool startsNewCombo) =>
+ Precision.AlmostEquals(circle.StartTime, time, 1)
+ && Precision.AlmostEquals(circle.Position, position, 0.01f)
+ && circle.NewCombo == startsNewCombo
+ && circle.Samples.SequenceEqual(slider.HeadCircle.Samples)
+ && circle.SampleControlPoint.IsRedundant(slider.SampleControlPoint);
+ }
+
+ private bool sliderRestored(Slider slider)
+ {
+ var objects = EditorBeatmap.HitObjects.Where(h => h.StartTime >= slider.StartTime && h.GetEndTime() <= slider.EndTime).ToList();
+
+ if (objects.Count > 1)
+ return false;
+
+ var hitObject = objects.Single();
+ if (!(hitObject is Slider restoredSlider))
+ return false;
+
+ return Precision.AlmostEquals(slider.StartTime, restoredSlider.StartTime)
+ && Precision.AlmostEquals(slider.GetEndTime(), restoredSlider.GetEndTime())
+ && Precision.AlmostEquals(slider.Position, restoredSlider.Position, 0.01f)
+ && Precision.AlmostEquals(slider.EndPosition, restoredSlider.EndPosition, 0.01f);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
index b0e173e752..9148f0715c 100644
--- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
@@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
- [TestCase(6.6975550434910005d, "diffcalc-test")]
- [TestCase(1.4670676815251105d, "zero-length-sliders")]
+ [TestCase(6.6972307565739273d, "diffcalc-test")]
+ [TestCase(1.4484754139145539d, "zero-length-sliders")]
public void Test(double expected, string name)
=> base.Test(expected, name);
- [TestCase(8.9389769779826267d, "diffcalc-test")]
- [TestCase(1.7786917985891204d, "zero-length-sliders")]
+ [TestCase(8.9382559208689809d, "diffcalc-test")]
+ [TestCase(1.7548875851757628d, "zero-length-sliders")]
public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new OsuModDoubleTime());
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
index a08fe3b7c5..e0a216c8e0 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
public double AimStrain { get; set; }
public double SpeedStrain { get; set; }
public double FlashlightRating { get; set; }
+ public double SliderFactor { get; set; }
public double ApproachRate { get; set; }
public double OverallDifficulty { get; set; }
public double DrainRate { get; set; }
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index b0a764dc4d..558ddc16ef 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -34,8 +34,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return new OsuDifficultyAttributes { Mods = mods, Skills = skills };
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
- double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
- double flashlightRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier;
+ double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
+ double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier;
+ double flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;
+
+ double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
if (mods.Any(h => h is OsuModRelax))
speedRating = 0.0;
@@ -74,6 +77,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
AimStrain = aimRating,
SpeedStrain = speedRating,
FlashlightRating = flashlightRating,
+ SliderFactor = sliderFactor,
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
OverallDifficulty = (80 - hitWindowGreat) / 6,
DrainRate = drainRate,
@@ -108,7 +112,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return new Skill[]
{
- new Aim(mods),
+ new Aim(mods, true),
+ new Aim(mods, false),
new Speed(mods, hitWindowGreat),
new Flashlight(mods)
};
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index a9d9ff6985..8d45c7a8cc 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -125,6 +125,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty
aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate);
}
+ // We assume 15% of sliders in a map are difficult since there's no way to tell from the performance calculator.
+ double estimateDifficultSliders = Attributes.SliderCount * 0.15;
+
+ if (Attributes.SliderCount > 0)
+ {
+ double estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, Attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders);
+ double sliderNerfFactor = (1 - Attributes.SliderFactor) * Math.Pow(1 - estimateSliderEndsDropped / estimateDifficultSliders, 3) + Attributes.SliderFactor;
+ aimValue *= sliderNerfFactor;
+ }
+
aimValue *= accuracy;
// It is important to also consider accuracy difficulty when doing that.
aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
index cbaad93bed..d073d751d0 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
private const int normalized_radius = 50; // Change radius to 50 to make 100 the diameter. Easier for mental maths.
private const int min_delta_time = 25;
private const float maximum_slider_radius = normalized_radius * 2.4f;
- private const float assumed_slider_radius = normalized_radius * 1.65f;
+ private const float assumed_slider_radius = normalized_radius * 1.8f;
protected new OsuHitObject BaseObject => (OsuHitObject)base.BaseObject;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
index 4c40a49396..2a8d2ce759 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
@@ -14,11 +14,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
public class Aim : OsuStrainSkill
{
- public Aim(Mod[] mods)
+ public Aim(Mod[] mods, bool withSliders)
: base(mods)
{
+ this.withSliders = withSliders;
}
+ private readonly bool withSliders;
+
protected override int HistoryLength => 2;
private const double wide_angle_multiplier = 1.5;
@@ -44,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
double currVelocity = osuCurrObj.JumpDistance / osuCurrObj.StrainTime;
// But if the last object is a slider, then we extend the travel velocity through the slider into the current object.
- if (osuLastObj.BaseObject is Slider)
+ if (osuLastObj.BaseObject is Slider && withSliders)
{
double movementVelocity = osuCurrObj.MovementDistance / osuCurrObj.MovementTime; // calculate the movement velocity from slider end to current object
double travelVelocity = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; // calculate the slider velocity from slider head to slider end.
@@ -55,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
// As above, do the same for the previous hitobject.
double prevVelocity = osuLastObj.JumpDistance / osuLastObj.StrainTime;
- if (osuLastLastObj.BaseObject is Slider)
+ if (osuLastLastObj.BaseObject is Slider && withSliders)
{
double movementVelocity = osuLastObj.MovementDistance / osuLastObj.MovementTime;
double travelVelocity = osuLastObj.TravelDistance / osuLastObj.TravelTime;
@@ -135,7 +138,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
aimStrain += Math.Max(acuteAngleBonus * acute_angle_multiplier, wideAngleBonus * wide_angle_multiplier + velocityChangeBonus * velocity_change_multiplier);
// Add in additional slider velocity bonus.
- aimStrain += sliderBonus * slider_multiplier;
+ if (withSliders)
+ aimStrain += sliderBonus * slider_multiplier;
return aimStrain;
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
index a7fadfb67f..17a62fc61c 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
@@ -11,9 +11,12 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
+using osu.Framework.Utils;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
@@ -47,6 +50,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
[Resolved(CanBeNull = true)]
private IEditorChangeHandler changeHandler { get; set; }
+ [Resolved(CanBeNull = true)]
+ private BindableBeatDivisor beatDivisor { get; set; }
+
public override Quad SelectionQuad => BodyPiece.ScreenSpaceDrawQuad;
private readonly BindableList controlPoints = new BindableList();
@@ -173,6 +179,20 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
}
}
+ protected override bool OnKeyDown(KeyDownEvent e)
+ {
+ if (!IsSelected)
+ return false;
+
+ if (e.Key == Key.F && e.ControlPressed && e.ShiftPressed)
+ {
+ convertToStream();
+ return true;
+ }
+
+ return false;
+ }
+
private int addControlPoint(Vector2 position)
{
position -= HitObject.Position;
@@ -234,9 +254,56 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
editorBeatmap?.Update(HitObject);
}
+ private void convertToStream()
+ {
+ if (editorBeatmap == null || changeHandler == null || beatDivisor == null)
+ return;
+
+ var timingPoint = editorBeatmap.ControlPointInfo.TimingPointAt(HitObject.StartTime);
+ double streamSpacing = timingPoint.BeatLength / beatDivisor.Value;
+
+ changeHandler.BeginChange();
+
+ int i = 0;
+ double time = HitObject.StartTime;
+
+ while (!Precision.DefinitelyBigger(time, HitObject.GetEndTime(), 1))
+ {
+ // positionWithRepeats is a fractional number in the range of [0, HitObject.SpanCount()]
+ // and indicates how many fractional spans of a slider have passed up to time.
+ double positionWithRepeats = (time - HitObject.StartTime) / HitObject.Duration * HitObject.SpanCount();
+ double pathPosition = positionWithRepeats - (int)positionWithRepeats;
+ // every second span is in the reverse direction - need to reverse the path position.
+ if (Precision.AlmostBigger(positionWithRepeats % 2, 1))
+ pathPosition = 1 - pathPosition;
+
+ Vector2 position = HitObject.Position + HitObject.Path.PositionAt(pathPosition);
+
+ var samplePoint = (SampleControlPoint)HitObject.SampleControlPoint.DeepClone();
+ samplePoint.Time = time;
+
+ editorBeatmap.Add(new HitCircle
+ {
+ StartTime = time,
+ Position = position,
+ NewCombo = i == 0 && HitObject.NewCombo,
+ SampleControlPoint = samplePoint,
+ Samples = HitObject.HeadCircle.Samples.Select(s => s.With()).ToList()
+ });
+
+ i += 1;
+ time = HitObject.StartTime + i * streamSpacing;
+ }
+
+ editorBeatmap.Remove(HitObject);
+
+ changeHandler.EndChange();
+ }
+
public override MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)),
+ new OsuMenuItem("Convert to stream", MenuItemType.Destructive, convertToStream),
};
// Always refer to the drawable object's slider body so subsequent movement deltas are calculated with updated positions.
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 304a65e5c7..677aaf6f78 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -116,8 +116,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual("Insane", beatmapInfo.DifficultyName);
Assert.AreEqual(string.Empty, metadata.Source);
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", metadata.Tags);
- Assert.AreEqual(557821, beatmapInfo.OnlineBeatmapID);
- Assert.AreEqual(241526, beatmapInfo.BeatmapSet.OnlineBeatmapSetID);
+ Assert.AreEqual(557821, beatmapInfo.OnlineID);
+ Assert.AreEqual(241526, beatmapInfo.BeatmapSet.OnlineID);
}
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 37c1dfc657..bfd6ff0314 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
var beatmap = decodeAsJson(normal);
var meta = beatmap.BeatmapInfo.Metadata;
- Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID);
+ Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineID);
Assert.AreEqual("Soleily", meta.Artist);
Assert.AreEqual("Soleily", meta.ArtistUnicode);
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index 3093a5719d..e00d7a1115 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -18,6 +18,7 @@ using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.IO;
using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Scoring;
@@ -387,6 +388,41 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
+ [Test]
+ public async Task TestModelCreationFailureDoesntReturn()
+ {
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
+ {
+ try
+ {
+ var osu = LoadOsuIntoHost(host);
+ var importer = osu.Dependencies.Get();
+
+ var progressNotification = new ImportProgressNotification();
+
+ var zipStream = new MemoryStream();
+
+ using (var zip = ZipArchive.Create())
+ zip.SaveTo(zipStream, new ZipWriterOptions(CompressionType.Deflate));
+
+ var imported = await importer.Import(
+ progressNotification,
+ new ImportTask(zipStream, string.Empty)
+ );
+
+ checkBeatmapSetCount(osu, 0);
+ checkBeatmapCount(osu, 0);
+
+ Assert.IsEmpty(imported);
+ Assert.AreEqual(ProgressNotificationState.Cancelled, progressNotification.State);
+ }
+ finally
+ {
+ host.Exit();
+ }
+ }
+ }
+
[Test]
public async Task TestRollbackOnFailure()
{
@@ -506,7 +542,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var imported = await LoadOszIntoOsu(osu);
foreach (var b in imported.Beatmaps)
- b.OnlineBeatmapID = null;
+ b.OnlineID = null;
osu.Dependencies.Get().Update(imported);
@@ -545,19 +581,19 @@ namespace osu.Game.Tests.Beatmaps.IO
var toImport = new BeatmapSetInfo
{
- OnlineBeatmapSetID = 1,
+ OnlineID = 1,
Metadata = metadata,
Beatmaps = new List
{
new BeatmapInfo
{
- OnlineBeatmapID = 2,
+ OnlineID = 2,
Metadata = metadata,
BaseDifficulty = difficulty
},
new BeatmapInfo
{
- OnlineBeatmapID = 2,
+ OnlineID = 2,
Metadata = metadata,
Status = BeatmapSetOnlineStatus.Loved,
BaseDifficulty = difficulty
@@ -570,8 +606,8 @@ namespace osu.Game.Tests.Beatmaps.IO
var imported = await manager.Import(toImport);
Assert.NotNull(imported);
- Assert.AreEqual(null, imported.Value.Beatmaps[0].OnlineBeatmapID);
- Assert.AreEqual(null, imported.Value.Beatmaps[1].OnlineBeatmapID);
+ Assert.AreEqual(null, imported.Value.Beatmaps[0].OnlineID);
+ Assert.AreEqual(null, imported.Value.Beatmaps[1].OnlineID);
}
finally
{
@@ -1020,13 +1056,13 @@ namespace osu.Game.Tests.Beatmaps.IO
{
IEnumerable resultSets = null;
var store = osu.Dependencies.Get();
- waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any(),
+ waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.OnlineID == 241526)).Any(),
@"BeatmapSet did not import to the database in allocated time.", timeout);
// ensure we were stored to beatmap database backing...
Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1).");
- IEnumerable queryBeatmaps() => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0);
- IEnumerable queryBeatmapSets() => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526);
+ IEnumerable queryBeatmaps() => store.QueryBeatmaps(s => s.BeatmapSet.OnlineID == 241526 && s.BaseDifficultyID > 0);
+ IEnumerable queryBeatmapSets() => store.QueryBeatmapSets(s => s.OnlineID == 241526);
// if we don't re-check here, the set will be inserted but the beatmaps won't be present yet.
waitForOrAssert(() => queryBeatmaps().Count() == 12,
@@ -1042,7 +1078,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var set = queryBeatmapSets().First();
foreach (BeatmapInfo b in set.Beatmaps)
- Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineBeatmapID == b.OnlineBeatmapID));
+ Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineID == b.OnlineID));
Assert.IsTrue(set.Beatmaps.Count > 0);
var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Any() == true);
diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
index eaf5d107ca..b2ab1eeaa6 100644
--- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var meta = beatmap.Metadata;
- Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID);
+ Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineID);
Assert.AreEqual("Soleily", meta.Artist);
Assert.AreEqual("Soleily", meta.ArtistUnicode);
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
diff --git a/osu.Game.Tests/Database/BeatmapImporterTests.cs b/osu.Game.Tests/Database/BeatmapImporterTests.cs
index e1fe1e224e..88d16c8a36 100644
--- a/osu.Game.Tests/Database/BeatmapImporterTests.cs
+++ b/osu.Game.Tests/Database/BeatmapImporterTests.cs
@@ -16,6 +16,7 @@ using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.IO.Archives;
using osu.Game.Models;
+using osu.Game.Overlays.Notifications;
using osu.Game.Stores;
using osu.Game.Tests.Resources;
using Realms;
@@ -367,6 +368,34 @@ namespace osu.Game.Tests.Database
});
}
+ [Test]
+ public void TestModelCreationFailureDoesntReturn()
+ {
+ RunTestWithRealmAsync(async (realmFactory, storage) =>
+ {
+ using var importer = new BeatmapImporter(realmFactory, storage);
+ using var store = new RealmRulesetStore(realmFactory, storage);
+
+ var progressNotification = new ImportProgressNotification();
+
+ var zipStream = new MemoryStream();
+
+ using (var zip = ZipArchive.Create())
+ zip.SaveTo(zipStream, new ZipWriterOptions(CompressionType.Deflate));
+
+ var imported = await importer.Import(
+ progressNotification,
+ new ImportTask(zipStream, string.Empty)
+ );
+
+ checkBeatmapSetCount(realmFactory.Context, 0);
+ checkBeatmapCount(realmFactory.Context, 0);
+
+ Assert.IsEmpty(imported);
+ Assert.AreEqual(ProgressNotificationState.Cancelled, progressNotification.State);
+ });
+ }
+
[Test]
public void TestRollbackOnFailure()
{
diff --git a/osu.Game.Tests/Models/DisplayStringTest.cs b/osu.Game.Tests/Models/DisplayStringTest.cs
index cac5dd1aaa..95af21eb5f 100644
--- a/osu.Game.Tests/Models/DisplayStringTest.cs
+++ b/osu.Game.Tests/Models/DisplayStringTest.cs
@@ -10,19 +10,28 @@ using osu.Game.Rulesets;
using osu.Game.Scoring;
using osu.Game.Users;
+#nullable enable
+
namespace osu.Game.Tests.Models
{
[TestFixture]
public class DisplayStringTest
{
+ [Test]
+ public void TestNull()
+ {
+ IBeatmapSetInfo? beatmap = null;
+ Assert.That(beatmap.GetDisplayString(), Is.EqualTo("null"));
+ }
+
[Test]
public void TestBeatmapSet()
{
var mock = new Mock();
- mock.Setup(m => m.Metadata.Artist).Returns("artist");
- mock.Setup(m => m.Metadata.Title).Returns("title");
- mock.Setup(m => m.Metadata.Author.Username).Returns("author");
+ mock.Setup(m => m.Metadata!.Artist).Returns("artist");
+ mock.Setup(m => m.Metadata!.Title).Returns("title");
+ mock.Setup(m => m.Metadata!.Author.Username).Returns("author");
Assert.That(mock.Object.GetDisplayString(), Is.EqualTo("artist - title (author)"));
}
@@ -32,9 +41,9 @@ namespace osu.Game.Tests.Models
{
var mock = new Mock();
- mock.Setup(m => m.Metadata.Artist).Returns("artist");
- mock.Setup(m => m.Metadata.Title).Returns("title");
- mock.Setup(m => m.Metadata.Author.Username).Returns(string.Empty);
+ mock.Setup(m => m.Metadata!.Artist).Returns("artist");
+ mock.Setup(m => m.Metadata!.Title).Returns("title");
+ mock.Setup(m => m.Metadata!.Author.Username).Returns(string.Empty);
Assert.That(mock.Object.GetDisplayString(), Is.EqualTo("artist - title"));
}
diff --git a/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs b/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs
index 9ce7e0a0e0..938edf07c6 100644
--- a/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs
+++ b/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs
@@ -12,8 +12,8 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestOnlineWithOnline()
{
- var ourInfo = new BeatmapSetInfo { OnlineBeatmapSetID = 123 };
- var otherInfo = new BeatmapSetInfo { OnlineBeatmapSetID = 123 };
+ var ourInfo = new BeatmapSetInfo { OnlineID = 123 };
+ var otherInfo = new BeatmapSetInfo { OnlineID = 123 };
Assert.AreEqual(ourInfo, otherInfo);
}
@@ -30,8 +30,8 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestDatabasedWithOnline()
{
- var ourInfo = new BeatmapSetInfo { ID = 123, OnlineBeatmapSetID = 12 };
- var otherInfo = new BeatmapSetInfo { OnlineBeatmapSetID = 12 };
+ var ourInfo = new BeatmapSetInfo { ID = 123, OnlineID = 12 };
+ var otherInfo = new BeatmapSetInfo { OnlineID = 12 };
Assert.AreEqual(ourInfo, otherInfo);
}
diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs
index ec97948532..ee1feeca8d 100644
--- a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs
+++ b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs
@@ -207,8 +207,8 @@ namespace osu.Game.Tests.NonVisual.Filtering
public void TestCriteriaMatchingBeatmapIDs(string query, bool filtered)
{
var beatmap = getExampleBeatmap();
- beatmap.OnlineBeatmapID = 20201010;
- beatmap.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = 1535 };
+ beatmap.OnlineID = 20201010;
+ beatmap.BeatmapSet = new BeatmapSetInfo { OnlineID = 1535 };
var criteria = new FilterCriteria { SearchText = query };
var carouselItem = new CarouselBeatmap(beatmap);
diff --git a/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs b/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs
index ad6f01881b..103831822c 100644
--- a/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs
+++ b/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs
@@ -22,7 +22,8 @@ namespace osu.Game.Tests.NonVisual.Ranking
var unstableRate = new UnstableRate(events);
- Assert.IsTrue(Precision.AlmostEquals(unstableRate.Value, 10 * Math.Sqrt(10)));
+ Assert.IsNotNull(unstableRate.Value);
+ Assert.IsTrue(Precision.AlmostEquals(unstableRate.Value.Value, 10 * Math.Sqrt(10)));
}
[Test]
diff --git a/osu.Game.Tests/Online/TestSceneBeatmapManager.cs b/osu.Game.Tests/Online/TestSceneBeatmapManager.cs
index 4d5bee13f2..fc1b4f224d 100644
--- a/osu.Game.Tests/Online/TestSceneBeatmapManager.cs
+++ b/osu.Game.Tests/Online/TestSceneBeatmapManager.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Online
private static readonly BeatmapSetInfo test_db_model = new BeatmapSetInfo
{
- OnlineBeatmapSetID = 1,
+ OnlineID = 1,
Metadata = new BeatmapMetadata
{
Artist = "test author",
diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs
index e3e2304990..b66da028f1 100644
--- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs
+++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs
@@ -58,7 +58,7 @@ namespace osu.Game.Tests.Online
testBeatmapInfo = getTestBeatmapInfo(testBeatmapFile);
testBeatmapSet = testBeatmapInfo.BeatmapSet;
- var existing = beatmaps.QueryBeatmapSet(s => s.OnlineBeatmapSetID == testBeatmapSet.OnlineBeatmapSetID);
+ var existing = beatmaps.QueryBeatmapSet(s => s.OnlineID == testBeatmapSet.OnlineID);
if (existing != null)
beatmaps.Delete(existing);
@@ -101,10 +101,10 @@ namespace osu.Game.Tests.Online
AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).Wait());
addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable);
- AddStep("delete beatmap", () => beatmaps.Delete(beatmaps.QueryBeatmapSet(b => b.OnlineBeatmapSetID == testBeatmapSet.OnlineBeatmapSetID)));
+ AddStep("delete beatmap", () => beatmaps.Delete(beatmaps.QueryBeatmapSet(b => b.OnlineID == testBeatmapSet.OnlineID)));
addAvailabilityCheckStep("state not downloaded", BeatmapAvailability.NotDownloaded);
- AddStep("undelete beatmap", () => beatmaps.Undelete(beatmaps.QueryBeatmapSet(b => b.OnlineBeatmapSetID == testBeatmapSet.OnlineBeatmapSetID)));
+ AddStep("undelete beatmap", () => beatmaps.Undelete(beatmaps.QueryBeatmapSet(b => b.OnlineID == testBeatmapSet.OnlineID)));
addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable);
}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs
new file mode 100644
index 0000000000..db42667033
--- /dev/null
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs
@@ -0,0 +1,123 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Screens;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Screens.Edit;
+using osu.Game.Screens.Edit.Components.Timelines.Summary;
+using osu.Game.Tests.Beatmaps.IO;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Visual.Editing
+{
+ public class TestSceneEditorTestGameplay : EditorTestScene
+ {
+ protected override bool IsolateSavingFromDatabase => false;
+
+ protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
+
+ [Resolved]
+ private OsuGameBase game { get; set; }
+
+ [Resolved]
+ private BeatmapManager beatmaps { get; set; }
+
+ private BeatmapSetInfo importedBeatmapSet;
+
+ public override void SetUpSteps()
+ {
+ AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game).Result);
+ base.SetUpSteps();
+ }
+
+ protected override void LoadEditor()
+ {
+ Beatmap.Value = beatmaps.GetWorkingBeatmap(importedBeatmapSet.Beatmaps.First(b => b.RulesetID == 0));
+ base.LoadEditor();
+ }
+
+ [Test]
+ public void TestBasicGameplayTest()
+ {
+ AddStep("click test gameplay button", () =>
+ {
+ var button = Editor.ChildrenOfType().Single();
+
+ InputManager.MoveMouseTo(button);
+ InputManager.Click(MouseButton.Left);
+ });
+
+ EditorPlayer editorPlayer = null;
+ AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
+ AddStep("exit player", () => editorPlayer.Exit());
+ AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
+ }
+
+ [Test]
+ public void TestCancelGameplayTestWithUnsavedChanges()
+ {
+ AddStep("delete all but first object", () => EditorBeatmap.RemoveRange(EditorBeatmap.HitObjects.Skip(1).ToList()));
+
+ AddStep("click test gameplay button", () =>
+ {
+ var button = Editor.ChildrenOfType().Single();
+
+ InputManager.MoveMouseTo(button);
+ InputManager.Click(MouseButton.Left);
+ });
+ AddUntilStep("save prompt shown", () => DialogOverlay.CurrentDialog is SaveBeforeGameplayTestDialog);
+
+ AddStep("dismiss prompt", () =>
+ {
+ var button = DialogOverlay.CurrentDialog.Buttons.Last();
+ InputManager.MoveMouseTo(button);
+ InputManager.Click(MouseButton.Left);
+ });
+
+ AddWaitStep("wait some", 3);
+ AddAssert("stayed in editor", () => Stack.CurrentScreen is Editor);
+ }
+
+ [Test]
+ public void TestSaveChangesBeforeGameplayTest()
+ {
+ AddStep("delete all but first object", () => EditorBeatmap.RemoveRange(EditorBeatmap.HitObjects.Skip(1).ToList()));
+ // bit of a hack to ensure this test can be ran multiple times without running into UNIQUE constraint failures
+ AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = Guid.NewGuid().ToString());
+
+ AddStep("click test gameplay button", () =>
+ {
+ var button = Editor.ChildrenOfType().Single();
+
+ InputManager.MoveMouseTo(button);
+ InputManager.Click(MouseButton.Left);
+ });
+ AddUntilStep("save prompt shown", () => DialogOverlay.CurrentDialog is SaveBeforeGameplayTestDialog);
+
+ AddStep("save changes", () => DialogOverlay.CurrentDialog.PerformOkAction());
+
+ EditorPlayer editorPlayer = null;
+ AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
+ AddAssert("beatmap has 1 object", () => editorPlayer.Beatmap.Value.Beatmap.HitObjects.Count == 1);
+
+ AddUntilStep("wait for return to editor", () => Stack.CurrentScreen is Editor);
+ AddAssert("track stopped", () => !Beatmap.Value.Track.IsRunning);
+ }
+
+ public override void TearDownSteps()
+ {
+ base.TearDownSteps();
+ AddStep("delete imported", () =>
+ {
+ beatmaps.Delete(importedBeatmapSet);
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs
index c3c803ff23..03e78ce854 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs
@@ -4,6 +4,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
+using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Edit;
@@ -23,6 +24,9 @@ namespace osu.Game.Tests.Visual.Editing
[Cached(typeof(IBeatSnapProvider))]
private readonly EditorBeatmap editorBeatmap;
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
+
public TestSceneSetupScreen()
{
editorBeatmap = new EditorBeatmap(new OsuBeatmap());
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs
index f961fff1e5..4bbffbdc7a 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs
@@ -4,6 +4,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
+using osu.Game.Overlays;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit;
@@ -18,6 +19,9 @@ namespace osu.Game.Tests.Visual.Editing
[Cached(typeof(IBeatSnapProvider))]
private readonly EditorBeatmap editorBeatmap;
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
+
protected override bool ScrollUsingMouseWheel => false;
public TestSceneTimingScreen()
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs
index 4c48d52acd..84c7f611af 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs
@@ -1,9 +1,10 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.Diagnostics;
using NUnit.Framework;
-using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Rulesets;
@@ -20,16 +21,17 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public class TestScenePerformancePointsCounter : OsuTestScene
{
- [Cached]
- private GameplayState gameplayState;
+ private DependencyProvidingContainer dependencyContainer;
- [Cached]
+ private GameplayState gameplayState;
private ScoreProcessor scoreProcessor;
private int iteration;
+ private Bindable lastJudgementResult = new Bindable();
private PerformancePointsCounter counter;
- public TestScenePerformancePointsCounter()
+ [SetUpSteps]
+ public void SetUpSteps() => AddStep("create components", () =>
{
var ruleset = CreateRuleset();
@@ -38,32 +40,43 @@ namespace osu.Game.Tests.Visual.Gameplay
var beatmap = CreateWorkingBeatmap(ruleset.RulesetInfo)
.GetPlayableBeatmap(ruleset.RulesetInfo);
+ lastJudgementResult = new Bindable();
+
gameplayState = new GameplayState(beatmap, ruleset);
+ gameplayState.LastJudgementResult.BindTo(lastJudgementResult);
+
scoreProcessor = new ScoreProcessor();
- }
+
+ Child = dependencyContainer = new DependencyProvidingContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ CachedDependencies = new (Type, object)[]
+ {
+ (typeof(GameplayState), gameplayState),
+ (typeof(ScoreProcessor), scoreProcessor)
+ }
+ };
+
+ iteration = 0;
+ });
protected override Ruleset CreateRuleset() => new OsuRuleset();
- [SetUpSteps]
- public void SetUpSteps()
+ private void createCounter() => AddStep("Create counter", () =>
{
- AddStep("Create counter", () =>
+ dependencyContainer.Child = counter = new PerformancePointsCounter
{
- iteration = 0;
-
- Child = counter = new PerformancePointsCounter
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Scale = new Vector2(5),
- };
- });
- }
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Scale = new Vector2(5),
+ };
+ });
[Test]
public void TestBasicCounting()
{
int previousValue = 0;
+ createCounter();
AddAssert("counter displaying zero", () => counter.Current.Value == 0);
@@ -86,6 +99,17 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("counter non-zero", () => counter.Current.Value > 0);
}
+ [Test]
+ public void TestCounterUpdatesWithJudgementsBeforeCreation()
+ {
+ AddRepeatStep("Add judgement", applyOneJudgement, 10);
+
+ createCounter();
+
+ AddUntilStep("counter non-zero", () => counter.Current.Value > 0);
+ AddUntilStep("counter opaque", () => counter.Child.Alpha == 1);
+ }
+
private void applyOneJudgement()
{
var scoreInfo = gameplayState.Score.ScoreInfo;
@@ -94,13 +118,14 @@ namespace osu.Game.Tests.Visual.Gameplay
scoreInfo.Accuracy = 1;
scoreInfo.Statistics[HitResult.Great] = iteration * 1000;
- scoreProcessor.ApplyResult(new OsuJudgementResult(new HitObject
+ lastJudgementResult.Value = new OsuJudgementResult(new HitObject
{
StartTime = iteration * 10000,
}, new OsuJudgement())
{
Type = HitResult.Perfect,
- });
+ };
+ scoreProcessor.ApplyResult(lastJudgementResult.Value);
iteration++;
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs
index bf864f844c..cb5058779c 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs
@@ -215,7 +215,7 @@ namespace osu.Game.Tests.Visual.Gameplay
createPlayerTest(false, r =>
{
var beatmap = createTestBeatmap(r);
- beatmap.BeatmapInfo.OnlineBeatmapID = null;
+ beatmap.BeatmapInfo.OnlineID = null;
return beatmap;
});
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
index 20e859dd2b..9fadbe02bd 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
@@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("import beatmap", () =>
{
importedBeatmap = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result;
- importedBeatmapId = importedBeatmap.Beatmaps.First(b => b.RulesetID == 0).OnlineBeatmapID ?? -1;
+ importedBeatmapId = importedBeatmap.Beatmaps.First(b => b.RulesetID == 0).OnlineID ?? -1;
});
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs
index 0dc25cf378..be20799479 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs
@@ -61,6 +61,16 @@ namespace osu.Game.Tests.Visual.Gameplay
AddRepeatStep("Bring UR to 100", () => applyJudgement(-10, false), 10);
}
+ [Test]
+ public void TestCounterReceivesJudgementsBeforeCreation()
+ {
+ AddRepeatStep("Set UR to 250", () => applyJudgement(25, true), 4);
+
+ AddStep("Create Display", recreateDisplay);
+
+ AddUntilStep("UR = 250", () => counter.Current.Value == 250.0);
+ }
+
private void recreateDisplay()
{
Clear();
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
index fdd01446b9..b10856b704 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
importedSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result;
importedBeatmap = importedSet.Beatmaps.First(b => b.RulesetID == 0);
- importedBeatmapId = importedBeatmap.OnlineBeatmapID ?? -1;
+ importedBeatmapId = importedBeatmap.OnlineID ?? -1;
}
[SetUp]
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs
index 902629765f..25200560e4 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
foreach (int user in users)
{
- SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0);
+ SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineID ?? 0);
multiplayerUsers.Add(OnlinePlayDependencies.Client.AddUser(new APIUser { Id = user }, true));
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs
index af4e696fce..16a342df8c 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs
@@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
foreach (int user in users)
{
- SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0);
+ SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineID ?? 0);
var roomUser = OnlinePlayDependencies.Client.AddUser(new APIUser { Id = user }, true);
roomUser.MatchState = new TeamVersusUserState
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
index 0d6b428cce..aef1fb31d6 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
beatmaps.Add(new BeatmapInfo
{
Ruleset = rulesets.GetRuleset(i % 4),
- OnlineBeatmapID = beatmapId,
+ OnlineID = beatmapId,
Length = length,
BPM = bpm,
BaseDifficulty = new BeatmapDifficulty()
@@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
manager.Import(new BeatmapSetInfo
{
- OnlineBeatmapSetID = 10,
+ OnlineID = 10,
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
index 5e55759e01..05f9a94cf7 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
@@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
beatmaps.Add(new BeatmapInfo
{
Ruleset = new OsuRuleset().RulesetInfo,
- OnlineBeatmapID = beatmapId,
+ OnlineID = beatmapId,
DifficultyName = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
Length = length,
BPM = bpm,
@@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
manager.Import(new BeatmapSetInfo
{
- OnlineBeatmapSetID = 10,
+ OnlineID = 10,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs
index 4ec76e1e4b..4e1b3bb9bf 100644
--- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs
@@ -174,7 +174,7 @@ namespace osu.Game.Tests.Visual.Navigation
{
AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait());
PushAndConfirm(() => new TestPlaySongSelect());
- AddUntilStep("beatmap updated", () => Game.Beatmap.Value.BeatmapSetInfo.OnlineBeatmapSetID == 241526);
+ AddUntilStep("beatmap updated", () => Game.Beatmap.Value.BeatmapSetInfo.OnlineID == 241526);
}
public class DialogBlockingScreen : OsuScreen
diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs
index 5f5ebfccfb..ff976c7bf6 100644
--- a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs
@@ -107,20 +107,20 @@ namespace osu.Game.Tests.Visual.Navigation
imported = Game.BeatmapManager.Import(new BeatmapSetInfo
{
Hash = Guid.NewGuid().ToString(),
- OnlineBeatmapSetID = i,
+ OnlineID = i,
Metadata = metadata,
Beatmaps = new List
{
new BeatmapInfo
{
- OnlineBeatmapID = i * 1024,
+ OnlineID = i * 1024,
Metadata = metadata,
BaseDifficulty = difficulty,
Ruleset = ruleset ?? new OsuRuleset().RulesetInfo
},
new BeatmapInfo
{
- OnlineBeatmapID = i * 2048,
+ OnlineID = i * 2048,
Metadata = metadata,
BaseDifficulty = difficulty,
Ruleset = ruleset ?? new OsuRuleset().RulesetInfo
@@ -145,11 +145,11 @@ namespace osu.Game.Tests.Visual.Navigation
private void presentSecondDifficultyAndConfirm(Func getImport, int importedID)
{
- Predicate pred = b => b.OnlineBeatmapID == importedID * 2048;
+ Predicate pred = b => b.OnlineID == importedID * 2048;
AddStep("present difficulty", () => Game.PresentBeatmap(getImport(), pred));
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect);
- AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineBeatmapID == importedID * 2048);
+ AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineID == importedID * 2048);
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID);
}
}
diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
index aca7ada535..72f160c9a9 100644
--- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
@@ -39,20 +39,20 @@ namespace osu.Game.Tests.Visual.Navigation
beatmap = Game.BeatmapManager.Import(new BeatmapSetInfo
{
Hash = Guid.NewGuid().ToString(),
- OnlineBeatmapSetID = 1,
+ OnlineID = 1,
Metadata = metadata,
Beatmaps = new List
{
new BeatmapInfo
{
- OnlineBeatmapID = 1 * 1024,
+ OnlineID = 1 * 1024,
Metadata = metadata,
BaseDifficulty = difficulty,
Ruleset = new OsuRuleset().RulesetInfo
},
new BeatmapInfo
{
- OnlineBeatmapID = 1 * 2048,
+ OnlineID = 1 * 2048,
Metadata = metadata,
BaseDifficulty = difficulty,
Ruleset = new OsuRuleset().RulesetInfo
diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs
index a865bbe950..22a91fa9a3 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("button state not downloaded", () => downloadButton.DownloadState == DownloadState.NotDownloaded);
AddStep("import soleily", () => beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()));
- AddUntilStep("wait for beatmap import", () => beatmaps.GetAllUsableBeatmapSets().Any(b => b.OnlineBeatmapSetID == 241526));
+ AddUntilStep("wait for beatmap import", () => beatmaps.GetAllUsableBeatmapSets().Any(b => b.OnlineID == 241526));
AddUntilStep("button state downloaded", () => downloadButton.DownloadState == DownloadState.LocallyAvailable);
createButtonWithBeatmap(createSoleily());
@@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep("remove soleily", () =>
{
- var beatmap = beatmaps.QueryBeatmapSet(b => b.OnlineBeatmapSetID == 241526);
+ var beatmap = beatmaps.QueryBeatmapSet(b => b.OnlineID == 241526);
if (beatmap != null) beatmaps.Delete(beatmap);
});
diff --git a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs
index 366fa8a4af..7a5ee84eb4 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs
@@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Online
AddStep("Set beatmap", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null)
{
- BeatmapInfo = { OnlineBeatmapID = hasOnlineId ? 1234 : (int?)null }
+ BeatmapInfo = { OnlineID = hasOnlineId ? 1234 : (int?)null }
});
AddStep("Run command", () => Add(new NowPlayingCommand()));
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
index cda7e95a46..c5287d4257 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
@@ -121,8 +121,8 @@ namespace osu.Game.Tests.Visual.Playlists
beatmap.BeatmapInfo.BaseDifficulty.CircleSize = 1;
// intentionally increment online IDs to clash with import below.
- beatmap.BeatmapInfo.OnlineBeatmapID++;
- beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID++;
+ beatmap.BeatmapInfo.OnlineID++;
+ beatmap.BeatmapInfo.BeatmapSet.OnlineID++;
importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result.Value;
});
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
index 8d5d0ba8c7..423c0a048c 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
@@ -337,7 +337,7 @@ namespace osu.Game.Tests.Visual.Ranking
public UnrankedSoloResultsScreen(ScoreInfo score)
: base(score, true)
{
- Score.BeatmapInfo.OnlineBeatmapID = 0;
+ Score.BeatmapInfo.OnlineID = 0;
Score.BeatmapInfo.Status = BeatmapSetOnlineStatus.Pending;
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
index 03079fdc5f..534442c8b6 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
@@ -838,7 +838,7 @@ namespace osu.Game.Tests.Visual.SongSelect
return new BeatmapSetInfo
{
ID = id,
- OnlineBeatmapSetID = id,
+ OnlineID = id,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
@@ -867,7 +867,7 @@ namespace osu.Game.Tests.Visual.SongSelect
yield return new BeatmapInfo
{
- OnlineBeatmapID = id++ * 10,
+ OnlineID = id++ * 10,
DifficultyName = version,
StarRating = diff,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -884,7 +884,7 @@ namespace osu.Game.Tests.Visual.SongSelect
var toReturn = new BeatmapSetInfo
{
ID = id,
- OnlineBeatmapSetID = id,
+ OnlineID = id,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
@@ -900,7 +900,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
toReturn.Beatmaps.Add(new BeatmapInfo
{
- OnlineBeatmapID = b * 10,
+ OnlineID = b * 10,
Path = $"extra{b}.osu",
DifficultyName = $"Extra {b}",
Ruleset = rulesets.GetRuleset((b - 1) % 4),
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
index 855a59b5f5..a8a7a5d350 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
@@ -388,7 +388,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
leaderboard.BeatmapInfo = new BeatmapInfo
{
- OnlineBeatmapID = 1113057,
+ OnlineID = 1113057,
Status = status,
};
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
index 57f2d436c5..a0742b862b 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
@@ -180,11 +180,11 @@ namespace osu.Game.Tests.Visual.SongSelect
var beatmapSet = new BeatmapSetInfo
{
Hash = Guid.NewGuid().ToString(),
- OnlineBeatmapSetID = importID,
+ OnlineID = importID,
Metadata = metadata,
Beatmaps = difficultyRulesets.Select((ruleset, difficultyIndex) => new BeatmapInfo
{
- OnlineBeatmapID = importID * 1024 + difficultyIndex,
+ OnlineID = importID * 1024 + difficultyIndex,
Metadata = metadata,
BaseDifficulty = new BeatmapDifficulty(),
Ruleset = ruleset,
@@ -205,8 +205,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect);
AddUntilStep("recommended beatmap displayed", () =>
{
- int? expectedID = getImport().Beatmaps[expectedDiff - 1].OnlineBeatmapID;
- return Game.Beatmap.Value.BeatmapInfo.OnlineBeatmapID == expectedID;
+ int? expectedID = getImport().Beatmaps[expectedDiff - 1].OnlineID;
+ return Game.Beatmap.Value.BeatmapInfo.OnlineID == expectedID;
});
}
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index a0d78fff58..ee5a61f21f 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -507,13 +507,13 @@ namespace osu.Game.Tests.Visual.SongSelect
i.IsFiltered || i.Item.BeatmapInfo.Ruleset.ID == targetRuleset || i.Item.BeatmapInfo.Ruleset.ID == 0);
});
- AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.OnlineBeatmapID == target.OnlineBeatmapID);
- AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
+ AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.OnlineID == target.OnlineID);
+ AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineID == target.OnlineID);
AddStep("reset filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = string.Empty);
- AddAssert("game still correct", () => Beatmap.Value?.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
- AddAssert("carousel still correct", () => songSelect.Carousel.SelectedBeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
+ AddAssert("game still correct", () => Beatmap.Value?.BeatmapInfo.OnlineID == target.OnlineID);
+ AddAssert("carousel still correct", () => songSelect.Carousel.SelectedBeatmapInfo.OnlineID == target.OnlineID);
}
[Test]
@@ -544,8 +544,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null);
- AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.OnlineBeatmapID == target.OnlineBeatmapID);
- AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
+ AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.OnlineID == target.OnlineID);
+ AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineID == target.OnlineID);
AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = "nononoo");
@@ -918,7 +918,7 @@ namespace osu.Game.Tests.Visual.SongSelect
beatmaps.Add(new BeatmapInfo
{
Ruleset = getRuleset(),
- OnlineBeatmapID = beatmapId,
+ OnlineID = beatmapId,
DifficultyName = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
Length = length,
BPM = bpm,
@@ -931,7 +931,7 @@ namespace osu.Game.Tests.Visual.SongSelect
return new BeatmapSetInfo
{
- OnlineBeatmapSetID = setId,
+ OnlineID = setId,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
index 035f438b89..7231409dc5 100644
--- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
+++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
@@ -290,7 +290,7 @@ namespace osu.Game.Beatmaps
catch (BeatmapInvalidForRulesetException e)
{
if (rulesetInfo.Equals(beatmapInfo.Ruleset))
- Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineBeatmapID} to the beatmap's default ruleset ({beatmapInfo.Ruleset}).");
+ Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineID} to the beatmap's default ruleset ({beatmapInfo.Ruleset}).");
return new StarDifficulty();
}
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 5bbd48f26d..602bb22c40 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -23,13 +23,14 @@ namespace osu.Game.Beatmaps
public int BeatmapVersion;
- private int? onlineBeatmapID;
+ private int? onlineID;
[JsonProperty("id")]
- public int? OnlineBeatmapID
+ [Column("OnlineBeatmapID")]
+ public int? OnlineID
{
- get => onlineBeatmapID;
- set => onlineBeatmapID = value > 0 ? value : null;
+ get => onlineID;
+ set => onlineID = value > 0 ? value : null;
}
[JsonIgnore]
@@ -176,7 +177,7 @@ namespace osu.Game.Beatmaps
#region Implementation of IHasOnlineID
- public int OnlineID => OnlineBeatmapID ?? -1;
+ int IHasOnlineID.OnlineID => OnlineID ?? -1;
#endregion
diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs
index ff4305dc91..3becbee0ba 100644
--- a/osu.Game/Beatmaps/BeatmapModelManager.cs
+++ b/osu.Game/Beatmaps/BeatmapModelManager.cs
@@ -94,17 +94,17 @@ namespace osu.Game.Beatmaps
validateOnlineIds(beatmapSet);
- bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0);
+ bool hadOnlineIDs = beatmapSet.Beatmaps.Any(b => b.OnlineID > 0);
if (OnlineLookupQueue != null)
await OnlineLookupQueue.UpdateAsync(beatmapSet, cancellationToken).ConfigureAwait(false);
// ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID.
- if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0))
+ if (hadOnlineIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineID > 0))
{
- if (beatmapSet.OnlineBeatmapSetID != null)
+ if (beatmapSet.OnlineID != null)
{
- beatmapSet.OnlineBeatmapSetID = null;
+ beatmapSet.OnlineID = null;
LogForModel(beatmapSet, "Disassociating beatmap set ID due to loss of all beatmap IDs");
}
}
@@ -116,27 +116,27 @@ namespace osu.Game.Beatmaps
throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}.");
// check if a set already exists with the same online id, delete if it does.
- if (beatmapSet.OnlineBeatmapSetID != null)
+ if (beatmapSet.OnlineID != null)
{
- var existingSetWithSameOnlineID = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID);
+ var existingSetWithSameOnlineID = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineID == beatmapSet.OnlineID);
if (existingSetWithSameOnlineID != null)
{
Delete(existingSetWithSameOnlineID);
// in order to avoid a unique key constraint, immediately remove the online ID from the previous set.
- existingSetWithSameOnlineID.OnlineBeatmapSetID = null;
+ existingSetWithSameOnlineID.OnlineID = null;
foreach (var b in existingSetWithSameOnlineID.Beatmaps)
- b.OnlineBeatmapID = null;
+ b.OnlineID = null;
- LogForModel(beatmapSet, $"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been deleted.");
+ LogForModel(beatmapSet, $"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineID}). It has been deleted.");
}
}
}
private void validateOnlineIds(BeatmapSetInfo beatmapSet)
{
- var beatmapIds = beatmapSet.Beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList();
+ var beatmapIds = beatmapSet.Beatmaps.Where(b => b.OnlineID.HasValue).Select(b => b.OnlineID).ToList();
// ensure all IDs are unique
if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1))
@@ -147,7 +147,7 @@ namespace osu.Game.Beatmaps
}
// find any existing beatmaps in the database that have matching online ids
- var existingBeatmaps = QueryBeatmaps(b => beatmapIds.Contains(b.OnlineBeatmapID)).ToList();
+ var existingBeatmaps = QueryBeatmaps(b => beatmapIds.Contains(b.OnlineID)).ToList();
if (existingBeatmaps.Count > 0)
{
@@ -162,7 +162,7 @@ namespace osu.Game.Beatmaps
}
}
- void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineBeatmapID = null);
+ void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineID = null);
}
///
@@ -242,7 +242,7 @@ namespace osu.Game.Beatmaps
if (!base.CanSkipImport(existing, import))
return false;
- return existing.Beatmaps.Any(b => b.OnlineBeatmapID != null);
+ return existing.Beatmaps.Any(b => b.OnlineID != null);
}
protected override bool CanReuseExisting(BeatmapSetInfo existing, BeatmapSetInfo import)
@@ -250,11 +250,11 @@ namespace osu.Game.Beatmaps
if (!base.CanReuseExisting(existing, import))
return false;
- var existingIds = existing.Beatmaps.Select(b => b.OnlineBeatmapID).OrderBy(i => i);
- var importIds = import.Beatmaps.Select(b => b.OnlineBeatmapID).OrderBy(i => i);
+ var existingIds = existing.Beatmaps.Select(b => b.OnlineID).OrderBy(i => i);
+ var importIds = import.Beatmaps.Select(b => b.OnlineID).OrderBy(i => i);
// force re-import if we are not in a sane state.
- return existing.OnlineBeatmapSetID == import.OnlineBeatmapSetID && existingIds.SequenceEqual(importIds);
+ return existing.OnlineID == import.OnlineID && existingIds.SequenceEqual(importIds);
}
///
@@ -349,7 +349,7 @@ namespace osu.Game.Beatmaps
protected override bool CheckLocalAvailability(BeatmapSetInfo model, IQueryable items)
=> base.CheckLocalAvailability(model, items)
- || (model.OnlineBeatmapSetID != null && items.Any(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID));
+ || (model.OnlineID != null && items.Any(b => b.OnlineID == model.OnlineID));
protected override BeatmapSetInfo CreateModel(ArchiveReader reader)
{
@@ -368,7 +368,7 @@ namespace osu.Game.Beatmaps
return new BeatmapSetInfo
{
- OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID,
+ OnlineID = beatmap.BeatmapInfo.BeatmapSet?.OnlineID,
Beatmaps = new List(),
Metadata = beatmap.Metadata,
DateAdded = DateTimeOffset.UtcNow
diff --git a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs
index b05ad9a1dd..7c80d8ad56 100644
--- a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs
+++ b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs
@@ -84,8 +84,8 @@ namespace osu.Game.Beatmaps
{
beatmapInfo.Status = res.Status;
beatmapInfo.BeatmapSet.Status = res.BeatmapSet?.Status ?? BeatmapSetOnlineStatus.None;
- beatmapInfo.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
- beatmapInfo.OnlineBeatmapID = res.OnlineID;
+ beatmapInfo.BeatmapSet.OnlineID = res.OnlineBeatmapSetID;
+ beatmapInfo.OnlineID = res.OnlineID;
if (beatmapInfo.Metadata != null)
beatmapInfo.Metadata.AuthorID = res.AuthorID;
@@ -103,7 +103,7 @@ namespace osu.Game.Beatmaps
void fail(Exception e)
{
- beatmapInfo.OnlineBeatmapID = null;
+ beatmapInfo.OnlineID = null;
logForModel(set, $"Online retrieval failed for {beatmapInfo} ({e.Message})");
}
}
@@ -161,7 +161,7 @@ namespace osu.Game.Beatmaps
if (string.IsNullOrEmpty(beatmapInfo.MD5Hash)
&& string.IsNullOrEmpty(beatmapInfo.Path)
- && beatmapInfo.OnlineBeatmapID == null)
+ && beatmapInfo.OnlineID == null)
return false;
try
@@ -172,10 +172,10 @@ namespace osu.Game.Beatmaps
using (var cmd = db.CreateCommand())
{
- cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id 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 = @OnlineID OR filename = @Path";
cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmapInfo.MD5Hash));
- cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmapInfo.OnlineBeatmapID ?? (object)DBNull.Value));
+ cmd.Parameters.Add(new SqliteParameter("@OnlineID", beatmapInfo.OnlineID ?? (object)DBNull.Value));
cmd.Parameters.Add(new SqliteParameter("@Path", beatmapInfo.Path));
using (var reader = cmd.ExecuteReader())
@@ -186,8 +186,8 @@ namespace osu.Game.Beatmaps
beatmapInfo.Status = status;
beatmapInfo.BeatmapSet.Status = status;
- beatmapInfo.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0);
- beatmapInfo.OnlineBeatmapID = reader.GetInt32(1);
+ beatmapInfo.BeatmapSet.OnlineID = reader.GetInt32(0);
+ beatmapInfo.OnlineID = reader.GetInt32(1);
if (beatmapInfo.Metadata != null)
beatmapInfo.Metadata.AuthorID = reader.GetInt32(3);
diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs
index 0c93c4b9db..f42e6876e3 100644
--- a/osu.Game/Beatmaps/BeatmapSetInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs
@@ -16,12 +16,13 @@ namespace osu.Game.Beatmaps
{
public int ID { get; set; }
- private int? onlineBeatmapSetID;
+ private int? onlineID;
- public int? OnlineBeatmapSetID
+ [Column("OnlineBeatmapSetID")]
+ public int? OnlineID
{
- get => onlineBeatmapSetID;
- set => onlineBeatmapSetID = value > 0 ? value : null;
+ get => onlineID;
+ set => onlineID = value > 0 ? value : null;
}
public DateTimeOffset DateAdded { get; set; }
@@ -74,8 +75,8 @@ namespace osu.Game.Beatmaps
if (ID != 0 && other.ID != 0)
return ID == other.ID;
- if (OnlineBeatmapSetID.HasValue && other.OnlineBeatmapSetID.HasValue)
- return OnlineBeatmapSetID == other.OnlineBeatmapSetID;
+ if (OnlineID.HasValue && other.OnlineID.HasValue)
+ return OnlineID == other.OnlineID;
if (!string.IsNullOrEmpty(Hash) && !string.IsNullOrEmpty(other.Hash))
return Hash == other.Hash;
@@ -85,7 +86,7 @@ namespace osu.Game.Beatmaps
#region Implementation of IHasOnlineID
- public int OnlineID => OnlineBeatmapSetID ?? -1;
+ int IHasOnlineID.OnlineID => OnlineID ?? -1;
#endregion
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index f0c19f80f3..65d050e608 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -263,11 +263,11 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"BeatmapID":
- beatmap.BeatmapInfo.OnlineBeatmapID = Parsing.ParseInt(pair.Value);
+ beatmap.BeatmapInfo.OnlineID = Parsing.ParseInt(pair.Value);
break;
case @"BeatmapSetID":
- beatmap.BeatmapInfo.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = Parsing.ParseInt(pair.Value) };
+ beatmap.BeatmapInfo.BeatmapSet = new BeatmapSetInfo { OnlineID = Parsing.ParseInt(pair.Value) };
break;
}
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index bcb14526c7..49853418d6 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -133,8 +133,8 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine(FormattableString.Invariant($"Version: {beatmap.BeatmapInfo.DifficultyName}"));
if (!string.IsNullOrEmpty(beatmap.Metadata.Source)) writer.WriteLine(FormattableString.Invariant($"Source: {beatmap.Metadata.Source}"));
if (!string.IsNullOrEmpty(beatmap.Metadata.Tags)) writer.WriteLine(FormattableString.Invariant($"Tags: {beatmap.Metadata.Tags}"));
- if (beatmap.BeatmapInfo.OnlineBeatmapID != null) writer.WriteLine(FormattableString.Invariant($"BeatmapID: {beatmap.BeatmapInfo.OnlineBeatmapID}"));
- if (beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID != null) writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID}"));
+ if (beatmap.BeatmapInfo.OnlineID != null) writer.WriteLine(FormattableString.Invariant($"BeatmapID: {beatmap.BeatmapInfo.OnlineID}"));
+ if (beatmap.BeatmapInfo.BeatmapSet?.OnlineID != null) writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet.OnlineID}"));
}
private void handleDifficulty(TextWriter writer)
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 320f108886..e8b6996869 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -264,7 +264,7 @@ namespace osu.Game.Database
model = CreateModel(archive);
if (model == null)
- return Task.FromResult>(new EntityFrameworkLive(null));
+ return Task.FromResult>(null);
}
catch (TaskCanceledException)
{
diff --git a/osu.Game/Database/IModelImporter.cs b/osu.Game/Database/IModelImporter.cs
index 5d0a044578..d00cfb2035 100644
--- a/osu.Game/Database/IModelImporter.cs
+++ b/osu.Game/Database/IModelImporter.cs
@@ -7,6 +7,8 @@ using System.Threading.Tasks;
using osu.Game.IO.Archives;
using osu.Game.Overlays.Notifications;
+#nullable enable
+
namespace osu.Game.Database
{
///
@@ -26,7 +28,7 @@ namespace osu.Game.Database
/// Whether this is a low priority import.
/// An optional cancellation token.
/// The imported model, if successful.
- Task> Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default);
+ Task?> Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default);
///
/// Silently import an item from an .
@@ -34,7 +36,7 @@ namespace osu.Game.Database
/// The archive to be imported.
/// Whether this is a low priority import.
/// An optional cancellation token.
- Task> Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default);
+ Task?> Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default);
///
/// Silently import an item from a .
@@ -43,7 +45,7 @@ namespace osu.Game.Database
/// An optional archive to use for model population.
/// Whether this is a low priority import.
/// An optional cancellation token.
- Task> Import(TModel item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default);
+ Task?> Import(TModel item, ArchiveReader? archive = null, bool lowPriority = false, CancellationToken cancellationToken = default);
///
/// A user displayable name for the model type associated with this manager.
diff --git a/osu.Game/Database/ImportTask.cs b/osu.Game/Database/ImportTask.cs
index 1433a567a9..1fb5a42630 100644
--- a/osu.Game/Database/ImportTask.cs
+++ b/osu.Game/Database/ImportTask.cs
@@ -47,10 +47,30 @@ namespace osu.Game.Database
///
public ArchiveReader GetReader()
{
- if (Stream != null)
- return new ZipArchiveReader(Stream, Path);
+ return Stream != null
+ ? getReaderFrom(Stream)
+ : getReaderFrom(Path);
+ }
- return getReaderFrom(Path);
+ ///
+ /// Creates an from a stream.
+ ///
+ /// A seekable stream containing the archive content.
+ /// A reader giving access to the archive's content.
+ private ArchiveReader getReaderFrom(Stream stream)
+ {
+ if (!(stream is MemoryStream memoryStream))
+ {
+ // This isn't used in any current path. May need to reconsider for performance reasons (ie. if we don't expect the incoming stream to be copied out).
+ byte[] buffer = new byte[stream.Length];
+ stream.Read(buffer, 0, (int)stream.Length);
+ memoryStream = new MemoryStream(buffer);
+ }
+
+ if (ZipUtils.IsZipArchive(memoryStream))
+ return new ZipArchiveReader(memoryStream, Path);
+
+ return new LegacyByteArrayReader(memoryStream.ToArray(), Path);
}
///
diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs
index 1d8322aadd..d8d2cb8981 100644
--- a/osu.Game/Database/OsuDbContext.cs
+++ b/osu.Game/Database/OsuDbContext.cs
@@ -125,11 +125,11 @@ namespace osu.Game.Database
{
base.OnModelCreating(modelBuilder);
- modelBuilder.Entity().HasIndex(b => b.OnlineBeatmapID).IsUnique();
+ modelBuilder.Entity().HasIndex(b => b.OnlineID).IsUnique();
modelBuilder.Entity().HasIndex(b => b.MD5Hash);
modelBuilder.Entity().HasIndex(b => b.Hash);
- modelBuilder.Entity().HasIndex(b => b.OnlineBeatmapSetID).IsUnique();
+ modelBuilder.Entity().HasIndex(b => b.OnlineID).IsUnique();
modelBuilder.Entity().HasIndex(b => b.DeletePending);
modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique();
diff --git a/osu.Game/Extensions/ModelExtensions.cs b/osu.Game/Extensions/ModelExtensions.cs
index 5c96add076..d8e0938d46 100644
--- a/osu.Game/Extensions/ModelExtensions.cs
+++ b/osu.Game/Extensions/ModelExtensions.cs
@@ -54,7 +54,7 @@ namespace osu.Game.Extensions
}
// fallback in case none of the above happens to match.
- result ??= model.ToString();
+ result ??= model?.ToString() ?? @"null";
return result;
}
}
diff --git a/osu.Game/IO/Archives/LegacyByteArrayReader.cs b/osu.Game/IO/Archives/LegacyByteArrayReader.cs
index 0c3620403f..ea8ff3bbe0 100644
--- a/osu.Game/IO/Archives/LegacyByteArrayReader.cs
+++ b/osu.Game/IO/Archives/LegacyByteArrayReader.cs
@@ -7,7 +7,7 @@ using System.IO;
namespace osu.Game.IO.Archives
{
///
- /// Allows reading a single file from the provided stream.
+ /// Allows reading a single file from the provided byte array.
///
public class LegacyByteArrayReader : ArchiveReader
{
diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs
index 22446634c1..c71cb6a00a 100644
--- a/osu.Game/Input/Bindings/GlobalActionContainer.cs
+++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs
@@ -76,6 +76,7 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.J }, GlobalAction.EditorNudgeLeft),
new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight),
new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode),
+ new KeyBinding(new[] { InputKey.F5 }, GlobalAction.EditorTestGameplay),
};
public IEnumerable InGameKeyBindings => new[]
@@ -288,6 +289,9 @@ namespace osu.Game.Input.Bindings
ToggleChatFocus,
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorCycleGridDisplayMode))]
- EditorCycleGridDisplayMode
+ EditorCycleGridDisplayMode,
+
+ [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTestGameplay))]
+ EditorTestGameplay
}
}
diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs
index 06f1b094bf..35a0c2ae74 100644
--- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs
+++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs
@@ -169,6 +169,11 @@ namespace osu.Game.Localisation
///
public static LocalisableString EditorCycleGridDisplayMode => new TranslatableString(getKey(@"editor_cycle_grid_display_mode"), @"Cycle grid display mode");
+ ///
+ /// "Test gameplay"
+ ///
+ public static LocalisableString EditorTestGameplay => new TranslatableString(getKey(@"editor_test_gameplay"), @"Test gameplay");
+
///
/// "Hold for HUD"
///
diff --git a/osu.Game/Online/BeatmapDownloadTracker.cs b/osu.Game/Online/BeatmapDownloadTracker.cs
index 592dc60d20..77a8fca1e4 100644
--- a/osu.Game/Online/BeatmapDownloadTracker.cs
+++ b/osu.Game/Online/BeatmapDownloadTracker.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Online
return;
// Used to interact with manager classes that don't support interface types. Will eventually be replaced.
- var beatmapSetInfo = new BeatmapSetInfo { OnlineBeatmapSetID = TrackedItem.OnlineID };
+ var beatmapSetInfo = new BeatmapSetInfo { OnlineID = TrackedItem.OnlineID };
if (Manager.IsAvailableLocally(beatmapSetInfo))
UpdateState(DownloadState.LocallyAvailable);
diff --git a/osu.Game/Online/Chat/NowPlayingCommand.cs b/osu.Game/Online/Chat/NowPlayingCommand.cs
index adb3d88df6..34b12c23e6 100644
--- a/osu.Game/Online/Chat/NowPlayingCommand.cs
+++ b/osu.Game/Online/Chat/NowPlayingCommand.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Online.Chat
break;
}
- string beatmapString = beatmapInfo.OnlineBeatmapID.HasValue ? $"[{api.WebsiteRootUrl}/b/{beatmapInfo.OnlineBeatmapID} {beatmapInfo}]" : beatmapInfo.ToString();
+ string beatmapString = beatmapInfo.OnlineID.HasValue ? $"[{api.WebsiteRootUrl}/b/{beatmapInfo.OnlineID} {beatmapInfo}]" : beatmapInfo.ToString();
channelManager.PostMessage($"is {verb} {beatmapString}", true, target);
Expire();
diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs
index eb9ea608f7..aa0e37363b 100644
--- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs
+++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs
@@ -113,7 +113,7 @@ namespace osu.Game.Online.Rooms
int onlineId = SelectedItem.Value.Beatmap.Value.OnlineID;
string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash;
- return beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == onlineId && b.MD5Hash == checksum && !b.BeatmapSet.DeletePending) != null;
+ return beatmapManager.QueryBeatmap(b => b.OnlineID == onlineId && b.MD5Hash == checksum && !b.BeatmapSet.DeletePending) != null;
}
}
}
diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs
index f9366674d8..6b95d288c5 100644
--- a/osu.Game/Online/Spectator/SpectatorClient.cs
+++ b/osu.Game/Online/Spectator/SpectatorClient.cs
@@ -144,7 +144,7 @@ namespace osu.Game.Online.Spectator
IsPlaying = true;
// transfer state at point of beginning play
- currentState.BeatmapID = score.ScoreInfo.BeatmapInfo.OnlineBeatmapID;
+ currentState.BeatmapID = score.ScoreInfo.BeatmapInfo.OnlineID;
currentState.RulesetID = score.ScoreInfo.RulesetID;
currentState.Mods = score.ScoreInfo.Mods.Select(m => new APIMod(m)).ToArray();
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index bf757a153f..095add399c 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -443,7 +443,7 @@ namespace osu.Game
BeatmapSetInfo databasedSet = null;
if (beatmap.OnlineID > 0)
- databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineID);
+ databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineID == beatmap.OnlineID);
if (beatmap is BeatmapSetInfo localBeatmap)
databasedSet ??= BeatmapManager.QueryBeatmapSet(s => s.Hash == localBeatmap.Hash);
diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs
index 5ed49cf384..8c7846783d 100644
--- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs
+++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs
@@ -80,7 +80,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
case DownloadState.LocallyAvailable:
Predicate findPredicate = null;
if (SelectedBeatmap.Value != null)
- findPredicate = b => b.OnlineBeatmapID == SelectedBeatmap.Value.OnlineID;
+ findPredicate = b => b.OnlineID == SelectedBeatmap.Value.OnlineID;
game?.PresentBeatmap(beatmapSet, findPredicate);
break;
diff --git a/osu.Game/Rulesets/Scoring/HitEventExtensions.cs b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs
new file mode 100644
index 0000000000..f645b12483
--- /dev/null
+++ b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs
@@ -0,0 +1,37 @@
+// Copyright (c) ppy Pty Ltd . 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;
+
+namespace osu.Game.Rulesets.Scoring
+{
+ public static class HitEventExtensions
+ {
+ ///
+ /// Calculates the "unstable rate" for a sequence of s.
+ ///
+ ///
+ /// A non-null value if unstable rate could be calculated,
+ /// and if unstable rate cannot be calculated due to being empty.
+ ///
+ public static double? CalculateUnstableRate(this IEnumerable hitEvents)
+ {
+ double[] timeOffsets = hitEvents.Where(affectsUnstableRate).Select(ev => ev.TimeOffset).ToArray();
+ return 10 * standardDeviation(timeOffsets);
+ }
+
+ private static bool affectsUnstableRate(HitEvent e) => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsHit();
+
+ private static double? standardDeviation(double[] timeOffsets)
+ {
+ if (timeOffsets.Length == 0)
+ return null;
+
+ double mean = timeOffsets.Average();
+ double squares = timeOffsets.Select(offset => Math.Pow(offset - mean, 2)).Sum();
+ return Math.Sqrt(squares / timeOffsets.Length);
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index c1234f8fb3..fc24972b8e 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -53,6 +53,12 @@ namespace osu.Game.Rulesets.Scoring
///
public readonly Bindable Mode = new Bindable();
+ ///
+ /// The s collected during gameplay thus far.
+ /// Intended for use with various statistics displays.
+ ///
+ public IReadOnlyList HitEvents => hitEvents;
+
///
/// The default portion of awarded for hitting s accurately. Defaults to 30%.
///
diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/TestGameplayButton.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/TestGameplayButton.cs
new file mode 100644
index 0000000000..0d7a4ad057
--- /dev/null
+++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/TestGameplayButton.cs
@@ -0,0 +1,34 @@
+// Copyright (c) ppy Pty Ltd . 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.Sprites;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays;
+
+namespace osu.Game.Screens.Edit.Components.Timelines.Summary
+{
+ public class TestGameplayButton : OsuButton
+ {
+ protected override SpriteText CreateText() => new OsuSpriteText
+ {
+ Depth = -1,
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Font = OsuFont.TorusAlternate.With(weight: FontWeight.Light, size: 24),
+ Shadow = false
+ };
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, OverlayColourProvider colourProvider)
+ {
+ BackgroundColour = colours.Orange1;
+ SpriteText.Colour = colourProvider.Background6;
+
+ Text = "Test!";
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 0ca7038580..738f607cc8 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -36,6 +36,7 @@ using osu.Game.Screens.Edit.Timing;
using osu.Game.Screens.Edit.Verify;
using osu.Game.Screens.Play;
using osu.Game.Users;
+using osuTK;
using osuTK.Graphics;
using osuTK.Input;
@@ -90,6 +91,8 @@ namespace osu.Game.Screens.Edit
private DependencyContainer dependencies;
+ private TestGameplayButton testGameplayButton;
+
private bool isNewBeatmap;
protected override UserActivity InitialActivity => new UserActivity.Editing(Beatmap.Value.BeatmapInfo);
@@ -106,6 +109,9 @@ namespace osu.Game.Screens.Edit
[Cached]
public readonly EditorClipboard Clipboard = new EditorClipboard();
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
+
public Editor(EditorLoader loader = null)
{
this.loader = loader;
@@ -262,7 +268,8 @@ namespace osu.Game.Screens.Edit
{
new Dimension(GridSizeMode.Absolute, 220),
new Dimension(),
- new Dimension(GridSizeMode.Absolute, 220)
+ new Dimension(GridSizeMode.Absolute, 220),
+ new Dimension(GridSizeMode.Absolute, 120),
},
Content = new[]
{
@@ -283,6 +290,13 @@ namespace osu.Game.Screens.Edit
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = 10 },
Child = new PlaybackControl { RelativeSizeAxes = Axes.Both },
+ },
+ testGameplayButton = new TestGameplayButton
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = 10 },
+ Size = new Vector2(1),
+ Action = testGameplay
}
},
}
@@ -456,6 +470,10 @@ namespace osu.Game.Screens.Edit
menuBar.Mode.Value = EditorScreenMode.Verify;
return true;
+ case GlobalAction.EditorTestGameplay:
+ testGameplayButton.TriggerClick();
+ return true;
+
default:
return false;
}
@@ -510,7 +528,21 @@ namespace osu.Game.Screens.Edit
ApplyToBackground(b => b.FadeColour(Color4.White, 500));
resetTrack();
- // To update the game-wide beatmap with any changes, perform a re-fetch on exit.
+ refetchBeatmap();
+
+ return base.OnExiting(next);
+ }
+
+ public override void OnSuspending(IScreen next)
+ {
+ refetchBeatmap();
+
+ base.OnSuspending(next);
+ }
+
+ private void refetchBeatmap()
+ {
+ // To update the game-wide beatmap with any changes, perform a re-fetch on exit/suspend.
// This is required as the editor makes its local changes via EditorBeatmap
// (which are not propagated outwards to a potentially cached WorkingBeatmap).
var refetchedBeatmap = beatmapManager.GetWorkingBeatmap(Beatmap.Value.BeatmapInfo);
@@ -520,8 +552,6 @@ namespace osu.Game.Screens.Edit
Logger.Log("Editor providing re-fetched beatmap post edit session");
Beatmap.Value = refetchedBeatmap;
}
-
- return base.OnExiting(next);
}
private void confirmExitWithSave()
@@ -752,6 +782,24 @@ namespace osu.Game.Screens.Edit
loader?.CancelPendingDifficultySwitch();
}
+ private void testGameplay()
+ {
+ if (HasUnsavedChanges)
+ {
+ dialogOverlay.Push(new SaveBeforeGameplayTestDialog(() =>
+ {
+ Save();
+ pushEditorPlayer();
+ }));
+ }
+ else
+ {
+ pushEditorPlayer();
+ }
+
+ void pushEditorPlayer() => this.Push(new PlayerLoader(() => new EditorPlayer()));
+ }
+
public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime);
public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime);
diff --git a/osu.Game/Screens/Edit/EditorPlayer.cs b/osu.Game/Screens/Edit/EditorPlayer.cs
new file mode 100644
index 0000000000..b2fab3fefc
--- /dev/null
+++ b/osu.Game/Screens/Edit/EditorPlayer.cs
@@ -0,0 +1,44 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Screens;
+using osu.Game.Overlays;
+using osu.Game.Screens.Play;
+
+namespace osu.Game.Screens.Edit
+{
+ public class EditorPlayer : Player
+ {
+ public EditorPlayer()
+ : base(new PlayerConfiguration { ShowResults = false })
+ {
+ }
+
+ [Resolved]
+ private MusicController musicController { get; set; }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ ScoreProcessor.HasCompleted.BindValueChanged(completed =>
+ {
+ if (completed.NewValue)
+ Scheduler.AddDelayed(this.Exit, RESULTS_DISPLAY_DELAY);
+ });
+ }
+
+ protected override void PrepareReplay()
+ {
+ // don't record replays.
+ }
+
+ protected override bool CheckModsAllowFailure() => false; // never fail.
+
+ public override bool OnExiting(IScreen next)
+ {
+ musicController.Stop();
+ return base.OnExiting(next);
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/EditorRoundedScreen.cs b/osu.Game/Screens/Edit/EditorRoundedScreen.cs
index 508663224d..7f7b3abc2a 100644
--- a/osu.Game/Screens/Edit/EditorRoundedScreen.cs
+++ b/osu.Game/Screens/Edit/EditorRoundedScreen.cs
@@ -6,6 +6,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osu.Game.Overlays;
namespace osu.Game.Screens.Edit
{
@@ -26,7 +27,7 @@ namespace osu.Game.Screens.Edit
}
[BackgroundDependencyLoader]
- private void load()
+ private void load(OverlayColourProvider colourProvider)
{
base.Content.Add(new Container
{
@@ -41,7 +42,7 @@ namespace osu.Game.Screens.Edit
{
new Box
{
- Colour = ColourProvider.Background3,
+ Colour = colourProvider.Background3,
RelativeSizeAxes = Axes.Both,
},
roundedContent = new Container
diff --git a/osu.Game/Screens/Edit/EditorScreen.cs b/osu.Game/Screens/Edit/EditorScreen.cs
index 516d7a23e0..2837cdcd9a 100644
--- a/osu.Game/Screens/Edit/EditorScreen.cs
+++ b/osu.Game/Screens/Edit/EditorScreen.cs
@@ -6,7 +6,6 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
-using osu.Game.Overlays;
namespace osu.Game.Screens.Edit
{
@@ -18,9 +17,6 @@ namespace osu.Game.Screens.Edit
[Resolved]
protected EditorBeatmap EditorBeatmap { get; private set; }
- [Cached]
- protected readonly OverlayColourProvider ColourProvider;
-
protected override Container Content => content;
private readonly Container content;
@@ -34,8 +30,6 @@ namespace osu.Game.Screens.Edit
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
- ColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
-
InternalChild = content = new PopoverContainer { RelativeSizeAxes = Axes.Both };
}
diff --git a/osu.Game/Screens/Edit/SaveBeforeGameplayTestDialog.cs b/osu.Game/Screens/Edit/SaveBeforeGameplayTestDialog.cs
new file mode 100644
index 0000000000..7c03664c66
--- /dev/null
+++ b/osu.Game/Screens/Edit/SaveBeforeGameplayTestDialog.cs
@@ -0,0 +1,32 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Overlays.Dialog;
+
+namespace osu.Game.Screens.Edit
+{
+ public class SaveBeforeGameplayTestDialog : PopupDialog
+ {
+ public SaveBeforeGameplayTestDialog(Action saveAndPreview)
+ {
+ HeaderText = "The beatmap will be saved in order to test it.";
+
+ Icon = FontAwesome.Regular.Save;
+
+ Buttons = new PopupDialogButton[]
+ {
+ new PopupDialogOkButton
+ {
+ Text = "Sounds good, let's go!",
+ Action = saveAndPreview
+ },
+ new PopupDialogCancelButton
+ {
+ Text = "Oops, continue editing",
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
index d6016ec4b9..dc928d90e9 100644
--- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
@@ -366,7 +366,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
var beatmap = SelectedItem.Value?.Beatmap.Value;
// Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
- var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineID);
+ var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineID == beatmap.OnlineID);
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs
index bdb5ff9bb2..22537c3ce0 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
private void load(IBindable ruleset)
{
// Sanity checks to ensure that PlaylistsPlayer matches the settings for the current PlaylistItem
- if (Beatmap.Value.BeatmapInfo.OnlineBeatmapID != PlaylistItem.Beatmap.Value.OnlineID)
+ if (Beatmap.Value.BeatmapInfo.OnlineID != PlaylistItem.Beatmap.Value.OnlineID)
throw new InvalidOperationException("Current Beatmap does not match PlaylistItem's Beatmap");
if (ruleset.Value.ID != PlaylistItem.Ruleset.Value.ID)
diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs
index ef289c2a20..b4359fe518 100644
--- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs
+++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs
@@ -92,6 +92,9 @@ namespace osu.Game.Screens.Play.HUD
scoreProcessor.NewJudgement += onJudgementChanged;
scoreProcessor.JudgementReverted += onJudgementChanged;
}
+
+ if (gameplayState?.LastJudgementResult.Value != null)
+ onJudgementChanged(gameplayState.LastJudgementResult.Value);
}
private bool isValid;
@@ -155,7 +158,10 @@ namespace osu.Game.Screens.Play.HUD
base.Dispose(isDisposing);
if (scoreProcessor != null)
+ {
scoreProcessor.NewJudgement -= onJudgementChanged;
+ scoreProcessor.JudgementReverted -= onJudgementChanged;
+ }
loadCancellationSource?.Cancel();
}
@@ -184,7 +190,7 @@ namespace osu.Game.Screens.Play.HUD
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
- Font = OsuFont.Numeric.With(size: 16)
+ Font = OsuFont.Numeric.With(size: 16, fixedWidth: true)
},
new OsuSpriteText
{
diff --git a/osu.Game/Screens/Play/HUD/UnstableRateCounter.cs b/osu.Game/Screens/Play/HUD/UnstableRateCounter.cs
index 89ae4f8a21..235f0f01fd 100644
--- a/osu.Game/Screens/Play/HUD/UnstableRateCounter.cs
+++ b/osu.Game/Screens/Play/HUD/UnstableRateCounter.cs
@@ -2,8 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -17,6 +15,7 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
+using osuTK;
namespace osu.Game.Screens.Play.HUD
{
@@ -29,8 +28,6 @@ namespace osu.Game.Screens.Play.HUD
private const float alpha_when_invalid = 0.3f;
private readonly Bindable valid = new Bindable();
- private readonly List hitOffsets = new List();
-
[Resolved]
private ScoreProcessor scoreProcessor { get; set; }
@@ -54,41 +51,20 @@ namespace osu.Game.Screens.Play.HUD
{
base.LoadComplete();
- scoreProcessor.NewJudgement += onJudgementAdded;
- scoreProcessor.JudgementReverted += onJudgementReverted;
- }
-
- private void onJudgementAdded(JudgementResult judgement)
- {
- if (!changesUnstableRate(judgement)) return;
-
- hitOffsets.Add(judgement.TimeOffset);
+ scoreProcessor.NewJudgement += updateDisplay;
+ scoreProcessor.JudgementReverted += updateDisplay;
updateDisplay();
}
- private void onJudgementReverted(JudgementResult judgement)
- {
- if (judgement.FailedAtJudgement || !changesUnstableRate(judgement)) return;
-
- hitOffsets.RemoveAt(hitOffsets.Count - 1);
- updateDisplay();
- }
+ private void updateDisplay(JudgementResult _) => Scheduler.AddOnce(updateDisplay);
private void updateDisplay()
{
- // At Count = 0, we get NaN, While we are allowing count = 1, it will be 0 since average = offset.
- if (hitOffsets.Count > 0)
- {
- double mean = hitOffsets.Average();
- double squares = hitOffsets.Select(offset => Math.Pow(offset - mean, 2)).Sum();
- Current.Value = (int)(Math.Sqrt(squares / hitOffsets.Count) * 10);
- valid.Value = true;
- }
- else
- {
- Current.Value = 0;
- valid.Value = false;
- }
+ double? unstableRate = scoreProcessor.HitEvents.CalculateUnstableRate();
+
+ valid.Value = unstableRate != null;
+ if (unstableRate != null)
+ Current.Value = (int)Math.Round(unstableRate.Value);
}
protected override IHasText CreateText() => new TextComponent
@@ -102,8 +78,8 @@ namespace osu.Game.Screens.Play.HUD
if (scoreProcessor == null) return;
- scoreProcessor.NewJudgement -= onJudgementAdded;
- scoreProcessor.JudgementReverted -= onJudgementReverted;
+ scoreProcessor.NewJudgement -= updateDisplay;
+ scoreProcessor.JudgementReverted -= updateDisplay;
}
private class TextComponent : CompositeDrawable, IHasText
@@ -123,6 +99,7 @@ namespace osu.Game.Screens.Play.HUD
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
+ Spacing = new Vector2(2),
Children = new Drawable[]
{
text = new OsuSpriteText
@@ -136,8 +113,8 @@ namespace osu.Game.Screens.Play.HUD
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.Numeric.With(size: 8, fixedWidth: true),
- Text = "UR",
- Padding = new MarginPadding { Bottom = 1.5f },
+ Text = @"UR",
+ Padding = new MarginPadding { Bottom = 1.5f }, // align baseline better
}
}
};
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index d852ac2940..ba34df0e59 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -5,6 +5,7 @@ using System;
using System.Diagnostics;
using System.Threading.Tasks;
using JetBrains.Annotations;
+using ManagedBass.Fx;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
@@ -67,6 +68,7 @@ namespace osu.Game.Screens.Play
private readonly BindableDouble volumeAdjustment = new BindableDouble(1);
private AudioFilter lowPassFilter;
+ private AudioFilter highPassFilter;
protected bool BackgroundBrightnessReduction
{
@@ -168,7 +170,8 @@ namespace osu.Game.Screens.Play
},
idleTracker = new IdleTracker(750),
}),
- lowPassFilter = new AudioFilter(audio.TrackMixer)
+ lowPassFilter = new AudioFilter(audio.TrackMixer),
+ highPassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass)
};
if (Beatmap.Value.BeatmapInfo.EpilepsyWarning)
@@ -239,6 +242,7 @@ namespace osu.Game.Screens.Play
Beatmap.Value.Track.Stop();
Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment);
lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF);
+ highPassFilter.CutoffTo(0);
}
public override bool OnExiting(IScreen next)
@@ -266,9 +270,9 @@ namespace osu.Game.Screens.Play
const double duration = 300;
- if (!resuming) logo.MoveTo(new Vector2(0.5f), duration, Easing.In);
+ if (!resuming) logo.MoveTo(new Vector2(0.5f), duration, Easing.OutQuint);
- logo.ScaleTo(new Vector2(0.15f), duration, Easing.In);
+ logo.ScaleTo(new Vector2(0.15f), duration, Easing.OutQuint);
logo.FadeIn(350);
Scheduler.AddDelayed(() =>
@@ -352,6 +356,7 @@ namespace osu.Game.Screens.Play
content.FadeInFromZero(400);
content.ScaleTo(1, 650, Easing.OutQuint).Then().Schedule(prepareNewPlayer);
lowPassFilter.CutoffTo(1000, 650, Easing.OutQuint);
+ highPassFilter.CutoffTo(300).Then().CutoffTo(0, 1250); // 1250 is to line up with the appearance of MetadataInfo (750 delay + 500 fade-in)
ApplyToBackground(b => b?.FadeColour(Color4.White, 800, Easing.OutQuint));
}
@@ -364,6 +369,7 @@ namespace osu.Game.Screens.Play
content.ScaleTo(0.7f, content_out_duration * 2, Easing.OutQuint);
content.FadeOut(content_out_duration, Easing.OutQuint);
lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, content_out_duration);
+ highPassFilter.CutoffTo(0, content_out_duration);
}
private void pushWhenLoaded()
diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs
index b04fcba0c6..c35548c6b4 100644
--- a/osu.Game/Screens/Play/SkipOverlay.cs
+++ b/osu.Game/Screens/Play/SkipOverlay.cs
@@ -270,7 +270,7 @@ namespace osu.Game.Screens.Play
colourNormal = colours.Yellow;
colourHover = colours.YellowDark;
- sampleConfirm = audio.Samples.Get(@"SongSelect/confirm-selection");
+ sampleConfirm = audio.Samples.Get(@"UI/submit-select");
Children = new Drawable[]
{
diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs
index 675cb71311..6cea75af0a 100644
--- a/osu.Game/Screens/Play/SoloPlayer.cs
+++ b/osu.Game/Screens/Play/SoloPlayer.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Screens.Play
protected override APIRequest CreateTokenRequest()
{
- if (!(Beatmap.Value.BeatmapInfo.OnlineBeatmapID is int beatmapId))
+ if (!(Beatmap.Value.BeatmapInfo.OnlineID is int beatmapId))
return null;
if (!(Ruleset.Value.ID is int rulesetId) || Ruleset.Value.ID > ILegacyRuleset.MAX_LEGACY_RULESET_ID)
@@ -40,9 +40,9 @@ namespace osu.Game.Screens.Play
{
var beatmap = score.ScoreInfo.BeatmapInfo;
- Debug.Assert(beatmap.OnlineBeatmapID != null);
+ Debug.Assert(beatmap.OnlineID != null);
- int beatmapId = beatmap.OnlineBeatmapID.Value;
+ int beatmapId = beatmap.OnlineID.Value;
return new SubmitSoloScoreRequest(beatmapId, token, score.ScoreInfo);
}
diff --git a/osu.Game/Screens/Play/SoloSpectator.cs b/osu.Game/Screens/Play/SoloSpectator.cs
index 1dcc191c0f..c65d4af2ae 100644
--- a/osu.Game/Screens/Play/SoloSpectator.cs
+++ b/osu.Game/Screens/Play/SoloSpectator.cs
@@ -241,7 +241,7 @@ namespace osu.Game.Screens.Play
if (!automaticDownload.Current.Value)
return;
- if (beatmaps.IsAvailableLocally(new BeatmapSetInfo { OnlineBeatmapSetID = beatmapSet.OnlineID }))
+ if (beatmaps.IsAvailableLocally(new BeatmapSetInfo { OnlineID = beatmapSet.OnlineID }))
return;
beatmaps.Download(beatmapSet);
diff --git a/osu.Game/Screens/Ranking/SoloResultsScreen.cs b/osu.Game/Screens/Ranking/SoloResultsScreen.cs
index 4f4dfa4909..425e6f983b 100644
--- a/osu.Game/Screens/Ranking/SoloResultsScreen.cs
+++ b/osu.Game/Screens/Ranking/SoloResultsScreen.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Screens.Ranking
protected override APIRequest FetchScores(Action> scoresCallback)
{
- if (Score.BeatmapInfo.OnlineBeatmapID == null || Score.BeatmapInfo.Status <= BeatmapSetOnlineStatus.Pending)
+ if (Score.BeatmapInfo.OnlineID == null || Score.BeatmapInfo.Status <= BeatmapSetOnlineStatus.Pending)
return null;
getScoreRequest = new GetScoresRequest(Score.BeatmapInfo, Score.Ruleset);
diff --git a/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs b/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs
index cd2b292547..0d23490f40 100644
--- a/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs
+++ b/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs
@@ -1,9 +1,7 @@
// Copyright (c) ppy Pty Ltd . 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 osu.Game.Rulesets.Scoring;
namespace osu.Game.Screens.Ranking.Statistics
@@ -11,7 +9,7 @@ namespace osu.Game.Screens.Ranking.Statistics
///
/// Displays the unstable rate statistic for a given play.
///
- public class UnstableRate : SimpleStatisticItem
+ public class UnstableRate : SimpleStatisticItem
{
///
/// Creates and computes an statistic.
@@ -20,21 +18,9 @@ namespace osu.Game.Screens.Ranking.Statistics
public UnstableRate(IEnumerable hitEvents)
: base("Unstable Rate")
{
- double[] timeOffsets = hitEvents.Where(e => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsHit())
- .Select(ev => ev.TimeOffset).ToArray();
- Value = 10 * standardDeviation(timeOffsets);
+ Value = hitEvents.CalculateUnstableRate();
}
- private static double standardDeviation(double[] timeOffsets)
- {
- if (timeOffsets.Length == 0)
- return double.NaN;
-
- double mean = timeOffsets.Average();
- double squares = timeOffsets.Select(offset => Math.Pow(offset - mean, 2)).Sum();
- return Math.Sqrt(squares / timeOffsets.Length);
- }
-
- protected override string DisplayValue(double value) => double.IsNaN(value) ? "(not available)" : value.ToString("N2");
+ protected override string DisplayValue(double? value) => value == null ? "(not available)" : value.Value.ToString(@"N2");
}
}
diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs
index 6f215b9287..7543c89f17 100644
--- a/osu.Game/Screens/Select/BeatmapDetails.cs
+++ b/osu.Game/Screens/Select/BeatmapDetails.cs
@@ -192,7 +192,7 @@ namespace osu.Game.Screens.Select
return;
}
- // for now, let's early abort if an OnlineBeatmapID is not present (should have been populated at import time).
+ // for now, let's early abort if an OnlineID is not present (should have been populated at import time).
if (BeatmapInfo == null || BeatmapInfo.OnlineID <= 0 || api.State.Value == APIState.Offline)
{
updateMetrics();
diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs
index 1334784613..cc2db6ed31 100644
--- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs
+++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs
@@ -66,8 +66,8 @@ namespace osu.Game.Screens.Select.Carousel
// this should be done after text matching so we can prioritise matching numbers in metadata.
if (!match && criteria.SearchNumber.HasValue)
{
- match = (BeatmapInfo.OnlineBeatmapID == criteria.SearchNumber.Value) ||
- (BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID == criteria.SearchNumber.Value);
+ match = (BeatmapInfo.OnlineID == criteria.SearchNumber.Value) ||
+ (BeatmapInfo.BeatmapSet?.OnlineID == criteria.SearchNumber.Value);
}
}
diff --git a/osu.Game/Screens/Select/Carousel/CarouselHeader.cs b/osu.Game/Screens/Select/Carousel/CarouselHeader.cs
index 40ca3e0764..ed3aea3445 100644
--- a/osu.Game/Screens/Select/Carousel/CarouselHeader.cs
+++ b/osu.Game/Screens/Select/Carousel/CarouselHeader.cs
@@ -113,7 +113,7 @@ namespace osu.Game.Screens.Select.Carousel
RelativeSizeAxes = Axes.Both,
};
- sampleHover = audio.Samples.Get("SongSelect/song-ping");
+ sampleHover = audio.Samples.Get("UI/default-hover");
}
public bool InsetForBorder
diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
index f30bec5d2b..d0f9d835fd 100644
--- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
+++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
@@ -238,8 +238,8 @@ namespace osu.Game.Screens.Select.Carousel
if (editRequested != null)
items.Add(new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested(beatmapInfo)));
- if (beatmapInfo.OnlineBeatmapID.HasValue && beatmapOverlay != null)
- items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmapInfo.OnlineBeatmapID.Value)));
+ if (beatmapInfo.OnlineID.HasValue && beatmapOverlay != null)
+ items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmapInfo.OnlineID.Value)));
if (collectionManager != null)
{
diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs
index 173b804d90..619b1e0fd0 100644
--- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs
+++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs
@@ -214,8 +214,8 @@ namespace osu.Game.Screens.Select.Carousel
if (Item.State.Value == CarouselItemState.NotSelected)
items.Add(new OsuMenuItem("Expand", MenuItemType.Highlighted, () => Item.State.Value = CarouselItemState.Selected));
- if (beatmapSet.OnlineBeatmapSetID != null && viewDetails != null)
- items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => viewDetails(beatmapSet.OnlineBeatmapSetID.Value)));
+ if (beatmapSet.OnlineID != null && viewDetails != null)
+ items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => viewDetails(beatmapSet.OnlineID.Value)));
if (collectionManager != null)
{
diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
index 9205c6c0d2..9c8ccee99b 100644
--- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
@@ -152,7 +152,7 @@ namespace osu.Game.Screens.Select.Leaderboards
return null;
}
- if (BeatmapInfo.OnlineBeatmapID == null || BeatmapInfo?.Status <= BeatmapSetOnlineStatus.Pending)
+ if (BeatmapInfo.OnlineID == null || BeatmapInfo?.Status <= BeatmapSetOnlineStatus.Pending)
{
PlaceholderState = PlaceholderState.Unavailable;
return null;
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index a2dea355ac..4da15ee53c 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -105,6 +105,8 @@ namespace osu.Game.Screens.Select
private readonly Bindable decoupledRuleset = new Bindable();
+ private double audioFeedbackLastPlaybackTime;
+
[Resolved]
private MusicController music { get; set; }
@@ -435,6 +437,7 @@ namespace osu.Game.Screens.Select
}
// We need to keep track of the last selected beatmap ignoring debounce to play the correct selection sounds.
+ private BeatmapInfo beatmapInfoPrevious;
private BeatmapInfo beatmapInfoNoDebounce;
private RulesetInfo rulesetNoDebounce;
@@ -477,6 +480,21 @@ namespace osu.Game.Screens.Select
else
selectionChangedDebounce = Scheduler.AddDelayed(run, 200);
+ if (beatmap != beatmapInfoPrevious)
+ {
+ if (beatmap != null && beatmapInfoPrevious != null && Time.Current - audioFeedbackLastPlaybackTime >= 50)
+ {
+ if (beatmap.BeatmapSetInfoID == beatmapInfoPrevious.BeatmapSetInfoID)
+ sampleChangeDifficulty.Play();
+ else
+ sampleChangeBeatmap.Play();
+
+ audioFeedbackLastPlaybackTime = Time.Current;
+ }
+
+ beatmapInfoPrevious = beatmap;
+ }
+
void run()
{
// clear pending task immediately to track any potential nested debounce operation.
@@ -508,18 +526,7 @@ namespace osu.Game.Screens.Select
if (!EqualityComparer.Default.Equals(beatmap, Beatmap.Value.BeatmapInfo))
{
Logger.Log($"beatmap changed from \"{Beatmap.Value.BeatmapInfo}\" to \"{beatmap}\"");
-
- int? lastSetID = Beatmap.Value?.BeatmapInfo.BeatmapSetInfoID;
-
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap);
-
- if (beatmap != null)
- {
- if (beatmap.BeatmapSetInfoID == lastSetID)
- sampleChangeDifficulty.Play();
- else
- sampleChangeBeatmap.Play();
- }
}
if (this.IsCurrentScreen())
diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs
index 3bcfcb2a0b..1f07042ede 100644
--- a/osu.Game/Screens/Spectate/SpectatorScreen.cs
+++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs
@@ -84,7 +84,7 @@ namespace osu.Game.Screens.Spectate
if (!playingUserStates.TryGetValue(userId, out var userState))
continue;
- if (beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID == userState.BeatmapID))
+ if (beatmapSet.Beatmaps.Any(b => b.OnlineID == userState.BeatmapID))
updateGameplayState(userId);
}
}
@@ -150,7 +150,7 @@ namespace osu.Game.Screens.Spectate
if (resolvedRuleset == null)
return;
- var resolvedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == spectatorState.BeatmapID);
+ var resolvedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineID == spectatorState.BeatmapID);
if (resolvedBeatmap == null)
return;
diff --git a/osu.Game/Stores/BeatmapImporter.cs b/osu.Game/Stores/BeatmapImporter.cs
index 01bf925547..a9605f1fc3 100644
--- a/osu.Game/Stores/BeatmapImporter.cs
+++ b/osu.Game/Stores/BeatmapImporter.cs
@@ -63,7 +63,7 @@ namespace osu.Game.Stores
validateOnlineIds(beatmapSet, realm);
- bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineID > 0);
+ bool hadOnlineIDs = beatmapSet.Beatmaps.Any(b => b.OnlineID > 0);
if (onlineLookupQueue != null)
{
@@ -72,7 +72,7 @@ namespace osu.Game.Stores
}
// ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID.
- if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineID > 0))
+ if (hadOnlineIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineID > 0))
{
if (beatmapSet.OnlineID > 0)
{
@@ -182,7 +182,7 @@ namespace osu.Game.Stores
return new RealmBeatmapSet
{
- OnlineID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID ?? -1,
+ OnlineID = beatmap.BeatmapInfo.BeatmapSet?.OnlineID ?? -1,
// Metadata = beatmap.Metadata,
DateAdded = DateTimeOffset.UtcNow
};
@@ -254,7 +254,7 @@ namespace osu.Game.Stores
{
Hash = hash,
DifficultyName = decodedInfo.DifficultyName,
- OnlineID = decodedInfo.OnlineBeatmapID ?? -1,
+ OnlineID = decodedInfo.OnlineID ?? -1,
AudioLeadIn = decodedInfo.AudioLeadIn,
StackLeniency = decodedInfo.StackLeniency,
SpecialStyle = decodedInfo.SpecialStyle,
diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs
index b9eda5c06e..32a9b0b9d1 100644
--- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs
+++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs
@@ -35,10 +35,10 @@ namespace osu.Game.Tests.Beatmaps
BeatmapInfo.RulesetID = ruleset.ID ?? 0;
BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata;
BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo };
- BeatmapInfo.BeatmapSet.OnlineBeatmapSetID = Interlocked.Increment(ref onlineSetID);
+ BeatmapInfo.BeatmapSet.OnlineID = Interlocked.Increment(ref onlineSetID);
BeatmapInfo.Length = 75000;
BeatmapInfo.OnlineInfo = new APIBeatmap();
- BeatmapInfo.OnlineBeatmapID = Interlocked.Increment(ref onlineBeatmapID);
+ BeatmapInfo.OnlineID = Interlocked.Increment(ref onlineBeatmapID);
}
protected virtual Beatmap CreateBeatmap() => createTestBeatmap();
diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
index 036afb26b4..8f7e6d7f5c 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
@@ -311,7 +311,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
var apiRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == Room.RoomID);
IBeatmapSetInfo? set = apiRoom.Playlist.FirstOrDefault(p => p.BeatmapID == beatmapId)?.Beatmap.Value.BeatmapSet
- ?? beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId)?.BeatmapSet;
+ ?? beatmaps.QueryBeatmap(b => b.OnlineID == beatmapId)?.BeatmapSet;
if (set == null)
throw new InvalidOperationException("Beatmap not found.");
diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs
index a4cef02395..fc7bc324ca 100644
--- a/osu.Game/Tests/Visual/OsuTestScene.cs
+++ b/osu.Game/Tests/Visual/OsuTestScene.cs
@@ -203,7 +203,7 @@ namespace osu.Game.Tests.Visual
return new APIBeatmapSet
{
- OnlineID = beatmap.BeatmapSet.OnlineID,
+ OnlineID = ((IBeatmapSetInfo)beatmap.BeatmapSet).OnlineID,
Status = BeatmapSetOnlineStatus.Ranked,
Covers = new BeatmapSetOnlineCovers
{
@@ -222,8 +222,8 @@ namespace osu.Game.Tests.Visual
{
new APIBeatmap
{
- OnlineID = beatmap.OnlineID,
- OnlineBeatmapSetID = beatmap.BeatmapSet.OnlineID,
+ OnlineID = ((IBeatmapInfo)beatmap).OnlineID,
+ OnlineBeatmapSetID = ((IBeatmapSetInfo)beatmap.BeatmapSet).OnlineID,
Status = beatmap.Status,
Checksum = beatmap.MD5Hash,
AuthorID = beatmap.Metadata.Author.OnlineID,
diff --git a/osu.Game/Utils/ZipUtils.cs b/osu.Game/Utils/ZipUtils.cs
index cd4d876451..eb2d2d3b80 100644
--- a/osu.Game/Utils/ZipUtils.cs
+++ b/osu.Game/Utils/ZipUtils.cs
@@ -9,6 +9,34 @@ namespace osu.Game.Utils
{
public static class ZipUtils
{
+ public static bool IsZipArchive(MemoryStream stream)
+ {
+ try
+ {
+ stream.Seek(0, SeekOrigin.Begin);
+
+ using (var arc = ZipArchive.Open(stream))
+ {
+ foreach (var entry in arc.Entries)
+ {
+ using (entry.OpenEntryStream())
+ {
+ }
+ }
+ }
+
+ return true;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ finally
+ {
+ stream.Seek(0, SeekOrigin.Begin);
+ }
+ }
+
public static bool IsZipArchive(string path)
{
if (!File.Exists(path))
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index df3c9b355a..93fb729f46 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -37,7 +37,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 1852957e87..3a2a3fd65e 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -71,7 +71,7 @@
-
+